import type { McpEndpointConfig, VoiceAgentConfig } from "./config.ts";
import { McpToolClient, type McpToolCaller } from "./mcp-tool-client.ts";

export interface McpSmokeOptions {
  clientFactory?: (endpoint: McpEndpointConfig) => McpToolCaller;
  env?: Record<string, string | undefined>;
}

export interface McpSmokeCheck {
  service: string;
  label: string;
  required: boolean;
  ok: boolean;
  skipped: boolean;
  tool?: string;
  error?: string;
  summary?: string;
}

export interface McpSmokeReport {
  ok: boolean;
  checks: McpSmokeCheck[];
}

interface SmokeSpec {
  service: keyof VoiceAgentConfig["mcp"];
  label: string;
  tools: string[];
  required: boolean;
  args?: (env: Record<string, string | undefined>) => Record<string, unknown> | undefined;
  enabled?: (env: Record<string, string | undefined>) => boolean;
}

export async function runMcpSmoke(
  config: VoiceAgentConfig,
  opts: McpSmokeOptions = {},
): Promise<McpSmokeReport> {
  const env = opts.env ?? process.env;
  const checks: McpSmokeCheck[] = [];
  const clients = new Map<string, McpToolCaller>();
  const toolLists = new Map<string, string[]>();

  for (const spec of smokeSpecs()) {
    const endpoint = config.mcp[spec.service];
    const enabled = spec.enabled?.(env) ?? true;
    if (!enabled) {
      checks.push({
        service: endpoint.name,
        label: spec.label,
        required: spec.required,
        ok: !spec.required,
        skipped: true,
        error: "missing optional smoke-test environment input",
      });
      continue;
    }

    if (!endpoint.bearerToken) {
      checks.push({
        service: endpoint.name,
        label: spec.label,
        required: spec.required,
        ok: false,
        skipped: true,
        error: "MCP_BEARER_TOKEN is not configured",
      });
      continue;
    }

    try {
      const client = await ensureClient(endpoint, clients, opts.clientFactory);
      const tools = await ensureToolList(endpoint.name, client, toolLists);
      const tool = selectAvailableTool(tools, spec.tools);
      if (!tool) {
        checks.push({
          service: endpoint.name,
          label: spec.label,
          required: spec.required,
          ok: !spec.required,
          skipped: true,
          error: `none of these tools are exposed: ${spec.tools.join(", ")}`,
        });
        continue;
      }

      const result = await client.callTool(tool, spec.args?.(env) ?? {});
      const failure = detectMcpFailure(result);
      checks.push({
        service: endpoint.name,
        label: spec.label,
        required: spec.required,
        ok: !failure,
        skipped: false,
        tool,
        ...(failure ? { error: failure } : {}),
        summary: summarizeMcpResult(result),
      });
    } catch (err) {
      checks.push({
        service: endpoint.name,
        label: spec.label,
        required: spec.required,
        ok: false,
        skipped: false,
        error: err instanceof Error ? err.message : String(err),
      });
    }
  }

  for (const client of clients.values()) await client.close();
  const ok = checks.every((check) => check.ok || (!check.required && check.skipped));
  return { ok, checks };
}

export function selectAvailableTool(
  availableTools: string[],
  candidates: string[],
): string | undefined {
  return candidates.find((candidate) => availableTools.includes(candidate));
}

