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

interface EventsInput {
  sources?: AnalyticsSource[];
  event_type?: "mcp" | "skill" | "hook" | "agent_tool" | "agent_lifecycle";
  date_from?: string;
  date_to?: string;
  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", "limit", "offset"]);

  let sources: AnalyticsSource[] | undefined;
  if (Array.isArray(obj.sources)) {
    sources = [];
    for (const src of obj.sources) {
      if (typeof src !== "string" || !ALL_SOURCES.includes(src as AnalyticsSource)) throw new ValidationError("Invalid sources.");
      if (!sources.includes(src as AnalyticsSource)) sources.push(src as AnalyticsSource);
    }
  }

  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") {
      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.");
  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), 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 && input.sources.length > 0) {
    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);
  }
  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;
    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, 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
  };
}
