import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { createInterface } from "node:readline";
import type { ChildProcess } from "node:child_process";
import { dirname, join, resolve } from "node:path";
import { BestAgentError, isRecord } from "@best-agent/foundation";
import type {
  CapabilityRequest,
  McpPromptArgumentSummary,
  McpPromptSummary,
  McpResourceSummary,
  McpServerHealth,
  McpServerSessionSummary,
  McpTemplateSummary,
  ToolInputValue,
} from "@best-agent/protocol";
import { z } from "zod";
import type { ToolDefinition, ToolExecutionContext, ToolInputJsonSchema } from "./contracts.js";
import { coerceLooseStringInput } from "./input-normalization.js";
import { typedToolInputSchema } from "./schema.js";

export interface McpToolConfig {
  args: string[];
  command: string;
  description: string;
  inputSchema?: ToolInputJsonSchema;
  sourceExtension?: string;
  serverName: string;
  toolName: string;
}

type McpToolInput = {
  input: string;
};

const MCP_TOOL_INPUT_SCHEMA = typedToolInputSchema<McpToolInput>(
  z.strictObject({
    input: z.string().min(1),
  }),
);

type McpServerSession = {
  child: ChildProcess;
  discoveryCapabilities: {
    prompts: boolean;
    resources: boolean;
  };
  key: string;
  lines: ReturnType<typeof createInterface>;
  stderr: { value: string };
};

type McpServerSessionTelemetry = {
  args: string[];
  attachmentState: "live" | "persisted";
  authMessage?: string;
  authState: "unknown" | "authenticated" | "auth-required";
  callCount: number;
  command: string;
  health: McpServerHealth;
  lastUsedAt: string;
  pid?: number;
  prompts?: McpPromptSummary[];
  resources?: McpResourceSummary[];
  restartCount: number;
  serverName: string;
  sessionKey: string;
  startedAt: string;
  templates?: McpTemplateSummary[];
};

const sessionPool = new Map<string, Promise<McpServerSession>>();
const sessionTelemetry = new Map<string, McpServerSessionTelemetry>();
let persistencePath: string | undefined;

type PersistedMcpServerSession = {
  args: string[];
  command: string;
  prompts?: McpPromptSummary[];
  resources?: McpResourceSummary[];
  serverName: string;
  session: McpServerSessionSummary;
  templates?: McpTemplateSummary[];
};

export function createMcpToolDefinition(config: McpToolConfig): ToolDefinition<ToolInputValue> {
  const normalizedInputSchema = normalizeMcpInputSchema(config.inputSchema);
  const inputSchemaWasWrapped =
    config.inputSchema !== undefined && normalizedInputSchema !== config.inputSchema;
  const normalizedConfig = {
    ...config,
    description: inputSchemaWasWrapped
      ? `${config.description}\n\nInput schema was normalized from a non-object MCP schema; pass the argument in the \`value\` field.`
      : config.description,
    inputSchema: normalizedInputSchema,
  };
  const inputSchema = normalizedConfig.inputSchema ?? MCP_TOOL_INPUT_SCHEMA;
  return {
    description: normalizedConfig.description,
    executionClass: "external-side-effect",
    idempotent: false,
    inputSchema,
    ...(normalizedConfig.inputSchema
      ? {}
      : {
          legacyAdapter: {
            adapt(input, _context) {
              return {
                input: coerceLooseStringInput(
                  qualifyMcpToolName(normalizedConfig.serverName, normalizedConfig.toolName),
                  input,
                ),
              };
            },
          },
        }),
    name: qualifyMcpToolName(normalizedConfig.serverName, normalizedConfig.toolName),
    source: "mcp",
    sourceExtension: normalizedConfig.sourceExtension,
    sourceServer: normalizedConfig.serverName,
    capabilities(_input: ToolInputValue, context: ToolExecutionContext): CapabilityRequest[] {
      return [
        {
          group: "shell",
          kind: "mcp",
          argv: [normalizedConfig.command, ...normalizedConfig.args],
          cwd: context.cwd,
          approvalScopes: ["once"],
          reason:
            "Executing an MCP server launches an external process outside the runtime-owned filesystem and network checks for this single call.",
        },
      ];
    },
    async execute(input: ToolInputValue, context: ToolExecutionContext): Promise<string> {
      return callMcpTool(normalizedConfig, input, context);
    },
  };
}

