/**
 * MCP Tool Handler — Official MCP Apps SDK Pattern
 *
 * Shows how to integrate json-render with the MCP Apps SDK
 * (`@modelcontextprotocol/ext-apps`) to serve AI-generated UIs.
 *
 * Architecture:
 *   1. registerAppTool defines each GHL view tool with _meta.ui.resourceUri
 *   2. registerAppResource serves the bundled single-file HTML
 *   3. Tool handler fetches GHL data, optionally asks Claude to generate a JSON tree,
 *      and returns it as structuredContent
 *   4. The HTML app receives data via ontoolresult, feeds it to json-render's Renderer
 *
 * This means the same single HTML bundle can render ANY GHL view —
 * it's the JSON tree in structuredContent that determines what shows up.
 */

// ────────────────────────────────────────────────────────────────
// NOTE: These imports are for reference only. This file is NOT
// compiled as part of the Next.js spike app. It shows what the
// real MCP server code would look like.
// ────────────────────────────────────────────────────────────────

/*
import {
  registerAppTool,
  registerAppResource,
  RESOURCE_MIME_TYPE,       // "text/html;profile=mcp-app"
} from "@modelcontextprotocol/ext-apps/server";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import * as fs from "node:fs/promises";
import * as path from "node:path";
*/

// ────────────────────────────────────────────────────────────────
// Server setup (pseudo-code with real SDK types)
// ────────────────────────────────────────────────────────────────

