#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";

// ============================================
// CONFIGURATION
// ============================================
const MCP_NAME = "pipedrive";
const MCP_VERSION = "1.0.0";
const API_BASE_URL = "https://api.pipedrive.com/v1";

// ============================================
// API CLIENT
// ============================================
class PipedriveClient {
  private apiToken: string;
  private baseUrl: string;

  constructor(apiToken: string) {
    this.apiToken = apiToken;
    this.baseUrl = API_BASE_URL;
  }

  private buildUrl(endpoint: string, params: Record<string, any> = {}): string {
    const url = new URL(`${this.baseUrl}${endpoint}`);
    url.searchParams.set("api_token", this.apiToken);
    for (const [key, value] of Object.entries(params)) {
      if (value !== undefined && value !== null) {
        url.searchParams.set(key, String(value));
      }
    }
    return url.toString();
  }

  async request(endpoint: string, options: RequestInit = {}, params: Record<string, any> = {}) {
    const url = this.buildUrl(endpoint, options.method === "GET" ? params : {});
    const response = await fetch(url, {
      ...options,
      headers: {
        "Content-Type": "application/json",
        ...options.headers,
      },
    });

    if (!response.ok) {
      const error = await response.text();
      throw new Error(`Pipedrive API error: ${response.status} - ${error}`);
    }

    return response.json();
  }

  async get(endpoint: string, params: Record<string, any> = {}) {
    return this.request(endpoint, { method: "GET" }, params);
  }

  async post(endpoint: string, data: any) {
    return this.request(endpoint, {
      method: "POST",
      body: JSON.stringify(data),
    });
  }

  async put(endpoint: string, data: any) {
    return this.request(endpoint, {
      method: "PUT",
      body: JSON.stringify(data),
    });
  }
}

// ============================================
// TOOL DEFINITIONS
// ============================================
const tools = [
  {
    name: "list_deals",
    description: "List all deals from Pipedrive. Returns paginated list of deals with their details.",
    inputSchema: {
      type: "object" as const,
      properties: {
        status: { 
          type: "string", 
          description: "Filter by deal status",
          enum: ["open", "won", "lost", "deleted", "all_not_deleted"]
        },
        start: { type: "number", description: "Pagination start (default 0)" },
        limit: { type: "number", description: "Items per page (default 100, max 500)" },
        sort: { type: "string", description: "Field to sort by (e.g., 'add_time DESC')" },
        user_id: { type: "number", description: "Filter by owner user ID" },
        stage_id: { type: "number", description: "Filter by pipeline stage ID" },
        pipeline_id: { type: "number", description: "Filter by pipeline ID" },
      },
    },
  },
  {
    name: "get_deal",
    description: "Get details of a specific deal by ID",
    inputSchema: {
      type: "object" as const,
      properties: {
        id: { type: "number", description: "Deal ID" },
      },
      required: ["id"],
    },
  },
  {
    name: "create_deal",
    description: "Create a new deal in Pipedrive",
    inputSchema: {
      type: "object" as const,
      properties: {
        title: { type: "string", description: "Deal title (required)" },
        value: { type: "number", description: "Deal value/amount" },
        currency: { type: "string", description: "Currency code (e.g., USD, EUR)" },
        person_id: { type: "number", description: "ID of person to link" },
        org_id: { type: "number", description: "ID of organization to link" },
        pipeline_id: { type: "number", description: "Pipeline ID" },
        stage_id: { type: "number", description: "Stage ID within pipeline" },
        status: { type: "string", enum: ["open", "won", "lost"], description: "Deal status" },
        expected_close_date: { type: "string", description: "Expected close date (YYYY-MM-DD)" },
        probability: { type: "number", description: "Deal success probability (0-100)" },
        visible_to: { type: "number", description: "Visibility (1=owner, 3=entire company)" },
      },
      required: ["title"],
    },
  },
  {
    name: "update_deal",
    description: "Update an existing deal",
    inputSchema: {
      type: "object" as const,
      properties: {
        id: { type: "number", description: "Deal ID to update" },
        title: { type: "string", description: "Deal title" },
        value: { type: "number", description: "Deal value/amount" },
        currency: { type: "string", description: "Currency code" },
        person_id: { type: "number", description: "ID of person to link" },
        org_id: { type: "number", description: "ID of organization to link" },
        stage_id: { type: "number", description: "Stage ID within pipeline" },
        status: { type: "string", enum: ["open", "won", "lost"], description: "Deal status" },
        expected_close_date: { type: "string", description: "Expected close date (YYYY-MM-DD)" },
        probability: { type: "number", description: "Deal success probability (0-100)" },
        lost_reason: { type: "string", description: "Reason for losing (if status=lost)" },
        won_time: { type: "string", description: "Won timestamp (if status=won)" },
        lost_time: { type: "string", description: "Lost timestamp (if status=lost)" },
      },
      required: ["id"],
    },
  },
  {
    name: "list_persons",
    description: "List all persons (contacts) from Pipedrive",
    inputSchema: {
      type: "object" as const,
      properties: {
        start: { type: "number", description: "Pagination start (default 0)" },
        limit: { type: "number", description: "Items per page (default 100, max 500)" },
        sort: { type: "string", description: "Field to sort by" },
        filter_id: { type: "number", description: "Filter ID to apply" },
        first_char: { type: "string", description: "Filter by first character of name" },
      },
    },
  },
  {
    name: "create_person",
    description: "Create a new person (contact) in Pipedrive",
    inputSchema: {
      type: "object" as const,
      properties: {
        name: { type: "string", description: "Person's name (required)" },
        email: { 
          type: "array", 
          items: { type: "object" },
          description: "Email addresses [{value: 'email@example.com', primary: true, label: 'work'}]" 
        },
        phone: { 
          type: "array", 
          items: { type: "object" },
          description: "Phone numbers [{value: '+1234567890', primary: true, label: 'work'}]" 
        },
        org_id: { type: "number", description: "Organization ID to link" },
        visible_to: { type: "number", description: "Visibility (1=owner, 3=entire company)" },
        add_time: { type: "string", description: "Creation time (YYYY-MM-DD HH:MM:SS)" },
      },
      required: ["name"],
    },
  },
  {
    name: "list_activities",
    description: "List all activities from Pipedrive",
    inputSchema: {
      type: "object" as const,
      properties: {
        start: { type: "number", description: "Pagination start (default 0)" },
        limit: { type: "number", description: "Items per page (default 100, max 500)" },
        user_id: { type: "number", description: "Filter by user ID" },
        type: { type: "string", description: "Activity type (call, meeting, task, deadline, email, lunch)" },
        done: { type: "number", description: "Filter by done status (0 or 1)" },
        start_date: { type: "string", description: "Start date filter (YYYY-MM-DD)" },
        end_date: { type: "string", description: "End date filter (YYYY-MM-DD)" },
      },
    },
  },
  {
    name: "add_activity",
    description: "Add a new activity to Pipedrive",
    inputSchema: {
      type: "object" as const,
      properties: {
        subject: { type: "string", description: "Activity subject (required)" },
        type: { 
          type: "string", 
          description: "Activity type (call, meeting, task, deadline, email, lunch)" 
        },
        due_date: { type: "string", description: "Due date (YYYY-MM-DD)" },
        due_time: { type: "string", description: "Due time (HH:MM)" },
        duration: { type: "string", description: "Duration (HH:MM)" },
        deal_id: { type: "number", description: "Deal ID to link" },
        person_id: { type: "number", description: "Person ID to link" },
        org_id: { type: "number", description: "Organization ID to link" },
        note: { type: "string", description: "Activity note/description" },
        done: { type: "number", description: "Mark as done (0 or 1)" },
        busy_flag: { type: "boolean", description: "Mark as busy in calendar" },
        participants: { 
          type: "array", 
          items: { type: "object" },
          description: "Participants [{person_id: 1, primary_flag: true}]" 
        },
      },
      required: ["subject"],
    },
  },
];

