/**
 * SERP Analysis
 * ──────────────
 * Enhanced SERP analysis including AI Overview detection and competitor overlap.
 * Extracted and expanded from dataforseo-onpage.ts.
 *
 * Internal module — third-party names must not appear in user-facing contexts.
 */

import { AuditDataSourceError } from "../types";
import type { SerpPosition } from "../scrapers/dataforseo-onpage";

export type { SerpPosition };

const BASE = "https://api.dataforseo.com/v3";
const LOGIN = process.env.DATAFORSEO_LOGIN ?? "";
const PASSWORD = process.env.DATAFORSEO_PASSWORD ?? "";

function authHeader(): string {
  return "Basic " + Buffer.from(`${LOGIN}:${PASSWORD}`).toString("base64");
}

function isConfigured(): boolean {
  return !!(LOGIN && PASSWORD);
}

async function dfsPost<T = unknown>(endpoint: string, body: unknown[]): Promise<T> {
  if (!isConfigured()) throw new AuditDataSourceError("Structural scanner not configured", "dataforseo-serp");

  const res = await fetch(`${BASE}${endpoint}`, {
    method: "POST",
    headers: { Authorization: authHeader(), "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });

  if (!res.ok) {
    throw new AuditDataSourceError(`Structural scanner HTTP ${res.status}`, "dataforseo-serp");
  }

  const data = (await res.json()) as { status_code?: number; status_message?: string; tasks?: unknown[] };
  if (data.status_code !== 20000) {
    throw new AuditDataSourceError(
      `Structural scanner envelope error ${data.status_code}`,
      "dataforseo-serp",
    );
  }
  return data as T;
}

export interface SerpAnalysis {
  positions: SerpPosition[];
  aiOverviewPresence: Array<{
    keyword: string;
    hasAiOverview: boolean;
    brandMentioned: boolean;
    snippet: string | null;
  }>;
  competitorDomains: string[]; // top 5 competitor domains across all queries
  competitorDetails: Array<{
    domain: string;
    queries: string[];    // queries where this competitor appeared
    avgRankPosition: number;
  }>;
}

interface DfsSerpItem {
  type?: string;
  rank_absolute?: number;
  url?: string;
  title?: string;
  description?: string;
  text?: string;
  items?: DfsSerpItem[];
}

interface DfsSerpTask {
  data?: { keyword?: string };
  result?: Array<{
    items?: DfsSerpItem[];
  }>;
}

function extractDomain(url: string | undefined): string | null {
  if (!url) return null;
  try {
    return new URL(url).hostname.replace(/^www\./, "");
  } catch {
    return null;
  }
}

function extractAiOverviewSnippet(item: DfsSerpItem): string | null {
  // AI overview items may have text or description at root, or in nested items
  if (item.text) return item.text.slice(0, 500);
  if (item.description) return item.description.slice(0, 500);
  if (Array.isArray(item.items)) {
    const nested = item.items.find((i) => i.text ?? i.description);
    if (nested) return (nested.text ?? nested.description ?? "").slice(0, 500);
  }
  return null;
}

export async function fetchSerpAnalysis(
  queries: string[],
  locationCode = 2840,
): Promise<SerpAnalysis> {
  const empty: SerpAnalysis = { positions: [], aiOverviewPresence: [], competitorDomains: [], competitorDetails: [] };
  if (!isConfigured() || queries.length === 0) return empty;

  try {
    const tasks = queries.map((keyword) => ({
      keyword,
      location_code: locationCode,
      language_code: "en",
      depth: 10,
      device: "desktop",
    }));

    const res = await dfsPost<{
      status_code: number;
      tasks?: DfsSerpTask[];
    }>("/serp/google/organic/live/advanced", tasks);

    if (!res.tasks) return empty;

    // Domain map: domain → { querySet, totalRank, rankCount }
    const domainQueryMap = new Map<string, Set<number>>();
    const domainRankMap = new Map<string, { total: number; count: number; queries: string[] }>();

    const positions: SerpPosition[] = [];
    const aiOverviewPresence: SerpAnalysis["aiOverviewPresence"] = [];

    res.tasks.forEach((task, i) => {
      const keyword = queries[i] ?? "";
      const items = task.result?.[0]?.items ?? [];

      // Organic positions
      const organic = items.filter((it) => it.type === "organic");
      const first = organic[0];
      positions.push({
        keyword,
        position: first?.rank_absolute ?? null,
        url: first?.url ?? null,
        title: first?.title ?? null,
      });

      // Collect all organic domains for competitor analysis
      for (const orgItem of organic) {
        const dom = extractDomain(orgItem.url);
        if (dom) {
          const existing = domainQueryMap.get(dom) ?? new Set<number>();
          existing.add(i);
          domainQueryMap.set(dom, existing);

          const rankEntry = domainRankMap.get(dom) ?? { total: 0, count: 0, queries: [] };
          rankEntry.total += orgItem.rank_absolute ?? 10;
          rankEntry.count++;
          if (!rankEntry.queries.includes(keyword)) rankEntry.queries.push(keyword);
          domainRankMap.set(dom, rankEntry);
        }
      }

      // AI Overview detection
      const aiOverviewItem = items.find((it) => it.type === "ai_overview");
      if (aiOverviewItem) {
        const snippet = extractAiOverviewSnippet(aiOverviewItem);
        // brandMentioned: look for brand domain in snippet text (best-effort)
        const brandMentioned = false; // Cannot determine brand without domain context here; caller can post-process
        aiOverviewPresence.push({ keyword, hasAiOverview: true, brandMentioned, snippet });
        console.log(`[audit-serp] AI Overview detected for "${keyword}" snippet=${snippet?.slice(0, 80) ?? "none"}`);
      } else {
        aiOverviewPresence.push({ keyword, hasAiOverview: false, brandMentioned: false, snippet: null });
      }
    });

    // Top 5 competitor domains appearing in 2+ queries
    const competitorDomains = [...domainQueryMap.entries()]
      .filter(([, querySet]) => querySet.size >= 2)
      .sort((a, b) => b[1].size - a[1].size)
      .slice(0, 5)
      .map(([domain]) => domain);

    const competitorDetails = [...domainRankMap.entries()]
      .map(([domain, data]) => ({
        domain,
        queries: data.queries,
        avgRankPosition: Math.round(data.total / data.count),
      }))
      .sort((a, b) => a.avgRankPosition - b.avgRankPosition)
      .slice(0, 10);

    console.log(`[audit-serp] fetchSerpAnalysis queries=${queries.length} aiOverviews=${aiOverviewPresence.filter((a) => a.hasAiOverview).length} competitorDomains=${competitorDomains.join(",")}`);

    return { positions, aiOverviewPresence, competitorDomains, competitorDetails };
  } catch (err) {
    console.warn(
      `[audit-serp] fetchSerpAnalysis failed:`,
      err instanceof Error ? err.message : err,
    );
    return empty;
  }
}