function smokeSpecs(): SmokeSpec[] {
  return [
    {
      service: "advancedmd",
      label: "AdvancedMD auth status",
      tools: ["auth_status"],
      required: true,
    },
    {
      service: "advancedmd",
      label: "AdvancedMD login",
      tools: ["login"],
      required: false,
      enabled: (env) => env.AMD_SMOKE_LOGIN === "1",
    },
    {
      service: "advancedmd",
      label: "AdvancedMD fieldset info",
      tools: ["get_fieldset_info"],
      required: true,
    },
    {
      service: "advancedmd",
      label: "AdvancedMD patient search",
      tools: ["search_patients"],
      required: false,
      enabled: (env) => Boolean(env.AMD_SMOKE_SEARCHTERM),
      args: (env) => ({ searchterm: env.AMD_SMOKE_SEARCHTERM }),
    },
    {
      service: "advancedmd",
      label: "AdvancedMD appointment history",
      tools: ["get_appointment_history"],
      required: false,
      enabled: (env) => Boolean(env.AMD_SMOKE_PATIENT_ID),
      args: (env) => ({ patientid: env.AMD_SMOKE_PATIENT_ID }),
    },
    {
      service: "advancedmd",
      label: "AdvancedMD visit info by date",
      tools: ["get_visit_info_by_date"],
      required: false,
      enabled: (env) => Boolean(env.AMD_SMOKE_VISIT_DATE),
      args: (env) => ({ visitdate: env.AMD_SMOKE_VISIT_DATE }),
    },
    {
      service: "ringcentral",
      label: "RingCentral account info",
      tools: ["get_account_info"],
      required: true,
    },
    {
      service: "ringcentralAdmin",
      label: "RingCentral auth context",
      tools: ["get_auth_context"],
      required: true,
    },
    {
      service: "ringcentral",
      label: "RingCentral phone numbers",
      tools: ["list_phone_numbers", "get_phone_numbers"],
      required: true,
    },
    {
      service: "ringcentralAdmin",
      label: "RingCentral detailed call log",
      tools: [
        "pull_call_log",
        "get_detailed_call_logs",
        "list_detailed_call_logs",
        "get_call_logs",
        "list_call_logs",
        "get_call_log",
        "list_call_log",
      ],
      required: true,
      args: () => ringCentralDetailedCallLogArgs(),
    },
    {
      service: "ringcentralAdmin",
      label: "RingCentral Admin service status",
      tools: ["get_service_status"],
      required: true,
    },
    {
      service: "ringcentralAdmin",
      label: "RingCentral Admin call queues",
      tools: ["list_call_queues"],
      required: true,
    },
    {
      service: "ringcentralAdmin",
      label: "RingCentral Admin queue members",
      tools: ["get_call_queue_members"],
      required: false,
      enabled: (env) => Boolean(env.RC_SMOKE_QUEUE_ID),
      args: (env) => ({
        queue_id: env.RC_SMOKE_QUEUE_ID,
        queueId: env.RC_SMOKE_QUEUE_ID,
      }),
    },
    {
      service: "ringcentralAdmin",
      label: "RingCentral extension devices",
      tools: ["list_extension_devices"],
      required: false,
      enabled: (env) => Boolean(env.RC_SMOKE_EXTENSION_ID),
      args: (env) => ({
        extension_id: env.RC_SMOKE_EXTENSION_ID,
        extensionId: env.RC_SMOKE_EXTENSION_ID,
      }),
    },
    {
      service: "ringcentralAdmin",
      label: "RingCentral device SIP info",
      tools: ["read_device_sip_info"],
      required: false,
      enabled: (env) => Boolean(env.RC_SMOKE_DEVICE_ID),
      args: (env) => ({
        device_id: env.RC_SMOKE_DEVICE_ID,
        deviceId: env.RC_SMOKE_DEVICE_ID,
      }),
    },
  ];
}

async function ensureClient(
  endpoint: McpEndpointConfig,
  clients: Map<string, McpToolCaller>,
  factory?: (endpoint: McpEndpointConfig) => McpToolCaller,
): Promise<McpToolCaller> {
  const existing = clients.get(endpoint.name);
  if (existing) return existing;
  const bearerToken = endpoint.bearerToken;
  if (!bearerToken) throw new Error("MCP_BEARER_TOKEN is not configured");
  const client =
    factory?.(endpoint) ??
    new McpToolClient({
      name: endpoint.name,
      mcpUrl: endpoint.mcpUrl,
      bearerToken,
    });
  clients.set(endpoint.name, client);
  return client;
}

async function ensureToolList(
  serviceName: string,
  client: McpToolCaller,
  toolLists: Map<string, string[]>,
): Promise<string[]> {
  const existing = toolLists.get(serviceName);
  if (existing) return existing;
  const tools = await client.listTools();
  toolLists.set(serviceName, tools);
  return tools;
}

function ringCentralDetailedCallLogArgs(): Record<string, unknown> {
  const now = new Date();
  const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
  return {
    date_from: yesterday.toISOString(),
    date_to: now.toISOString(),
    dateFrom: yesterday.toISOString(),
    dateTo: now.toISOString(),
    view: "Detailed",
    perPage: 10,
    max_pages: 1,
  };
}

function summarizeMcpResult(result: unknown): string {
  if (Array.isArray(result)) return `array(${result.length})`;
  if (!result || typeof result !== "object") return typeof result;
  const record = result as Record<string, unknown>;
  if (Array.isArray(record.records)) return `records(${record.records.length})`;
  if (Array.isArray(record.rows)) return `rows(${record.rows.length})`;
  if (Array.isArray(record.data)) return `data(${record.data.length})`;
  return `object keys: ${Object.keys(record).slice(0, 8).join(", ")}`;
}

function detectMcpFailure(result: unknown): string | undefined {
  if (typeof result === "string") {
    const lower = result.toLowerCase();
    if (
      lower.includes("error") ||
      lower.includes("invalid_client") ||
      lower.includes("unauthorized") ||
      lower.includes("missing")
    ) {
      return sanitizeError(result);
    }
    return undefined;
  }

  if (!result || typeof result !== "object" || Array.isArray(result)) return undefined;
  const record = result as Record<string, unknown>;
  if (record.ok === false) return sanitizeError(record.message ?? record.error ?? "tool returned ok=false");
  if ("error" in record) return sanitizeError(record.message ?? record.error);
  return undefined;
}

function sanitizeError(value: unknown): string {
  return String(value)
    .replace(/Bearer\s+[A-Za-z0-9._~-]+/g, "Bearer <redacted>")
    .replace(/[A-Za-z0-9_-]{24,}\.[A-Za-z0-9_-]{24,}\.[A-Za-z0-9_-]{24,}/g, "<redacted-jwt>")
    .slice(0, 500);
}
