import { mkdirSync } from "node:fs";
import { dirname } from "node:path";
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema, type CallToolRequest, type Tool } from "@modelcontextprotocol/sdk/types.js";
import { loadConfig } from "./config.js";
import { SqliteAdapter } from "./db.js";
import { migrate } from "./migrations.js";
import { hashPath, redactPath } from "./privacy.js";
import { AnalyticsError, ValidationError } from "./errors.js";
import { toMcpError, toMcpSuccess } from "./responses.js";
import { runAnalyticsScan, markRunningScansInterrupted } from "./scanner.js";
import { ALL_SOURCES, SESSION_SOURCES } from "./sources.js";
import { querySummary } from "./tools/summary.js";
import { queryModels } from "./tools/models.js";
import { querySessions } from "./tools/sessions.js";
import { queryEvents } from "./tools/events.js";
import { deleteImportedData } from "./tools/delete-imported.js";

const TOOL_ANALYTICS_STATUS = "analytics_status";
const TOOL_ANALYTICS_SCAN = "analytics_scan";
const TOOL_ANALYTICS_SUMMARY = "analytics_summary";
const TOOL_ANALYTICS_MODELS = "analytics_models";
const TOOL_ANALYTICS_SESSIONS = "analytics_sessions";
const TOOL_ANALYTICS_EVENTS = "analytics_events";
const TOOL_ANALYTICS_DELETE_IMPORTED = "analytics_delete_imported";

function parseStatusInput(args: unknown): { include_local_paths: boolean } {
  if (!args || typeof args !== "object" || Array.isArray(args)) {
    return { include_local_paths: false };
  }
  const raw = (args as Record<string, unknown>).include_local_paths;
  const keys = Object.keys(args as Record<string, unknown>);
  for (const key of keys) {
    if (key !== "include_local_paths") {
      throw new ValidationError(`Unknown property: ${key}`);
    }
  }
  if (raw === undefined) {
    return { include_local_paths: false };
  }
  if (typeof raw !== "boolean") {
    throw new ValidationError("include_local_paths must be a boolean.");
  }
  return { include_local_paths: raw };
}

function parseObjectInput(args: unknown): Record<string, unknown> {
  if (args === undefined) {
    return {};
  }
  if (!args || typeof args !== "object" || Array.isArray(args)) {
    throw new ValidationError("Input must be an object.");
  }
  return args as Record<string, unknown>;
}