// ============================================
// TOOL HANDLERS
// ============================================
async function handleTool(client: PipedriveClient, name: string, args: any) {
  switch (name) {
    case "list_deals": {
      const { status, start, limit, sort, user_id, stage_id, pipeline_id } = args;
      return await client.get("/deals", { 
        status, start, limit, sort, user_id, stage_id, pipeline_id 
      });
    }
    case "get_deal": {
      const { id } = args;
      return await client.get(`/deals/${id}`);
    }
    case "create_deal": {
      const { id, ...data } = args;
      return await client.post("/deals", data);
    }
    case "update_deal": {
      const { id, ...data } = args;
      return await client.put(`/deals/${id}`, data);
    }
    case "list_persons": {
      const { start, limit, sort, filter_id, first_char } = args;
      return await client.get("/persons", { start, limit, sort, filter_id, first_char });
    }
    case "create_person": {
      return await client.post("/persons", args);
    }
    case "list_activities": {
      const { start, limit, user_id, type, done, start_date, end_date } = args;
      return await client.get("/activities", { 
        start, limit, user_id, type, done, start_date, end_date 
      });
    }
    case "add_activity": {
      return await client.post("/activities", args);
    }
    default:
      throw new Error(`Unknown tool: ${name}`);
  }
}

// ============================================
// SERVER SETUP
// ============================================
async function main() {
  const apiToken = process.env.PIPEDRIVE_API_TOKEN;
  if (!apiToken) {
    console.error("Error: PIPEDRIVE_API_TOKEN environment variable required");
    process.exit(1);
  }

  const client = new PipedriveClient(apiToken);

  const server = new Server(
    { name: `${MCP_NAME}-mcp`, version: MCP_VERSION },
    { capabilities: { tools: {} } }
  );

  server.setRequestHandler(ListToolsRequestSchema, async () => ({
    tools,
  }));

  server.setRequestHandler(CallToolRequestSchema, async (request) => {
    const { name, arguments: args } = request.params;
    
    try {
      const result = await handleTool(client, name, args || {});
      return {
        content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
      };
    } catch (error) {
      const message = error instanceof Error ? error.message : String(error);
      return {
        content: [{ type: "text", text: `Error: ${message}` }],
        isError: true,
      };
    }
  });

  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error(`${MCP_NAME} MCP server running on stdio`);
}

main().catch(console.error);
