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

interface ModelsInput {
  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): ModelsInput {
  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: ModelsInput["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 queryModels(db: DatabaseAdapter, args: unknown): Promise<Record<string, unknown>> {
  const input = parseInput(args);
  const where: string[] = ["s.source IN ('codex','copilot','claude')"];
  const params: unknown[] = [];
  if (input.sources && input.sources.length > 0) {
    where.push(`s.source IN (${input.sources.map(() => "?").join(",")})`);
    params.push(...input.sources);
  }
  if (input.date_from) {
    where.push("s.updated_at >= ?");
    params.push(input.date_from);
  }
  if (input.date_to) {
    where.push("s.updated_at <= ?");
    params.push(input.date_to);
  }

  const rows = await db.all<{
    model: string | null;
    sessions: number;
    messages: number;
    input_tokens: number;
    output_tokens: number;
    reasoning_tokens: number;
    token_available_count: number;
    token_missing_count: number;
    partial_token_data_count: number;
  }>(
    `
    SELECT
      COALESCE(NULLIF(mm.model, ''), NULLIF(s.model_primary,''), 'unknown') as model,
      COUNT(DISTINCT s.id) as sessions,
      COUNT(mm.id) as messages,
      COALESCE(SUM(mm.input_tokens),0) as input_tokens,
      COALESCE(SUM(mm.output_tokens),0) as output_tokens,
      COALESCE(SUM(mm.reasoning_tokens),0) as reasoning_tokens,
      COALESCE(SUM(CASE WHEN mm.token_available = 1 THEN 1 ELSE 0 END),0) as token_available_count,
      COALESCE(SUM(CASE WHEN mm.token_available = 0 THEN 1 ELSE 0 END),0) as token_missing_count,
      COALESCE(SUM(CASE WHEN mm.partial_token_data = 1 THEN 1 ELSE 0 END),0) as partial_token_data_count
    FROM sessions s
    LEFT JOIN message_metrics mm ON mm.session_id = s.id
    WHERE ${where.join(" AND ")}
    GROUP BY COALESCE(NULLIF(mm.model, ''), NULLIF(s.model_primary,''), 'unknown')
    ORDER BY sessions DESC, model ASC
    LIMIT ? OFFSET ?
    `,
    [...params, input.limit, input.offset]
  );

  return {
    ok: true,
    limit: input.limit,
    offset: input.offset,
    models: rows.map((row) => ({
      model: row.model,
      sessions: row.sessions,
      messages: row.messages,
      input_tokens: row.input_tokens,
      output_tokens: row.output_tokens,
      reasoning_tokens: row.reasoning_tokens,
      token_available_count: row.token_available_count,
      token_missing_count: row.token_missing_count,
      partial_token_data: row.partial_token_data_count > 0
    }))
  };
}