export const SERVER_EXAMPLE = `
import {
  registerAppTool,
  registerAppResource,
  RESOURCE_MIME_TYPE,
} from "@modelcontextprotocol/ext-apps/server";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import * as fs from "node:fs/promises";
import * as path from "node:path";

// The single-file HTML bundle (built by vite-plugin-singlefile) that contains
// the json-render registry + Renderer. It receives a JSON tree via ontoolresult
// and renders it using the GHL component catalog.
const DIST_DIR = path.join(__dirname, "app-ui");

export function createGHLServer(): McpServer {
  const server = new McpServer({
    name: "ghl-json-render",
    version: "1.0.0",
  });

  // ─── Resource URI: One HTML app serves ALL GHL views ────────
  // The magic: we ship ONE html bundle that can render any view.
  // The JSON tree in structuredContent determines what appears.
  const ghlAppUri = "ui://ghl-json-render/app.html";

  // ─── Step 1: Register the UI resource ───────────────────────
  // Serves the bundled single-file HTML containing:
  //   - @json-render/react Renderer
  //   - GHL component registry (all 20 components)
  //   - @modelcontextprotocol/ext-apps App client
  //   - Tailwind CSS styles
  registerAppResource(
    server as any,
    ghlAppUri,
    ghlAppUri,
    { mimeType: RESOURCE_MIME_TYPE },
    async () => {
      const html = await fs.readFile(
        path.join(DIST_DIR, "ghl-app.html"),
        "utf-8"
      );
      return {
        contents: [{
          uri: ghlAppUri,
          mimeType: RESOURCE_MIME_TYPE,
          text: html,
        }],
      };
    }
  );

  // ─── Step 2: Register view tools ────────────────────────────
  // Each tool fetches GHL data and returns a JSON UI tree.
  // The SAME html resource renders all of them.

  registerAppTool(
    server,
    "view_contacts",
    {
      title: "Contact Grid",
      description: "Show a searchable, sortable grid of GHL contacts",
      inputSchema: {
        locationId: z.string().describe("GHL location ID"),
        query: z.string().optional().describe("Search query"),
        limit: z.number().optional().describe("Max contacts to show"),
      },
      _meta: {
        ui: { resourceUri: ghlAppUri },
      },
    },
    async (args: { locationId: string; query?: string; limit?: number }) => {
      // 1. Fetch from GHL API
      const contacts = await fetchGHLContacts(args.locationId, args.query, args.limit);

      // 2. Generate or select a pre-built JSON tree
      //    Option A: Use a static template and fill in data
      //    Option B: Have Claude generate from catalog (for complex/custom views)
      const uiTree = buildContactGridTree(contacts);

      // 3. Return structured content — the HTML app receives this via ontoolresult
      return {
        content: [
          { type: "text" as const, text: \`Showing \${contacts.length} contacts\` },
        ],
        structuredContent: {
          viewType: "contact-grid",
          uiTree,
          meta: {
            totalContacts: contacts.length,
            generatedAt: new Date().toISOString(),
          },
        },
      };
    }
  );

  registerAppTool(
    server,
    "view_pipeline",
    {
      title: "Pipeline Board",
      description: "Show a kanban-style pipeline board with opportunities",
      inputSchema: {
        locationId: z.string().describe("GHL location ID"),
        pipelineId: z.string().describe("GHL pipeline ID"),
      },
      _meta: {
        ui: { resourceUri: ghlAppUri },
      },
    },
    async (args: { locationId: string; pipelineId: string }) => {
      const opportunities = await fetchGHLOpportunities(args.locationId, args.pipelineId);
      const uiTree = buildPipelineTree(opportunities);

      return {
        content: [
          { type: "text" as const, text: \`Pipeline with \${opportunities.length} opportunities\` },
        ],
        structuredContent: {
          viewType: "pipeline",
          uiTree,
          meta: {
            totalOpportunities: opportunities.length,
            generatedAt: new Date().toISOString(),
          },
        },
      };
    }
  );

  registerAppTool(
    server,
    "view_invoice",
    {
      title: "Invoice Preview",
      description: "Show a formatted invoice preview with line items and totals",
      inputSchema: {
        locationId: z.string().describe("GHL location ID"),
        invoiceId: z.string().describe("GHL invoice ID"),
      },
      _meta: {
        ui: { resourceUri: ghlAppUri },
      },
    },
    async (args: { locationId: string; invoiceId: string }) => {
      const invoice = await fetchGHLInvoice(args.locationId, args.invoiceId);
      const uiTree = buildInvoiceTree(invoice);

      return {
        content: [
          { type: "text" as const, text: \`Invoice \${invoice.number} — \${invoice.status}\` },
        ],
        structuredContent: {
          viewType: "invoice",
          uiTree,
          meta: { invoiceId: args.invoiceId },
        },
      };
    }
  );

  registerAppTool(
    server,
    "view_campaign_stats",
    {
      title: "Campaign Stats",
      description: "Show email campaign performance dashboard",
      inputSchema: {
        locationId: z.string().describe("GHL location ID"),
        campaignId: z.string().describe("GHL campaign ID"),
      },
      _meta: {
        ui: { resourceUri: ghlAppUri },
      },
    },
    async (args: { locationId: string; campaignId: string }) => {
      const campaign = await fetchGHLCampaign(args.locationId, args.campaignId);
      const uiTree = buildCampaignTree(campaign);

      return {
        content: [
          { type: "text" as const, text: \`Campaign "\${campaign.name}" — \${campaign.status}\` },
        ],
        structuredContent: {
          viewType: "campaign",
          uiTree,
          meta: { campaignId: args.campaignId },
        },
      };
    }
  );

  // ─── Step 3: Register action tools (no UI, called by app) ──
  // These are called from the HTML app via app.callServerTool()
  // when the user clicks buttons, drags cards, etc.

  server.tool(
    "ghl_move_opportunity",
    "Move an opportunity to a different pipeline stage",
    {
      locationId: z.string(),
      opportunityId: z.string(),
      stageId: z.string(),
    },
    async (args) => {
      // Call GHL API to move opportunity
      // await ghlApi.moveOpportunity(args.locationId, args.opportunityId, args.stageId);
      return {
        content: [{ type: "text" as const, text: \`Moved opportunity \${args.opportunityId} to stage \${args.stageId}\` }],
        structuredContent: { success: true, opportunityId: args.opportunityId, newStage: args.stageId },
      };
    }
  );

  server.tool(
    "ghl_send_invoice",
    "Send an invoice via email",
    {
      locationId: z.string(),
      invoiceId: z.string(),
    },
    async (args) => {
      // await ghlApi.sendInvoice(args.locationId, args.invoiceId);
      return {
        content: [{ type: "text" as const, text: \`Sent invoice \${args.invoiceId}\` }],
        structuredContent: { success: true, invoiceId: args.invoiceId },
      };
    }
  );

  server.tool(
    "ghl_mark_paid",
    "Mark an invoice as paid",
    {
      locationId: z.string(),
      invoiceId: z.string(),
    },
    async (args) => {
      // await ghlApi.markInvoicePaid(args.locationId, args.invoiceId);
      return {
        content: [{ type: "text" as const, text: \`Marked invoice \${args.invoiceId} as paid\` }],
        structuredContent: { success: true, invoiceId: args.invoiceId, newStatus: "paid" },
      };
    }
  );

  return server;
}

// ─── GHL API Stubs ────────────────────────────────────────────

async function fetchGHLContacts(locationId: string, query?: string, limit?: number) {
  // GET /contacts?locationId={locationId}&query={query}&limit={limit}
  return [
    { id: "ct_001", firstName: "Sarah", lastName: "Johnson", email: "sarah@acme.co" },
    { id: "ct_002", firstName: "Michael", lastName: "Chen", email: "mchen@techstart.io" },
  ];
}

async function fetchGHLOpportunities(locationId: string, pipelineId: string) {
  return [{ id: "opp_001", name: "Website Redesign", value: 25000, stage: "qualified" }];
}

async function fetchGHLInvoice(locationId: string, invoiceId: string) {
  return { number: "INV-2026-0142", status: "sent", total: 15478.91 };
}

async function fetchGHLCampaign(locationId: string, campaignId: string) {
  return { name: "Q1 Product Launch", status: "complete", sent: 12450 };
}

// ─── Tree Builders (static templates filled with data) ────────
// These return UITree objects. In the AI-generated flow, Claude would
// produce these from the catalog prompt. For common views, static
// templates are faster and more predictable.

function buildContactGridTree(contacts: any[]) {
  return {
    root: "page",
    elements: {
      page: { key: "page", type: "Card", props: { padding: "none" }, children: ["header", "table"] },
      header: { key: "header", type: "PageHeader", props: { title: "Contacts", stats: [{ label: "total", value: String(contacts.length) }] } },
      table: {
        key: "table", type: "DataTable",
        props: {
          selectable: true, rowAction: "view_contact", pageSize: 10,
          columns: [
            { key: "name", label: "Name", sortable: true, format: "avatar" },
            { key: "email", label: "Email", sortable: true, format: "email" },
          ],
          rows: contacts.map(c => ({ id: c.id, name: c.firstName + " " + c.lastName, email: c.email })),
        },
      },
    },
  };
}

function buildPipelineTree(opportunities: any[]) {
  return { root: "page", elements: { page: { key: "page", type: "Section", props: {}, children: [] } } };
}

function buildInvoiceTree(invoice: any) {
  return { root: "page", elements: { page: { key: "page", type: "Section", props: {}, children: [] } } };
}

function buildCampaignTree(campaign: any) {
  return { root: "page", elements: { page: { key: "page", type: "Section", props: {}, children: [] } } };
}

// ─── Entry point ──────────────────────────────────────────────

async function main() {
  const server = createGHLServer();
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("GHL json-render MCP server started");
}

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

// ────────────────────────────────────────────────────────────────
// Client-side HTML app (would be built by Vite + singlefile plugin)
// ────────────────────────────────────────────────────────────────

export const CLIENT_APP_EXAMPLE = `
<!-- ghl-app.html — The single-file HTML app served via registerAppResource -->
<!-- Built from a React app using vite-plugin-singlefile -->
<!--
  This single HTML file contains:
  - React + ReactDOM
  - @json-render/react Renderer, DataProvider, ActionProvider, VisibilityProvider
  - All 20 GHL component implementations (PageHeader, DataTable, KanbanBoard, etc.)
  - @modelcontextprotocol/ext-apps App client
  - Tailwind CSS (purged)

  It receives a UITree via ontoolresult.structuredContent.uiTree
  and renders it with the GHL component registry.