export function qualifyMcpToolName(serverName: string, toolName: string): string {
  return `mcp:${serverName}:${toolName}`;
}

function toMcpToolArguments(
  config: McpToolConfig,
  input: ToolInputValue,
): Record<string, ToolInputValue> {
  if (config.inputSchema) {
    return isRecord(input) ? (input as Record<string, ToolInputValue>) : {};
  }
  return {
    input: isRecord(input) && typeof input.input === "string" ? input.input : String(input),
  };
}

function normalizeMcpInputSchema(
  schema: ToolInputJsonSchema | undefined,
): ToolInputJsonSchema | undefined {
  if (!schema || isObjectRootMcpInputSchema(schema)) {
    return schema;
  }
  // MCP tool calls are object-shaped in this runtime. Non-object discovered schemas
  // are exposed to providers and sent to MCP servers as a { value: ... } wrapper.
  return {
    additionalProperties: false,
    properties: {
      value: schema,
    },
    required: ["value"],
    type: "object",
  };
}

function isObjectRootMcpInputSchema(schema: ToolInputJsonSchema): boolean {
  if (schema.type === "object") {
    return true;
  }
  return schema.type === undefined && schema.properties !== undefined;
}

export function configureMcpSessionPersistence(storageRoot?: string): void {
  const nextPath = storageRoot
    ? join(resolve(storageRoot), "host", "mcp-sessions.json")
    : undefined;
  if (nextPath === persistencePath) {
    return;
  }
  for (const active of sessionPool.values()) {
    void active
      .then((session) => {
        invalidateMcpSession(session);
      })
      .catch(() => {});
  }
  sessionPool.clear();
  persistencePath = nextPath;
  sessionTelemetry.clear();
  if (!persistencePath || !existsSync(persistencePath)) {
    return;
  }
  for (const record of readPersistedMcpSessions(persistencePath)) {
    sessionTelemetry.set(record.session.sessionKey, {
      args: [...record.args],
      attachmentState: "persisted",
      ...(record.session.authMessage ? { authMessage: record.session.authMessage } : {}),
      authState: record.session.authState,
      callCount: record.session.callCount,
      command: record.command,
      health: "degraded",
      lastUsedAt: record.session.lastUsedAt,
      ...(record.session.pid ? { pid: record.session.pid } : {}),
      ...(record.prompts
        ? { prompts: record.prompts.map((prompt) => clonePromptSummary(prompt)) }
        : {}),
      ...(record.resources
        ? { resources: record.resources.map((resource) => ({ ...resource })) }
        : {}),
      restartCount: record.session.restartCount,
      serverName: record.serverName,
      sessionKey: record.session.sessionKey,
      startedAt: record.session.startedAt,
      ...(record.templates
        ? { templates: record.templates.map((template) => ({ ...template })) }
        : {}),
    });
  }
}

export function listMcpServerSessions(): Array<{
  args: string[];
  command: string;
  prompts?: McpPromptSummary[];
  resources?: McpResourceSummary[];
  serverName: string;
  session: McpServerSessionSummary;
  templates?: McpTemplateSummary[];
}> {
  return Array.from(sessionTelemetry.values())
    .sort((left, right) => left.serverName.localeCompare(right.serverName))
    .map((entry) => ({
      args: [...entry.args],
      command: entry.command,
      ...(entry.prompts
        ? { prompts: entry.prompts.map((prompt) => clonePromptSummary(prompt)) }
        : {}),
      ...(entry.resources
        ? { resources: entry.resources.map((resource) => ({ ...resource })) }
        : {}),
      serverName: entry.serverName,
      session: {
        attachmentState: entry.attachmentState,
        ...(entry.authMessage ? { authMessage: entry.authMessage } : {}),
        authState: entry.authState,
        callCount: entry.callCount,
        health: entry.health,
        lastUsedAt: entry.lastUsedAt,
        ...(entry.pid ? { pid: entry.pid } : {}),
        restartCount: entry.restartCount,
        sessionKey: entry.sessionKey,
        startedAt: entry.startedAt,
      },
      ...(entry.templates
        ? { templates: entry.templates.map((template) => ({ ...template })) }
        : {}),
    }));
}

