#!/usr/bin/env node
// ============================================================================
// Pipedrive MCP Server — Production Quality, 70+ tools, 27 UI apps
// ============================================================================

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { PipedriveClient } from "./client.js";
import type { ToolDefinition, ToolResult } from "./types.js";

// ---------------------------------------------------------------------------
// Lazy-loaded module registry
// ---------------------------------------------------------------------------

interface ToolModule {
  tools: ToolDefinition[];
  handle: (
    client: PipedriveClient,
    name: string,
    args: Record<string, unknown>
  ) => Promise<ToolResult>;
}

interface LazyGroup {
  path: string;
  module?: ToolModule;
  toolNames: string[];
}

// Tool groups — metadata loaded eagerly, handlers loaded lazily
const groups: LazyGroup[] = [
  // Core tools
  { path: "./tools/deals-tools.js", toolNames: [] },
  { path: "./tools/persons-tools.js", toolNames: [] },
  { path: "./tools/organizations-tools.js", toolNames: [] },
  { path: "./tools/activities-tools.js", toolNames: [] },
  { path: "./tools/pipelines-tools.js", toolNames: [] },
  { path: "./tools/stages-tools.js", toolNames: [] },
  { path: "./tools/products-tools.js", toolNames: [] },
  { path: "./tools/leads-tools.js", toolNames: [] },
  { path: "./tools/notes-tools.js", toolNames: [] },
  { path: "./tools/files-tools.js", toolNames: [] },
  { path: "./tools/filters-tools.js", toolNames: [] },
  { path: "./tools/goals-tools.js", toolNames: [] },
  { path: "./tools/webhooks-tools.js", toolNames: [] },
  { path: "./tools/users-tools.js", toolNames: [] },
  { path: "./tools/mail-tools.js", toolNames: [] },
  { path: "./tools/subscriptions-tools.js", toolNames: [] },
  
  // UI apps
  { path: "./apps/deal-dashboard.js", toolNames: [] },
  { path: "./apps/deal-detail.js", toolNames: [] },
  { path: "./apps/deal-grid.js", toolNames: [] },
  { path: "./apps/pipeline-kanban.js", toolNames: [] },
  { path: "./apps/pipeline-analytics.js", toolNames: [] },
  { path: "./apps/pipeline-funnel.js", toolNames: [] },
  { path: "./apps/person-detail.js", toolNames: [] },
  { path: "./apps/person-grid.js", toolNames: [] },
  { path: "./apps/org-detail.js", toolNames: [] },
  { path: "./apps/org-grid.js", toolNames: [] },
  { path: "./apps/activity-dashboard.js", toolNames: [] },
  { path: "./apps/activity-calendar.js", toolNames: [] },
  { path: "./apps/lead-inbox.js", toolNames: [] },
  { path: "./apps/lead-detail.js", toolNames: [] },
  { path: "./apps/product-catalog.js", toolNames: [] },
  { path: "./apps/product-detail.js", toolNames: [] },
  { path: "./apps/note-manager.js", toolNames: [] },
  { path: "./apps/file-manager.js", toolNames: [] },
  { path: "./apps/goal-tracker.js", toolNames: [] },
  { path: "./apps/revenue-dashboard.js", toolNames: [] },
  { path: "./apps/email-inbox.js", toolNames: [] },
  { path: "./apps/filter-manager.js", toolNames: [] },
  { path: "./apps/user-stats.js", toolNames: [] },
  { path: "./apps/deals-timeline.js", toolNames: [] },
  { path: "./apps/subscription-manager.js", toolNames: [] },
  { path: "./apps/search-results.js", toolNames: [] },
  { path: "./apps/won-deals.js", toolNames: [] },
];

const toolToGroup = new Map<string, number>();
let allTools: ToolDefinition[] = [];

async function loadGroupMetadata(): Promise<void> {
  const toolDefs: ToolDefinition[] = [];
  for (let i = 0; i < groups.length; i++) {
    const mod = (await import(groups[i].path)) as ToolModule;
    groups[i].module = mod;
    groups[i].toolNames = mod.tools.map((t) => t.name);
    for (const tool of mod.tools) {
      toolToGroup.set(tool.name, i);
      toolDefs.push(tool);
    }
  }
  allTools = toolDefs;
}

async function getHandler(
  toolName: string
): Promise<ToolModule["handle"] | null> {
  const idx = toolToGroup.get(toolName);
  if (idx === undefined) return null;
  const group = groups[idx];
  if (!group.module) {
    group.module = (await import(group.path)) as ToolModule;
  }
  return group.module.handle;
}

// ---------------------------------------------------------------------------
// Server setup
// ---------------------------------------------------------------------------

async function main(): Promise<void> {
  // Init client (validates API token)
  let client: PipedriveClient;
  try {
    client = new PipedriveClient();
  } catch (e) {
    console.error(
      (e as Error).message ||
        "Failed to initialize. Set PIPEDRIVE_API_TOKEN env var."
    );
    process.exit(1);
  }

  // Load all tool metadata
  await loadGroupMetadata();

  const server = new Server(
    {
      name: "pipedrive-mcp",
      version: "1.0.0",
    },
    {
      capabilities: {
        tools: {},
      },
    }
  );

  // --- List Tools ---
  server.setRequestHandler(ListToolsRequestSchema, async () => {
    return {
      tools: allTools.map((t) => ({
        name: t.name,
        description: t.description,
        inputSchema: t.inputSchema,
      })),
    };
  });

  // --- Call Tool ---
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
    const { name, arguments: args } = request.params;
    const handler = await getHandler(name);
    if (!handler) {
      return {
        content: [{ type: "text" as const, text: `Unknown tool: ${name}` }],
        isError: true,
      } as Record<string, unknown>;
    }
    const result = await handler(
      client,
      name,
      (args as Record<string, unknown>) || {}
    );
    return result as unknown as Record<string, unknown>;
  });

  // --- Connect stdio transport ---
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error(
    `Pipedrive MCP server running — ${allTools.length} tools loaded across ${groups.length} modules`
  );
}

main().catch((error) => {
  console.error("Fatal error:", error);
  process.exit(1);
});
