/**
 * Competitor Monitoring Service
 * ─────────────────────────────
 * Sitemap crawling, change detection, ranking comparison, and AI
 * competitive analysis. External fetches are rate-limited and
 * results are cached in CompetitorSnapshot / CompetitorRanking.
 */

import { prisma } from "@/lib/prisma";
import { XMLParser } from "fast-xml-parser";
import { callLlm } from "@/lib/ai-avatar/llm-client";
import { checkKeywordRank } from "@/lib/services/rank-check";

const parser = new XMLParser({ ignoreAttributes: false });
const FETCH_TIMEOUT = 10000;
const MAX_DETAIL_FETCH = 20;

function sleep(ms: number) { return new Promise((r) => setTimeout(r, ms)); }

async function safeFetch(url: string): Promise<string | null> {
  try {
    const ctrl = new AbortController();
    const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT);
    const res = await fetch(url, { signal: ctrl.signal, headers: { "User-Agent": "meSEO-Bot/1.0" } });
    clearTimeout(timer);
    return res.ok ? await res.text() : null;
  } catch { return null; }
}

// ─── Sitemap crawling ───────────────────────────────────────────────────────

function extractUrls(xml: string): string[] {
  try {
    const parsed = parser.parse(xml);
    // Handle sitemap index
    const sitemaps = parsed?.sitemapindex?.sitemap;
    if (sitemaps) {
      const locs = Array.isArray(sitemaps) ? sitemaps : [sitemaps];
      return locs.map((s: { loc?: string }) => s.loc).filter(Boolean) as string[];
    }
    // Handle urlset
    const urls = parsed?.urlset?.url;
    if (urls) {
      const items = Array.isArray(urls) ? urls : [urls];
      return items.map((u: { loc?: string }) => u.loc).filter(Boolean) as string[];
    }
    return [];
  } catch { return []; }
}

export async function crawlSitemap(domain: string): Promise<{ urls: string[]; sitemapUrl: string }> {
  const clean = domain.replace(/^(https?:\/\/)/, "").replace(/\/$/, "");
  const base = `https://${clean}`;

  // Try common sitemap locations
  for (const path of ["/sitemap.xml", "/sitemap_index.xml"]) {
    const xml = await safeFetch(`${base}${path}`);
    if (xml) {
      let urls = extractUrls(xml);
      // If sitemap index, fetch child sitemaps
      if (urls.length > 0 && urls[0].endsWith(".xml")) {
        const childUrls: string[] = [];
        for (const childUrl of urls.slice(0, 10)) {
          const childXml = await safeFetch(childUrl);
          if (childXml) childUrls.push(...extractUrls(childXml));
          await sleep(200);
        }
        urls = childUrls;
      }
      return { urls: [...new Set(urls)], sitemapUrl: `${base}${path}` };
    }
  }

  // Check robots.txt
  const robots = await safeFetch(`${base}/robots.txt`);
  if (robots) {
    const match = robots.match(/Sitemap:\s*(https?:\/\/\S+)/i);
    if (match) {
      const xml = await safeFetch(match[1]);
      if (xml) {
        const urls = extractUrls(xml);
        return { urls: [...new Set(urls)], sitemapUrl: match[1] };
      }
    }
  }

  return { urls: [], sitemapUrl: "" };
}

// ─── Change detection ───────────────────────────────────────────────────────

export async function detectChanges(competitorId: string, currentUrls: string[]): Promise<{ newPages: string[]; removedPages: string[] }> {
  const prev = await prisma.competitorSnapshot.findFirst({
    where: { competitorId },
    orderBy: { crawledAt: "desc" },
    select: { sitemapUrls: true },
  });
  const prevUrls = new Set<string>((prev?.sitemapUrls as string[] | null) ?? []);
  const currSet = new Set(currentUrls);

  const newPages = currentUrls.filter((u) => !prevUrls.has(u)).slice(0, 50);
  const removedPages = [...prevUrls].filter((u) => !currSet.has(u)).slice(0, 50);
  return { newPages, removedPages };
}

// ─── Ranking comparison ─────────────────────────────────────────────────────

export interface RankComparison { keyword: string; brandPosition: number | null; competitorPosition: number | null; winner: string; gap: number | null }