async function callMcpTool(
  config: McpToolConfig,
  input: ToolInputValue,
  context: ToolExecutionContext,
): Promise<string> {
  for (let attempt = 0; attempt < 2; attempt += 1) {
    const session = await getMcpSession(config, context);
    try {
      const response = await sendRpcRequest(session.child, session.lines, "tools/call", {
        arguments: toMcpToolArguments(config, input),
        name: config.toolName,
      });
      const responseRecord = isRecord(response) ? response : undefined;

      if (responseRecord?.isError) {
        throw new BestAgentError(
          "MCP_CALL_FAILED",
          extractMcpText(responseRecord.content) || "MCP tool call failed.",
        );
      }

      recordMcpSessionCall(session);
      return extractMcpText(responseRecord?.content) || "";
    } catch (error) {
      const errorMessage =
        error instanceof BestAgentError
          ? error.message
          : error instanceof Error
            ? error.message
            : undefined;
      invalidateMcpSession(session, errorMessage);
      if (attempt === 0 && !isSessionAlive(session) && !isAuthErrorMessage(errorMessage)) {
        continue;
      }
      if (error instanceof BestAgentError) {
        throw withSessionStderr(error, session);
      }
      throw withSessionStderr(
        new BestAgentError(
          "MCP_CALL_FAILED",
          error instanceof Error ? error.message : "Unknown MCP error",
        ),
        session,
      );
    }
  }

  throw new BestAgentError("MCP_CALL_FAILED", "MCP tool call failed after restarting the session.");
}

async function getMcpSession(
  config: McpToolConfig,
  context: ToolExecutionContext,
): Promise<McpServerSession> {
  const key = createSessionKey(config, context.cwd);
  const existing = sessionPool.get(key);
  if (existing) {
    const session = await existing;
    if (isSessionAlive(session)) {
      return session;
    }
    invalidateMcpSession(session);
  }

  const created = createMcpSession(config, context, key);
  sessionPool.set(key, created);
  try {
    return await created;
  } catch (error) {
    if (sessionPool.get(key) === created) {
      sessionPool.delete(key);
    }
    throw error;
  }
}

async function createMcpSession(
  config: McpToolConfig,
  context: ToolExecutionContext,
  key: string,
): Promise<McpServerSession> {
  const child = context.sandbox.spawnCommand([config.command, ...config.args], context.cwd, {
    denyNetwork: true,
    denyWrites: true,
    stdio: ["pipe", "pipe", "pipe"],
  });
  if (!child.stdin || !child.stdout || !child.stderr) {
    throw new BestAgentError("MCP_CALL_FAILED", "MCP server process pipes were not available.");
  }

  const stderr = { value: "" };
  child.stderr.on("data", (chunk: Buffer | string) => {
    stderr.value += chunk.toString();
  });

  const lines = createInterface({
    input: child.stdout,
    crlfDelay: Number.POSITIVE_INFINITY,
  });
  const session: McpServerSession = {
    child,
    discoveryCapabilities: {
      prompts: false,
      resources: false,
    },
    key,
    lines,
    stderr,
  };
  const timestamp = new Date().toISOString();
  const previous = sessionTelemetry.get(key);
  sessionTelemetry.set(key, {
    args: [...config.args],
    attachmentState: "live",
    ...(previous?.authMessage ? { authMessage: previous.authMessage } : {}),
    authState: previous?.authState ?? "unknown",
    callCount: previous?.callCount ?? 0,
    command: config.command,
    health: "healthy",
    lastUsedAt: previous?.lastUsedAt ?? timestamp,
    ...(child.pid ? { pid: child.pid } : {}),
    ...(previous?.prompts
      ? { prompts: previous.prompts.map((prompt) => clonePromptSummary(prompt)) }
      : {}),
    ...(previous?.resources
      ? { resources: previous.resources.map((resource) => ({ ...resource })) }
      : {}),
    restartCount: previous ? previous.restartCount + 1 : 0,
    serverName: config.serverName,
    sessionKey: key,
    startedAt: timestamp,
    ...(previous?.templates
      ? { templates: previous.templates.map((template) => ({ ...template })) }
      : {}),
  });
  persistSessionTelemetry();

  try {
    const initialization = await sendRpcRequest(child, lines, "initialize", {
      capabilities: {},
      clientInfo: {
        name: "best-agent",
        version: "0.0.0",
      },
      protocolVersion: "2025-06-18",
    });

    child.stdin.write(
      `${JSON.stringify({
        jsonrpc: "2.0",
        method: "notifications/initialized",
        params: {},
      })}\n`,
    );
    const initializationRecord = isRecord(initialization) ? initialization : undefined;
    const capabilities = isRecord(initializationRecord?.capabilities)
      ? initializationRecord.capabilities
      : undefined;
    session.discoveryCapabilities = {
      prompts: Boolean(capabilities?.prompts),
      resources: Boolean(capabilities?.resources),
    };
    attachMcpNotificationListener(session);
    await refreshMcpSessionDiscovery(session, session.discoveryCapabilities);
    return session;
  } catch (error) {
    invalidateMcpSession(session);
    if (error instanceof BestAgentError) {
      throw withSessionStderr(error, session);
    }
    throw withSessionStderr(
      new BestAgentError(
        "MCP_CALL_FAILED",
        error instanceof Error ? error.message : "Unknown MCP error",
      ),
      session,
    );
  }
}

