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

interface SessionsInput {
  sources?: ("codex" | "copilot" | "claude")[];
  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): SessionsInput {
  const obj = (args && typeof args === "object" && !Array.isArray(args) ? args : {}) as Record<string, unknown>;
  assertAllowedKeys(obj, ["sources", "date_from", "date_to", "limit", "offset"]);

  const allowed = ["codex", "copilot", "claude"] as const;
  let sources: SessionsInput["sources"];
  if (Array.isArray(obj.sources)) {
    sources = [];
    for (const src of obj.sources) {
      if (typeof src !== "string" || !allowed.includes(src as (typeof allowed)[number])) throw new ValidationError("Invalid sources.");
      if (!sources.includes(src as (typeof allowed)[number])) sources.push(src as (typeof allowed)[number]);
    }
  }
  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, date_from: parseIso(obj.date_from), date_to: parseIso(obj.date_to), limit, offset };
}

export async function querySessions(db: DatabaseAdapter, args: unknown): Promise<Record<string, unknown>> {
  const input = parseInput(args);
  const where: string[] = ["source IN ('codex','copilot','claude')"];
  const params: unknown[] = [];
  if (input.sources && input.sources.length > 0) {
    where.push(`source IN (${input.sources.map(() => "?").join(",")})`);
    params.push(...input.sources);
  }
  if (input.date_from) {
    where.push("updated_at >= ?");
    params.push(input.date_from);
  }
  if (input.date_to) {
    where.push("updated_at <= ?");
    params.push(input.date_to);
  }

  const total = await db.get<{ total: number }>(`SELECT COUNT(*) as total FROM sessions WHERE ${where.join(" AND ")}`, params);
  const rows = await db.all<{
    id: string;
    source: string;
    project_id: string | null;
    mode: string;
    model_primary: string | null;
    token_available: number;
    message_count: number;
    user_message_count: number;
    assistant_message_count: number;
    created_at: string | null;
    updated_at: string;
  }>(
    `
    SELECT id, source, project_id, mode, model_primary, token_available, message_count, user_message_count, assistant_message_count, created_at, updated_at
    FROM sessions
    WHERE ${where.join(" AND ")}
    ORDER BY updated_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
  };
}
