/**
 * Competitive AI Change Detection & Alerting
 * ────────────────────────────────────────────
 * Compares AiCitation snapshots across check periods to detect
 * position changes, new competitors, and lost citations.
 * Writes signals via the existing SiteEvent persistence model.
 */

import { prisma } from "@/lib/prisma";
import crypto from "crypto";

export interface AiCompetitiveSignal {
  type: "ai_position_lost" | "ai_position_gained" | "ai_new_competitor" | "ai_competitor_lost";
  priority: "critical" | "high" | "medium";
  keyword: string;
  details: string;
  metadata: Record<string, unknown>;
}

export async function detectCompetitiveChanges(brandId: string): Promise<AiCompetitiveSignal[]> {
  const now = new Date();
  const d14 = new Date(now.getTime() - 14 * 86_400_000);
  const d7 = new Date(now.getTime() - 7 * 86_400_000);

  const [latest, previous] = await Promise.all([
    prisma.aiCitation.findMany({
      where: { brandId, checkedAt: { gte: d7 } },
      orderBy: { checkedAt: "desc" },
    }),
    prisma.aiCitation.findMany({
      where: { brandId, checkedAt: { gte: d14, lt: d7 } },
      orderBy: { checkedAt: "desc" },
    }),
  ]);

  if (latest.length === 0 || previous.length === 0) return [];

  const latestByKw = groupByKeyword(latest);
  const previousByKw = groupByKeyword(previous);
  const signals: AiCompetitiveSignal[] = [];

  for (const [keyword, latestCitations] of latestByKw) {
    const prevCitations = previousByKw.get(keyword);
    if (!prevCitations) continue;

    const wasCited = prevCitations.some((c) => c.brandCited);
    const isCited = latestCitations.some((c) => c.brandCited);

    if (wasCited && !isCited) {
      const prevPos = prevCitations.find((c) => c.brandCited)?.citedPosition ?? 0;
      const topCompetitor = extractTopCompetitor(latestCitations);
      signals.push({
        type: "ai_position_lost",
        priority: "critical",
        keyword,
        details: `You lost your AI citation for "${keyword}".${prevPos ? ` Was position #${prevPos}.` : ""}${topCompetitor ? ` ${topCompetitor} is now cited.` : ""}`,
        metadata: { previousPosition: prevPos, topCompetitor },
      });
    }

    if (!wasCited && isCited) {
      const pos = latestCitations.find((c) => c.brandCited)?.citedPosition ?? 0;
      signals.push({
        type: "ai_position_gained",
        priority: "high",
        keyword,
        details: `You gained an AI citation for "${keyword}"${pos ? ` at position #${pos}` : ""}.`,
        metadata: { newPosition: pos },
      });
    }

    const prevCompetitors = extractCompetitors(prevCitations);
    const currentCompetitors = extractCompetitors(latestCitations);

    for (const comp of currentCompetitors) {
      if (!prevCompetitors.has(comp)) {
        signals.push({
          type: "ai_new_competitor",
          priority: "medium",
          keyword,
          details: `New competitor ${comp} appeared in AI results for "${keyword}".`,
          metadata: { competitor: comp },
        });
      }
    }

    for (const comp of prevCompetitors) {
      if (!currentCompetitors.has(comp)) {
        signals.push({
          type: "ai_competitor_lost",
          priority: "medium",
          keyword,
          details: `Competitor ${comp} lost their AI citation for "${keyword}".`,
          metadata: { competitor: comp },
        });
      }
    }
  }

  return signals;
}

export async function writeCompetitiveSignals(
  brandId: string,
  signals: AiCompetitiveSignal[],
): Promise<{ created: number; deduped: number }> {
  let created = 0;
  let deduped = 0;

  for (const signal of signals) {
    const hash = crypto.createHash("sha1")
      .update(`${brandId}:competitive:${signal.type}:${signal.keyword}`)
      .digest("hex");

    const existing = await prisma.siteEvent.findFirst({
      where: {
        brandId,
        eventType: "signal_ai_competitive",
        timestamp: { gte: new Date(Date.now() - 7 * 86_400_000) },
        eventData: { path: ["contentHash"], equals: hash },
      },
      select: { id: true },
    }).catch(() => null);

    if (existing) {
      deduped++;
      continue;
    }

    await prisma.siteEvent.create({
      data: {
        brandId,
        eventType: "signal_ai_competitive",
        pageUrl: "",
        eventData: JSON.parse(JSON.stringify({
          title: signal.details.slice(0, 100),
          description: signal.details,
          category: signal.type === "ai_position_lost" ? "issue" : signal.type === "ai_position_gained" ? "win" : "opportunity",
          priority: signal.priority,
          signalType: signal.type,
          keyword: signal.keyword,
          metadata: signal.metadata,
          contentHash: hash,
          status: "active",
        })),
      },
    }).catch(() => null);
    created++;
  }

  return { created, deduped };
}

export async function getCompetitiveTimeline(
  brandId: string,
  keyword?: string,
  limit = 20,
): Promise<Array<{
  keyword: string;
  checkedAt: Date;
  brandCited: boolean;
  citedPosition: number | null;
  competitors: Array<{ domain: string; position: number }>;
}>> {
  const where: Record<string, unknown> = { brandId };
  if (keyword) where.keyword = keyword;

  const citations = await prisma.aiCitation.findMany({
    where,
    orderBy: { checkedAt: "desc" },
    take: limit,
    select: {
      keyword: true,
      checkedAt: true,
      brandCited: true,
      citedPosition: true,
      competitorsCited: true,
    },
  });

  return citations.map((c) => ({
    keyword: c.keyword,
    checkedAt: c.checkedAt,
    brandCited: c.brandCited,
    citedPosition: c.citedPosition,
    competitors: parseCompetitors(c.competitorsCited),
  }));
}

// ─── Helpers ───────────────────────────────────────────────────────────────

function groupByKeyword(citations: Array<{
  keyword: string;
  brandCited: boolean;
  citedPosition: number | null;
  competitorsCited: unknown;
}>): Map<string, typeof citations> {
  const map = new Map<string, typeof citations>();
  for (const c of citations) {
    const list = map.get(c.keyword) ?? [];
    list.push(c);
    map.set(c.keyword, list);
  }
  return map;
}

function extractTopCompetitor(citations: Array<{ competitorsCited: unknown }>): string | null {
  for (const c of citations) {
    const comps = parseCompetitors(c.competitorsCited);
    if (comps.length > 0) return comps[0].domain;
  }
  return null;
}

function extractCompetitors(citations: Array<{ competitorsCited: unknown }>): Set<string> {
  const set = new Set<string>();
  for (const c of citations) {
    for (const comp of parseCompetitors(c.competitorsCited)) {
      set.add(comp.domain);
    }
  }
  return set;
}

function parseCompetitors(raw: unknown): Array<{ domain: string; position: number }> {
  if (!Array.isArray(raw)) return [];
  return raw.filter(
    (item): item is { domain: string; position: number } =>
      typeof item === "object" && item !== null && typeof (item as Record<string, unknown>).domain === "string",
  );
}