function createSessionKey(config: McpToolConfig, cwd: string): string {
  return JSON.stringify({
    args: config.args,
    command: config.command,
    cwd,
    serverName: config.serverName,
  });
}

function isSessionAlive(session: McpServerSession): boolean {
  return session.child.exitCode === null && !session.child.killed;
}

function invalidateMcpSession(session: McpServerSession, errorMessage?: string): void {
  const current = sessionPool.get(session.key);
  if (current) {
    void current
      .then((active) => {
        if (active === session) {
          sessionPool.delete(session.key);
        }
      })
      .catch(() => {
        sessionPool.delete(session.key);
      });
  }
  session.lines.close();
  markMcpSessionFailure(session, errorMessage);
  if (isSessionAlive(session)) {
    session.child.kill();
  }
}

function recordMcpSessionCall(session: McpServerSession): void {
  const telemetry = sessionTelemetry.get(session.key);
  if (!telemetry) {
    return;
  }
  sessionTelemetry.set(session.key, {
    ...telemetry,
    attachmentState: "live",
    authMessage: undefined,
    authState: "authenticated",
    callCount: telemetry.callCount + 1,
    health: "healthy",
    lastUsedAt: new Date().toISOString(),
    ...(session.child.pid ? { pid: session.child.pid } : {}),
  });
  persistSessionTelemetry();
}

async function refreshMcpSessionDiscovery(
  session: McpServerSession,
  capabilities: { prompts: boolean; resources: boolean },
): Promise<void> {
  const telemetry = sessionTelemetry.get(session.key);
  if (!telemetry) {
    return;
  }

  const [resources, templates, prompts] = await Promise.all([
    capabilities.resources
      ? listMcpResources(session)
      : Promise.resolve([] as McpResourceSummary[]),
    capabilities.resources
      ? listMcpTemplates(session)
      : Promise.resolve([] as McpTemplateSummary[]),
    capabilities.prompts ? listMcpPrompts(session) : Promise.resolve([] as McpPromptSummary[]),
  ]);

  const current = sessionTelemetry.get(session.key);
  if (!current) {
    return;
  }

  sessionTelemetry.set(session.key, {
    ...current,
    ...(capabilities.prompts || capabilities.resources
      ? { authMessage: undefined, authState: "authenticated" }
      : {}),
    ...(capabilities.prompts ? (prompts ? { prompts } : {}) : { prompts: [] }),
    ...(capabilities.resources ? (resources ? { resources } : {}) : { resources: [] }),
    ...(capabilities.resources ? (templates ? { templates } : {}) : { templates: [] }),
  });
  persistSessionTelemetry();
}

function attachMcpNotificationListener(session: McpServerSession): void {
  session.lines.on("line", (line) => {
    let payload: { id?: unknown; method?: unknown } | undefined;
    try {
      payload = JSON.parse(line) as { id?: unknown; method?: unknown };
    } catch {
      return;
    }
    if (
      !payload ||
      payload.id !== undefined ||
      typeof payload.method !== "string" ||
      !isSessionAlive(session)
    ) {
      return;
    }
    if (
      payload.method !== "notifications/prompts/list_changed" &&
      payload.method !== "notifications/resources/list_changed"
    ) {
      return;
    }
    void refreshMcpSessionDiscovery(session, session.discoveryCapabilities);
  });
}

