import type { DatabaseAdapter } from "../db.js";
import { ValidationError, assertAllowedKeys } from "../errors.js";
import { ALL_SOURCES, type AnalyticsSource } from "../sources.js";
import { parseOptionalEnumArray } from "../validation.js";

interface EventsInput {
  sources?: AnalyticsSource[];
  event_type?: "mcp" | "skill" | "hook" | "agent_tool" | "agent_lifecycle" | "hotword";
  date_from?: string;
  date_to?: string;
  include_self: boolean;
  limit: number;
  offset: number;
}

function parseIso(value: unknown): string | undefined {
  if (value === undefined) return undefined;
  if (typeof value !== "string" || !Number.isFinite(Date.parse(value))) throw new ValidationError("Invalid date.");
  return new Date(value).toISOString();
}

function parseInput(args: unknown): EventsInput {
  const obj = (args && typeof args === "object" && !Array.isArray(args) ? args : {}) as Record<string, unknown>;
  assertAllowedKeys(obj, ["sources", "event_type", "date_from", "date_to", "include_self", "limit", "offset"]);

  const sources = parseOptionalEnumArray(obj.sources, ALL_SOURCES, "sources");

  let eventType: EventsInput["event_type"];
  if (obj.event_type !== undefined) {
    if (obj.event_type !== "mcp" && obj.event_type !== "skill" && obj.event_type !== "hook" && obj.event_type !== "agent_tool" && obj.event_type !== "agent_lifecycle" && obj.event_type !== "hotword") {
      throw new ValidationError("Invalid event_type.");
    }
    eventType = obj.event_type;
  }

  if (obj.limit !== undefined && typeof obj.limit !== "number") throw new ValidationError("limit must be a number.");
  if (obj.offset !== undefined && typeof obj.offset !== "number") throw new ValidationError("offset must be a number.");
  if (obj.include_self !== undefined && typeof obj.include_self !== "boolean") throw new ValidationError("include_self must be a boolean.");
  const limit = typeof obj.limit === "number" ? Math.max(1, Math.min(1000, Math.floor(obj.limit))) : 100;
  const offset = typeof obj.offset === "number" ? Math.max(0, Math.floor(obj.offset)) : 0;
  return { sources, event_type: eventType, date_from: parseIso(obj.date_from), date_to: parseIso(obj.date_to), include_self: obj.include_self !== false, limit, offset };
}

export async function queryEvents(db: DatabaseAdapter, args: unknown): Promise<Record<string, unknown>> {
  const input = parseInput(args);
  const where: string[] = [];
  const params: unknown[] = [];
  if (input.sources) {
    where.push(`source IN (${input.sources.map(() => "?").join(",")})`);
    params.push(...input.sources);
  }
  if (input.event_type) {
    where.push("event_type = ?");
    params.push(input.event_type);
  }
  if (input.date_from) {
    where.push("occurred_at >= ?");
    params.push(input.date_from);
  }
  if (input.date_to) {
    where.push("occurred_at <= ?");
    params.push(input.date_to);
  }
  if (!input.include_self) where.push("is_self_event = 0");
  const whereSql = where.length > 0 ? `WHERE ${where.join(" AND ")}` : "";

  const total = await db.get<{ total: number }>(`SELECT COUNT(*) as total FROM runtime_events ${whereSql}`, params);
  const rows = await db.all<{
    id: string;
    source: string;
    session_id: string | null;
    event_type: string;
    event_origin: string;
    event_name: string;
    mcp_server_name: string | null;
    tool_name: string | null;
    skill_name: string | null;
    hook_name: string | null;
    hook_event: string | null;
    hook_phase: string | null;
    suggestion_outcome: string | null;
    mcp_suggestion_count: number | null;
    skill_suggestion_count: number | null;
    hotword_mode: string | null;
    context_mode: string | null;
    subagent_mode: string | null;
    quick_overridden: number | null;
    occurred_at: string;
    is_self_event: number;
  }>(
    `
    SELECT id, source, session_id, event_type, event_origin, event_name, mcp_server_name, tool_name, skill_name, hook_name, hook_event,
      hook_phase, suggestion_outcome, mcp_suggestion_count, skill_suggestion_count, hotword_mode, context_mode, subagent_mode, quick_overridden,
      occurred_at, is_self_event
    FROM runtime_events
    ${whereSql}
    ORDER BY occurred_at DESC, id ASC
    LIMIT ? OFFSET ?
    `,
    [...params, input.limit, input.offset]
  );

  const totalValue = total?.total ?? 0;
  return {
    ok: true,
    total: totalValue,
    limit: input.limit,
    offset: input.offset,
    has_more: input.offset + rows.length < totalValue,
    items: rows
  };
}
