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 { z } from "zod"

// This MCP server wraps the existing external WP testing implementation used by the chat app.
// It is intentionally kept small and env-configurable so host apps can load it only when needed.

const ToolInput = {
  plugin_path: z.string().min(1),
  plugin_slug: z.string().optional(),
  session_id: z.string().optional(),
  test_mode: z.enum(["cli", "visual", "both"]).optional().default("cli"),
  required_plugins: z
    .array(
      z.object({
        plugin_slug: z.string().min(1),
        source: z.enum(["wordpress.org", "url", "local"]).optional(),
        source_url: z.string().optional(),
        activate: z.boolean().optional(),
      }),
    )
    .optional(),
  test_scenarios: z
    .array(
      z.object({
        name: z.string().optional(),
        type: z.string().optional(),
        url: z.string().optional(),
        selector: z.string().optional(),
        expected_text: z.string().optional(),
        wp_cli_command: z.string().optional(),
        shortcode: z.string().optional(),
        hook: z.string().optional(),
        ajax_action: z.string().optional(),
        method: z.string().optional(),
        body: z.string().optional(),
        assertions: z
          .object({
            status_code: z.number().optional(),
            contains: z.array(z.string()).optional(),
            not_contains: z.array(z.string()).optional(),
            wp_cli_success: z.boolean().optional(),
          })
          .optional(),
      }),
    )
    .optional(),
  config_overrides: z.record(z.any()).optional(),
}

function toJsonSchema(schema) {
  // zod v4 exposes toJSONSchema under z.* in OpenCode, but in plain node usage
  // we keep schema definitions explicit below for MCP.
  // This helper exists only for future extension.
  void schema
  return {
    type: "object",
    additionalProperties: false,
  }
}

const TestToolInputJsonSchema = {
  type: "object",
  additionalProperties: false,
  required: ["plugin_path"],
  properties: {
    plugin_path: { type: "string", description: "Local filesystem path to plugin root" },
    plugin_slug: { type: "string", description: "Plugin slug (defaults to folder name)" },
    session_id: { type: "string", description: "Optional session id for isolation" },
    test_mode: {
      type: "string",
      enum: ["cli", "visual", "both"],
      description: "Testing mode (only 'cli' is implemented in this server)",
    },
    required_plugins: {
      type: "array",
      description: "Plugins to install/activate before running scenarios",
      items: {
        type: "object",
        additionalProperties: false,
        required: ["plugin_slug"],
        properties: {
          plugin_slug: { type: "string" },
          source: { type: "string", enum: ["wordpress.org", "url", "local"] },
          source_url: { type: "string" },
          activate: { type: "boolean" },
        },
      },
    },
    test_scenarios: {
      type: "array",
      description: "Scenario list; if omitted, defaults are generated",
      items: {
        type: "object",
        additionalProperties: true,
      },
    },
    config_overrides: {
      type: "object",
      description: "Overrides for external WP testing configuration (see docs/env vars)",
      additionalProperties: true,
    },
  },
}

const server = new Server(
  {
    name: "wp-cli-testing",
    version: "1.0.0",
  },
  {
    capabilities: {
      tools: {},
    },
  },
)

server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: "test_plugin_external_wp",
        description:
          "Run WP-CLI based verification against an externally hosted WordPress test site (over SSH).",
        inputSchema: TestToolInputJsonSchema,
      },
      {
        name: "external_wp_testing_config",
        description:
          "Return the resolved external WP testing config (from env vars) and highlight missing/invalid settings.",
        inputSchema: {
          type: "object",
          additionalProperties: false,
          properties: {
            config_overrides: {
              type: "object",
              additionalProperties: true,
              description: "Optional config overrides merged on top of env defaults",
            },
          },
        },
      },
    ],
  }
})

server.setRequestHandler(CallToolRequestSchema, async (req) => {
  const toolName = req.params.name
  const args = (req.params.arguments ?? {})

  // Lazy-load to avoid paying cost unless tool is actually invoked.
  // Note: external-wp-testing.js is CommonJS; when imported from ESM, 
  // exports are on .default for Node.js CommonJS modules
  let createExternalWpTester
  let getExternalTestingConfig
  
  try {
    const externalTesting = await import("../../../chat/external-wp-testing.js")
    const mod = externalTesting.default ?? externalTesting
    createExternalWpTester = mod?.createExternalWpTester
    getExternalTestingConfig = mod?.getExternalTestingConfig

    if (typeof createExternalWpTester !== "function" || typeof getExternalTestingConfig !== "function") {
      throw new Error("Required exports not found on module")
    }
  } catch (error) {
    return {
      content: [
        {
          type: "text",
          text: `wp-cli-testing MCP server error: Failed to load chat/external-wp-testing.js - ${error.message}`,
        },
      ],
      isError: true,
    }
  }

  if (toolName === "external_wp_testing_config") {
    const overrides =
      args && typeof args === "object" && (args.config_overrides && typeof args.config_overrides === "object")
        ? args.config_overrides
        : {}

    const config = getExternalTestingConfig(overrides)
    const missing = []
    if (!config.wpHost) missing.push("TEST_WP_HOST (or EXTERNAL_WP_HOST)")
    if (!config.wpSshUser) missing.push("TEST_WP_SSH_USER (or EXTERNAL_WP_SSH_USER)")
    if (!config.wpSshKey) missing.push("TEST_WP_SSH_KEY (or EXTERNAL_WP_SSH_KEY)")

    const safeConfig = {
      ...config,
      // avoid leaking filesystem layout too much; keep key path but don’t include any private key contents.
      wpSshKey: config.wpSshKey,
    }

    return {
      content: [
        {
          type: "text",
          text: JSON.stringify(
            {
              ok: missing.length === 0,
              missing,
              config: safeConfig,
            },
            null,
            2,
          ),
        },
      ],
    }
  }

  if (toolName === "test_plugin_external_wp") {
    const parsed = z
      .object(ToolInput)
      .safeParse(args && typeof args === "object" ? args : {})

    if (!parsed.success) {
      return {
        content: [{ type: "text", text: JSON.stringify({ ok: false, error: parsed.error.message }, null, 2) }],
        isError: true,
      }
    }

    if (parsed.data.test_mode !== "cli") {
      return {
        content: [
          {
            type: "text",
            text: JSON.stringify(
              {
                ok: false,
                error:
                  "Only test_mode='cli' is implemented by this MCP server. Use cli mode or extend the server.",
              },
              null,
              2,
            ),
          },
        ],
        isError: true,
      }
    }

    const tester = createExternalWpTester({})

    const result = await tester.runTest(
      {
        plugin_path: parsed.data.plugin_path,
        plugin_slug: parsed.data.plugin_slug,
        session_id: parsed.data.session_id,
        required_plugins: parsed.data.required_plugins ?? [],
        test_scenarios: parsed.data.test_scenarios ?? [],
        test_mode: "cli",
      },
      {
        configOverrides:
          parsed.data.config_overrides && typeof parsed.data.config_overrides === "object"
            ? parsed.data.config_overrides
            : {},
      },
    )

    return {
      content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
    }
  }

  return {
    content: [{ type: "text", text: `Unknown tool: ${toolName}` }],
    isError: true,
  }
})

const transport = new StdioServerTransport()
await server.connect(transport)