function withSessionStderr(error: BestAgentError, session: McpServerSession): BestAgentError {
  const stderr = session.stderr.value.trim();
  if (!stderr) {
    return error;
  }
  return new BestAgentError(error.code, `${error.message}\nMCP stderr: ${stderr}`);
}

async function sendRpcRequest(
  child: ChildProcess,
  lines: ReturnType<typeof createInterface>,
  method: string,
  params: object,
): Promise<unknown> {
  const id = `${Date.now()}-${Math.random().toString(16).slice(2)}`;

  return new Promise((resolve, reject) => {
    if (!child.stdin) {
      reject(
        new BestAgentError("MCP_CALL_FAILED", `MCP server stdin is unavailable for ${method}.`),
      );
      return;
    }

    const onExit = () => {
      lines.off("line", onLine);
      child.off("error", onExit);
      child.off("close", onExit);
      reject(
        new BestAgentError("MCP_CALL_FAILED", `MCP server exited before responding to ${method}.`),
      );
    };

    const onLine = (line: string) => {
      let payload: unknown;
      try {
        payload = JSON.parse(line);
      } catch {
        return;
      }

      if (!isRecord(payload) || payload.id !== id) {
        return;
      }

      lines.off("line", onLine);
      child.off("error", onExit);
      child.off("close", onExit);

      if (isRecord(payload.error)) {
        reject(
          new BestAgentError(
            "MCP_CALL_FAILED",
            String(payload.error.message ?? `MCP request ${method} failed.`),
          ),
        );
        return;
      }

      resolve(payload.result);
    };

    lines.on("line", onLine);
    child.once("error", onExit);
    child.once("close", onExit);
    child.stdin.write(
      `${JSON.stringify({
        jsonrpc: "2.0",
        id,
        method,
        params,
      })}\n`,
    );
  });
}

async function listMcpResources(
  session: McpServerSession,
): Promise<McpResourceSummary[] | undefined> {
  const result = await trySendRpcRequest(session, "resources/list");
  if (result === undefined) {
    return undefined;
  }
  if (!isRecord(result) || !Array.isArray(result.resources)) {
    return [];
  }
  return result.resources.flatMap((resource) => normalizeMcpResource(resource));
}

async function listMcpTemplates(
  session: McpServerSession,
): Promise<McpTemplateSummary[] | undefined> {
  const result = await trySendRpcRequest(session, "resources/templates/list");
  if (result === undefined) {
    return undefined;
  }
  if (!isRecord(result) || !Array.isArray(result.resourceTemplates)) {
    return [];
  }
  return result.resourceTemplates.flatMap((template) => normalizeMcpTemplate(template));
}

async function listMcpPrompts(session: McpServerSession): Promise<McpPromptSummary[] | undefined> {
  const result = await trySendRpcRequest(session, "prompts/list");
  if (result === undefined) {
    return undefined;
  }
  if (!isRecord(result) || !Array.isArray(result.prompts)) {
    return [];
  }
  return result.prompts.flatMap((prompt) => normalizeMcpPrompt(prompt));
}

async function trySendRpcRequest(
  session: McpServerSession,
  method: string,
): Promise<unknown | undefined> {
  try {
    return await sendRpcRequest(session.child, session.lines, method, {});
  } catch (error) {
    const errorMessage =
      error instanceof BestAgentError
        ? error.message
        : error instanceof Error
          ? error.message
          : undefined;
    if (!isSessionAlive(session)) {
      invalidateMcpSession(session, errorMessage);
    } else {
      markMcpSessionFailure(session, errorMessage);
    }
    return undefined;
  }
}

function extractMcpText(content: unknown): string {
  if (!Array.isArray(content)) {
    return "";
  }

  return content
    .filter(isRecord)
    .filter((entry) => entry.type === "text" && typeof entry.text === "string")
    .map((entry) => String(entry.text))
    .join("\n")
    .trim();
}