function toolDefinitions(): Tool[] {
  return [
    {
      name: TOOL_ANALYTICS_STATUS,
      description: "Returns analytics-node runtime status and redacted local configuration.",
      annotations: {
        readOnlyHint: true,
        destructiveHint: false,
        idempotentHint: true,
        openWorldHint: false
      },
      inputSchema: {
        type: "object",
        additionalProperties: false,
        properties: {
          include_local_paths: { type: "boolean" }
        }
      }
    },
    {
      name: TOOL_ANALYTICS_SCAN,
      description: "Scans local analytics sources and updates scan_runs/source_files incrementally.",
      annotations: {
        readOnlyHint: false,
        destructiveHint: false,
        idempotentHint: true,
        openWorldHint: false
      },
      inputSchema: {
        type: "object",
        additionalProperties: false,
        properties: {
          sources: {
            type: "array",
            items: { type: "string", enum: [...ALL_SOURCES] }
          },
          since: { type: "string" },
          force: { type: "boolean" },
          dry_run: { type: "boolean" }
        }
      }
    },
    {
      name: TOOL_ANALYTICS_SUMMARY,
      description: "Returns aggregated session and runtime event totals.",
      annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
      inputSchema: {
        type: "object",
        additionalProperties: false,
        properties: {
          sources: { type: "array", items: { type: "string", enum: [...ALL_SOURCES] } },
          date_from: { type: "string" },
          date_to: { type: "string" },
          group_by: { type: "array", items: { type: "string", enum: ["source", "project", "model", "day", "event_type", "event_name"] } }
        }
      }
    },
    {
      name: TOOL_ANALYTICS_MODELS,
      description: "Returns paginated model metrics for session-based sources.",
      annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
      inputSchema: {
        type: "object",
        additionalProperties: false,
        properties: {
          sources: { type: "array", items: { type: "string", enum: [...SESSION_SOURCES] } },
          date_from: { type: "string" },
          date_to: { type: "string" },
          limit: { type: "integer", minimum: 1, maximum: 1000 },
          offset: { type: "integer", minimum: 0 }
        }
      }
    },
    {
      name: TOOL_ANALYTICS_SESSIONS,
      description: "Returns paginated sessions with stable ordering.",
      annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
      inputSchema: {
        type: "object",
        additionalProperties: false,
        properties: {
          sources: { type: "array", items: { type: "string", enum: [...SESSION_SOURCES] } },
          date_from: { type: "string" },
          date_to: { type: "string" },
          limit: { type: "integer", minimum: 1, maximum: 1000 },
          offset: { type: "integer", minimum: 0 }
        }
      }
    },
    {
      name: TOOL_ANALYTICS_EVENTS,
      description: "Returns paginated runtime events with stable ordering.",
      annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
      inputSchema: {
        type: "object",
        additionalProperties: false,
        properties: {
          sources: { type: "array", items: { type: "string", enum: [...ALL_SOURCES] } },
          event_type: { type: "string", enum: ["mcp", "skill", "hook", "agent_tool", "agent_lifecycle"] },
          date_from: { type: "string" },
          date_to: { type: "string" },
          limit: { type: "integer", minimum: 1, maximum: 1000 },
          offset: { type: "integer", minimum: 0 }
        }
      }
    },
    {
      name: TOOL_ANALYTICS_DELETE_IMPORTED,
      description: "Deletes imported analytics rows in controlled two-step dry-run/confirm mode.",
      annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
      inputSchema: {
        type: "object",
        additionalProperties: false,
        properties: {
          sources: { type: "array", items: { type: "string", enum: [...ALL_SOURCES] } },
          client_surfaces: { type: "array", items: { type: "string", enum: ["cli", "desktop", "vscode", "vscode_insiders", "code", "unknown"] } },
          session_kinds: { type: "array", items: { type: "string", enum: ["main", "subagent", "task", "unknown"] } },
          date_from: { type: "string" },
          date_to: { type: "string" },
          period_type: { type: "string", enum: ["day", "month", "year"] },
          period_value: { type: "string" },
          delete_scope: { type: "string", enum: ["all", "sessions", "runtime_events", "source_files"] },
          dry_run: { type: "boolean" },
          confirm_delete: { type: "boolean" },
          confirm_plan_id: { type: "string" }
        }
      }
    }
  ];
}

export function getToolDefinitions(): Tool[] {
  return toolDefinitions();
}

export async function buildStatusResult(
  context: { config: ReturnType<typeof loadConfig>; db: SqliteAdapter },
  includeLocalPaths: boolean
): Promise<Record<string, unknown>> {
  const machine = await context.db.get<{ value: string }>("SELECT value FROM settings WHERE key = 'machine_id'");
  const salt = await context.db.get<{ value: string }>("SELECT value FROM settings WHERE key = 'hmac_salt'");
  const dbPath = includeLocalPaths ? context.config.dbPath : redactPath(context.config.dbPath);
  const hookLogPath = includeLocalPaths ? context.config.hookLogPath : redactPath(context.config.hookLogPath);
  const sessionsCount = (await context.db.get<{ n: number }>("SELECT COUNT(*) as n FROM sessions"))?.n ?? 0;
  const runtimeEventsCount = (await context.db.get<{ n: number }>("SELECT COUNT(*) as n FROM runtime_events"))?.n ?? 0;
  return {
    ok: true,
    server: { name: context.config.serverName, version: context.config.serverVersion },
    sources_supported: ["codex", "copilot", "claude", "hook_log"],
    db: {
      available: true,
      db_path_hash: salt ? hashPath(context.config.dbPath, salt.value) : null,
      hook_log_path_hash: salt ? hashPath(context.config.hookLogPath, salt.value) : null,
      ...(includeLocalPaths ? { db_path: dbPath, hook_log_path: hookLogPath } : {})
    },
    features: {
      pricing: false,
      dashboard: false,
      export: false
    },
    counts: {
      sessions: sessionsCount,
      runtime_events: runtimeEventsCount
    },
    settings: {
      machine_id_present: typeof machine?.value === "string" && machine.value.length > 0,
      hmac_salt_present: typeof salt?.value === "string" && salt.value.length > 0
    },
    warnings: []
  };
}