export async function compareRankings(brandId: string, competitorId: string): Promise<RankComparison[]> {
  const comp = await prisma.competitor.findUnique({ where: { id: competitorId } });
  if (!comp) return [];
  const brand = await prisma.brand.findUnique({ where: { id: brandId }, select: { domain: true } });
  if (!brand) return [];

  const tracked = await prisma.trackedKeyword.findMany({
    where: { brandId, isActive: true },
    include: { rankings: { orderBy: { checkedAt: "desc" }, take: 1 } },
    take: 20,
  });

  const results: RankComparison[] = [];
  for (const kw of tracked) {
    const brandPos = kw.rankings[0]?.position ?? null;
    const compResult = await checkKeywordRank(kw.keyword, comp.domain);
    if (compResult.position != null) {
      await prisma.competitorRanking.create({
        data: { competitorId, keyword: kw.keyword, position: compResult.position, url: compResult.url },
      });
    }
    const gap = brandPos != null && compResult.position != null ? compResult.position - brandPos : null;
    results.push({
      keyword: kw.keyword, brandPosition: brandPos, competitorPosition: compResult.position,
      winner: brandPos != null && compResult.position != null ? (brandPos <= compResult.position ? "brand" : "competitor") : "unknown",
      gap,
    });
    await sleep(200);
  }
  return results;
}

// ─── AI analysis ────────────────────────────────────────────────────────────

export interface CompetitorAnalysis {
  summary: string;
  threats: Array<{ title: string; description: string; severity: string }>;
  opportunities: Array<{ title: string; description: string; impact: string }>;
  contentGaps: Array<{ topic: string; competitorUrl?: string; suggestedAction: string }>;
  rankingBattles: Array<{ keyword: string; brandPos: number | null; compPos: number | null; recommendation: string }>;
  strategicRecommendation: string;
}

export async function analyzeCompetitor(competitorId: string, brandId: string): Promise<CompetitorAnalysis> {
  const comp = await prisma.competitor.findUnique({ where: { id: competitorId } });
  const brand = await prisma.brand.findUnique({ where: { id: brandId }, select: { name: true, domain: true, industry: true } });
  const latest = await prisma.competitorSnapshot.findFirst({ where: { competitorId }, orderBy: { crawledAt: "desc" } });
  const rankings = await prisma.competitorRanking.findMany({ where: { competitorId }, orderBy: { checkedAt: "desc" }, take: 20 });

  const newPages = (latest?.newPages as string[] | null) ?? [];
  const compKws = rankings.slice(0, 10).map((r) => `${r.keyword} (pos ${r.position ?? "?"})`).join(", ");

  const result = await callLlm([
    { role: "system", content: "You are a competitive intelligence analyst. Respond ONLY with JSON." },
    { role: "user", content: `Analyze competitor ${comp?.domain} for ${brand?.name} (${brand?.industry || "general"}, ${brand?.domain}).
Competitor pages: ${latest?.totalPages ?? "?"} total, ${newPages.length} new recently.
New pages: ${newPages.slice(0, 10).join(", ") || "none detected"}
Competitor rankings: ${compKws || "none checked"}

Return JSON: {"summary":"...","threats":[{"title":"...","description":"...","severity":"high|medium|low"}],"opportunities":[{"title":"...","description":"...","impact":"high|medium|low"}],"contentGaps":[{"topic":"...","suggestedAction":"..."}],"rankingBattles":[{"keyword":"...","brandPos":N,"compPos":N,"recommendation":"..."}],"strategicRecommendation":"..."}` },
  ], () => JSON.stringify({ summary: "Configure AI API key for competitive analysis.", threats: [], opportunities: [], contentGaps: [], rankingBattles: [], strategicRecommendation: "" }), { maxTokens: 3000 });

  try {
    let s = result.reply.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "");
    const f = s.indexOf("{"), l = s.lastIndexOf("}");
    if (f >= 0 && l > f) s = s.slice(f, l + 1);
    return JSON.parse(s) as CompetitorAnalysis;
  } catch { return { summary: result.reply.slice(0, 300), threats: [], opportunities: [], contentGaps: [], rankingBattles: [], strategicRecommendation: "" }; }
}

// ─── Full crawl + snapshot ──────────────────────────────────────────────────

export async function crawlAndSnapshot(competitorId: string, brandId: string): Promise<{ totalPages: number; newPages: string[]; removedPages: string[] }> {
  const comp = await prisma.competitor.findUnique({ where: { id: competitorId } });
  if (!comp) throw new Error("Competitor not found");

  const { urls } = await crawlSitemap(comp.domain);
  const { newPages, removedPages } = await detectChanges(competitorId, urls);

  // Fetch homepage metadata
  let title: string | null = null;
  let metaDesc: string | null = null;
  const html = await safeFetch(`https://${comp.domain.replace(/^https?:\/\//, "")}`);
  if (html) {
    title = html.match(/<title[^>]*>([^<]+)</i)?.[1]?.trim() ?? null;
    metaDesc = html.match(/<meta[^>]*name=["']description["'][^>]*content=["']([^"']+)/i)?.[1]?.trim() ?? null;
  }

  await prisma.competitorSnapshot.create({
    data: {
      competitorId, brandId, date: new Date(),
      totalPages: urls.length,
      newPages: newPages as unknown as object,
      removedPages: removedPages as unknown as object,
      sitemapUrls: urls as unknown as object,
      title, metaDescription: metaDesc,
    },
  });

  return { totalPages: urls.length, newPages, removedPages };
}