function normalizeMcpResource(value: unknown): McpResourceSummary[] {
  if (!value || typeof value !== "object" || typeof (value as { uri?: unknown }).uri !== "string") {
    return [];
  }
  return [
    {
      ...(typeof (value as { description?: unknown }).description === "string"
        ? { description: (value as { description: string }).description }
        : {}),
      ...(typeof (value as { mimeType?: unknown }).mimeType === "string"
        ? { mimeType: (value as { mimeType: string }).mimeType }
        : {}),
      ...(typeof (value as { name?: unknown }).name === "string"
        ? { name: (value as { name: string }).name }
        : {}),
      uri: (value as { uri: string }).uri,
    },
  ];
}

function normalizeMcpTemplate(value: unknown): McpTemplateSummary[] {
  if (
    !value ||
    typeof value !== "object" ||
    typeof (value as { name?: unknown }).name !== "string" ||
    typeof (value as { uriTemplate?: unknown }).uriTemplate !== "string"
  ) {
    return [];
  }
  return [
    {
      ...(typeof (value as { description?: unknown }).description === "string"
        ? { description: (value as { description: string }).description }
        : {}),
      ...(typeof (value as { mimeType?: unknown }).mimeType === "string"
        ? { mimeType: (value as { mimeType: string }).mimeType }
        : {}),
      name: (value as { name: string }).name,
      uriTemplate: (value as { uriTemplate: string }).uriTemplate,
    },
  ];
}

function normalizeMcpPrompt(value: unknown): McpPromptSummary[] {
  if (
    !value ||
    typeof value !== "object" ||
    typeof (value as { name?: unknown }).name !== "string"
  ) {
    return [];
  }
  const promptArguments = normalizeMcpPromptArguments((value as { arguments?: unknown }).arguments);
  return [
    {
      ...(promptArguments ? { arguments: promptArguments } : {}),
      ...(typeof (value as { description?: unknown }).description === "string"
        ? { description: (value as { description: string }).description }
        : {}),
      name: (value as { name: string }).name,
    },
  ];
}

function normalizeMcpPromptArguments(value: unknown): McpPromptArgumentSummary[] | undefined {
  if (!Array.isArray(value)) {
    return undefined;
  }
  const argumentsList = value.flatMap((argument) => {
    if (
      !argument ||
      typeof argument !== "object" ||
      typeof (argument as { name?: unknown }).name !== "string"
    ) {
      return [];
    }
    return [
      {
        ...(typeof (argument as { description?: unknown }).description === "string"
          ? { description: (argument as { description: string }).description }
          : {}),
        name: (argument as { name: string }).name,
        ...((argument as { required?: unknown }).required === true ? { required: true } : {}),
      },
    ];
  });
  return argumentsList.length > 0 ? argumentsList : undefined;
}

export async function closeMcpServerSessions(): Promise<void> {
  const sessions = await Promise.allSettled(Array.from(sessionPool.values()));
  for (const settled of sessions) {
    if (settled.status === "fulfilled") {
      invalidateMcpSession(settled.value);
    }
  }
  sessionPool.clear();
}

function markMcpSessionFailure(session: McpServerSession, errorMessage?: string): void {
  const telemetry = sessionTelemetry.get(session.key);
  if (!telemetry) {
    return;
  }
  sessionTelemetry.set(session.key, {
    ...telemetry,
    attachmentState: "persisted",
    ...(isAuthErrorMessage(errorMessage)
      ? {
          authMessage: errorMessage,
          authState: "auth-required" as const,
        }
      : {}),
    health: "degraded",
    lastUsedAt: new Date().toISOString(),
  });
  persistSessionTelemetry();
}

function persistSessionTelemetry(): void {
  if (!persistencePath) {
    return;
  }
  mkdirSync(dirname(persistencePath), { recursive: true });
  writeFileSync(
    persistencePath,
    JSON.stringify(
      listMcpServerSessions().map((entry) => ({
        args: entry.args,
        command: entry.command,
        ...(entry.prompts
          ? { prompts: entry.prompts.map((prompt) => clonePromptSummary(prompt)) }
          : {}),
        ...(entry.resources
          ? { resources: entry.resources.map((resource) => ({ ...resource })) }
          : {}),
        serverName: entry.serverName,
        session: entry.session,
        ...(entry.templates
          ? { templates: entry.templates.map((template) => ({ ...template })) }
          : {}),
      })),
      null,
      2,
    ),
  );
}