-->

<script type="module">
import { App } from "@modelcontextprotocol/ext-apps";

const app = new App({ name: "GHL App", version: "1.0.0" });

// Set handler BEFORE connect() — this is CRITICAL
app.ontoolresult = (result) => {
  const { uiTree, viewType, meta } = result.structuredContent;

  // Feed the JSON tree to the json-render Renderer
  // (React root is already mounted, just update state)
  window.__setUITree(uiTree);

  // Wire up action handlers to call back to the MCP server
  window.__setActionHandlers({
    view_contact: (params) => app.callServerTool({
      name: "ghl_view_contact",
      arguments: { locationId: meta.locationId, contactId: params.contactId },
    }),
    move_opportunity: (params) => app.callServerTool({
      name: "ghl_move_opportunity",
      arguments: { locationId: meta.locationId, ...params },
    }),
    send_invoice: (params) => app.callServerTool({
      name: "ghl_send_invoice",
      arguments: { locationId: meta.locationId, invoiceId: params.invoiceId },
    }),
    mark_paid: (params) => app.callServerTool({
      name: "ghl_mark_paid",
      arguments: { locationId: meta.locationId, invoiceId: params.invoiceId },
    }),
  });
};

app.onerror = (error) => {
  document.getElementById("app").innerHTML =
    '<div style="color:red;padding:20px">Error: ' + error.message + '</div>';
};

// Connect to host (triggers ontoolresult with initial data)
app.connect();
</script>
`;
