import { ENGINE_DISPLAY_META, fetchWithTimeout, retryWithBackoff, classifyError, extractDomain, parseJsonSafely } from "./shared";
import type { EngineResponse, Citation } from "./shared";
import { getCachedOrQuery } from "../cache";

const META = ENGINE_DISPLAY_META.Gemini;
const TIMEOUT_MS = 25_000;

interface GeminiGroundingChunk {
  web?: { uri?: string; title?: string };
}

interface GeminiGroundingMetadata {
  groundingChunks?: GeminiGroundingChunk[];
}

interface GeminiCandidate {
  content?: {
    parts?: Array<{ text?: string }>;
  };
  groundingMetadata?: GeminiGroundingMetadata;
}

interface GeminiBody {
  candidates?: GeminiCandidate[];
}

export async function queryGemini(query: string): Promise<EngineResponse> {
  return getCachedOrQuery("Gemini", query, () => _query(query));
}

async function _query(query: string): Promise<EngineResponse> {
  const apiKey = process.env.GEMINI_API_KEY?.trim();
  if (!apiKey) {
    return errorResponse("GEMINI_API_KEY not configured");
  }

  const t0 = Date.now();
  const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${apiKey}`;

  try {
    const res = await retryWithBackoff(
      () =>
        fetchWithTimeout(
          apiUrl,
          {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({
              contents: [{ parts: [{ text: query }] }],
              tools: [{ google_search: {} }],
            }),
          },
          TIMEOUT_MS,
        ),
      1,
      (err) => err instanceof Error && err.message.includes("fetch"),
    );

    if (!res.ok) {
      const engineError = classifyError(res.status, `Gemini HTTP ${res.status}`);
      return errorResponse(engineError.message, Date.now() - t0);
    }

    const body = parseJsonSafely<GeminiBody>(await res.text());
    const candidate = body?.candidates?.[0];
    const responseText =
      candidate?.content?.parts?.map((p) => p.text ?? "").join("") ?? null;

    const citations: Citation[] = (
      candidate?.groundingMetadata?.groundingChunks ?? []
    )
      .filter((c) => c.web?.uri)
      .map((c) => ({
        title: c.web?.title ?? extractDomain(c.web?.uri ?? ""),
        url: c.web?.uri ?? "",
        snippet: "",
        domain: extractDomain(c.web?.uri ?? ""),
      }));

    return {
      engineInternalName: "Gemini",
      engineNumber: META.number,
      engineDisplayName: META.displayName,
      engineSubtitle: META.subtitle,
      success: true,
      responseText,
      citations,
      errorMessage: null,
      durationMs: Date.now() - t0,
    };
  } catch (err) {
    return errorResponse(err instanceof Error ? err.message : "Unknown error", Date.now() - t0);
  }
}

function errorResponse(message: string, durationMs = 0): EngineResponse {
  return {
    engineInternalName: "Gemini",
    engineNumber: META.number,
    engineDisplayName: META.displayName,
    engineSubtitle: META.subtitle,
    success: false,
    responseText: null,
    citations: [],
    errorMessage: message,
    durationMs,
  };
}