function readPersistedMcpSessions(path: string): PersistedMcpServerSession[] {
  try {
    const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
    if (!Array.isArray(parsed)) {
      return [];
    }
    return parsed.flatMap((entry) => normalizePersistedMcpSession(entry));
  } catch {
    return [];
  }
}

function normalizePersistedMcpSession(value: unknown): PersistedMcpServerSession[] {
  if (!value || typeof value !== "object") {
    return [];
  }
  const session = normalizePersistedMcpSessionSummary((value as { session?: unknown }).session);
  if (
    !session ||
    typeof (value as { serverName?: unknown }).serverName !== "string" ||
    typeof (value as { command?: unknown }).command !== "string" ||
    !Array.isArray((value as { args?: unknown }).args)
  ) {
    return [];
  }
  return [
    {
      args: (value as { args: unknown[] }).args.filter(
        (entry): entry is string => typeof entry === "string",
      ),
      command: (value as { command: string }).command,
      ...(Array.isArray((value as { prompts?: unknown }).prompts)
        ? {
            prompts: (value as { prompts: unknown[] }).prompts
              .flatMap((prompt) => normalizeMcpPrompt(prompt))
              .map((prompt) => clonePromptSummary(prompt)),
          }
        : {}),
      ...(Array.isArray((value as { resources?: unknown }).resources)
        ? {
            resources: (value as { resources: unknown[] }).resources.flatMap((resource) =>
              normalizeMcpResource(resource),
            ),
          }
        : {}),
      serverName: (value as { serverName: string }).serverName,
      session,
      ...(Array.isArray((value as { templates?: unknown }).templates)
        ? {
            templates: (value as { templates: unknown[] }).templates.flatMap((template) =>
              normalizeMcpTemplate(template),
            ),
          }
        : {}),
    },
  ];
}

function normalizePersistedMcpSessionSummary(value: unknown): McpServerSessionSummary | undefined {
  if (
    !value ||
    typeof value !== "object" ||
    typeof (value as { callCount?: unknown }).callCount !== "number" ||
    typeof (value as { health?: unknown }).health !== "string" ||
    typeof (value as { lastUsedAt?: unknown }).lastUsedAt !== "string" ||
    typeof (value as { restartCount?: unknown }).restartCount !== "number" ||
    typeof (value as { sessionKey?: unknown }).sessionKey !== "string" ||
    typeof (value as { startedAt?: unknown }).startedAt !== "string"
  ) {
    return undefined;
  }
  return {
    attachmentState:
      (value as { attachmentState?: string }).attachmentState === "live" ? "live" : "persisted",
    ...(typeof (value as { authMessage?: unknown }).authMessage === "string"
      ? { authMessage: (value as { authMessage: string }).authMessage }
      : {}),
    authState:
      (value as { authState?: string }).authState === "authenticated" ||
      (value as { authState?: string }).authState === "auth-required"
        ? (value as { authState: "authenticated" | "auth-required" }).authState
        : "unknown",
    callCount: (value as { callCount: number }).callCount,
    health: ((value as { health: string }).health === "healthy"
      ? "healthy"
      : "degraded") as McpServerHealth,
    lastUsedAt: (value as { lastUsedAt: string }).lastUsedAt,
    ...(typeof (value as { pid?: unknown }).pid === "number"
      ? { pid: (value as { pid: number }).pid }
      : {}),
    restartCount: (value as { restartCount: number }).restartCount,
    sessionKey: (value as { sessionKey: string }).sessionKey,
    startedAt: (value as { startedAt: string }).startedAt,
  };
}

function clonePromptSummary(prompt: McpPromptSummary): McpPromptSummary {
  return {
    ...prompt,
    ...(prompt.arguments
      ? { arguments: prompt.arguments.map((argument) => ({ ...argument })) }
      : {}),
  };
}

function isAuthErrorMessage(message?: string): boolean {
  if (!message) {
    return false;
  }
  const normalized = message.toLowerCase();
  return [
    "auth",
    "authentication",
    "unauthorized",
    "forbidden",
    "credential",
    "oauth",
    "token",
    "api key",
    "login",
  ].some((token) => normalized.includes(token));
}