async function handleStatus(context: { config: ReturnType<typeof loadConfig>; db: SqliteAdapter }, args: unknown): Promise<Record<string, unknown>> {
  const input = parseStatusInput(args);
  return buildStatusResult(context, input.include_local_paths);
}

export async function bootstrap(): Promise<void> {
  const config = loadConfig();
  mkdirSync(dirname(config.dbPath), { recursive: true });
  mkdirSync(dirname(config.hookLogPath), { recursive: true });
  const db = new SqliteAdapter(config.dbPath);
  await migrate(db);
  await markRunningScansInterrupted(db);

  const context = { config, db };
  const server = new Server(
    { name: config.serverName, version: config.serverVersion },
    { capabilities: { tools: {} } }
  );

  server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: toolDefinitions() }));
  server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => {
    try {
      if (request.params.name === TOOL_ANALYTICS_STATUS) {
        const result = await handleStatus(context, parseObjectInput(request.params.arguments));
        return toMcpSuccess("analytics_status completed.", result);
      }
      if (request.params.name === TOOL_ANALYTICS_SCAN) {
        const result = await runAnalyticsScan(context, parseObjectInput(request.params.arguments));
        return toMcpSuccess("analytics_scan completed.", result);
      }
      if (request.params.name === TOOL_ANALYTICS_SUMMARY) {
        const result = await querySummary(context.db, parseObjectInput(request.params.arguments));
        return toMcpSuccess("analytics_summary completed.", result);
      }
      if (request.params.name === TOOL_ANALYTICS_MODELS) {
        const result = await queryModels(context.db, parseObjectInput(request.params.arguments));
        return toMcpSuccess("analytics_models completed.", result);
      }
      if (request.params.name === TOOL_ANALYTICS_SESSIONS) {
        const result = await querySessions(context.db, parseObjectInput(request.params.arguments));
        return toMcpSuccess("analytics_sessions completed.", result);
      }
      if (request.params.name === TOOL_ANALYTICS_EVENTS) {
        const result = await queryEvents(context.db, parseObjectInput(request.params.arguments));
        return toMcpSuccess("analytics_events completed.", result);
      }
      if (request.params.name === TOOL_ANALYTICS_DELETE_IMPORTED) {
        const result = await deleteImportedData(context.db, parseObjectInput(request.params.arguments));
        return toMcpSuccess("analytics_delete_imported completed.", result);
      }
      return toMcpError("UNKNOWN_TOOL", "Unknown tool.");
    } catch (error) {
      if (error instanceof AnalyticsError) {
        return toMcpError(error.code, error.message);
      }
      const detail = error instanceof Error ? error.message : "Unknown error";
      if (typeof detail === "string" && detail.startsWith("VALIDATION_ERROR:")) {
        return toMcpError("VALIDATION_ERROR", detail.replace("VALIDATION_ERROR:", "").trim());
      }
      return toMcpError("INTERNAL_ERROR", detail);
    }
  });

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

function isMainModule(): boolean {
  return typeof process.argv[1] === "string" && process.argv[1].endsWith("index.js");
}

if (isMainModule()) {
  bootstrap().catch((error: unknown) => {
    const detail = error instanceof Error ? error.message : "Unknown bootstrap error";
    console.error(JSON.stringify({ ok: false, error_code: "BOOTSTRAP_ERROR", detail }));
    process.exitCode = 1;
  });
}
