import { prisma } from "@/lib/prisma";
import { getDateRange, DateRange } from "./date-utils";

export type CompetitorType = "national" | "local" | "emerging";

/**
 * Get all competitors for a brand with their latest snapshots.
 *
 * Returns each row in two shapes simultaneously so both the legacy
 * `latest` consumers (older dashboard widgets) and the current
 * page-card normalisation (which expects `snapshots[0].crawledAt /
 * totalPages / newPages / changedPages / removedPages` plus the
 * top-level `lastCrawledAt` + `detectedCms` + `isActive`) work without
 * a second API round-trip.
 *
 * The `lastCrawledAt` field on Competitor is the canonical source for
 * "when was this last crawled" — the snapshot's crawledAt only updates
 * when a snapshot row gets written, which happens after every crawl
 * but predates this column. Fall back to snapshot.crawledAt when the
 * Competitor.lastCrawledAt is null (older rows).
 */
export async function getCompetitors(brandId: string, range: DateRange = "30d") {
  const { start, end } = getDateRange(range);

  const competitors = await prisma.competitor.findMany({
    // Soft-deleted Competitor rows stay in the table with
    // isActive=false. Without this filter the Competitor Intel
    // page list disagreed with the Dashboard count (which already
    // filters by isActive). Both call sites now resolve to the
    // same active set.
    where: { brandId, isActive: true },
    include: {
      // Date-bounded snapshot for the `latest` legacy field.
      snapshots: {
        where: { date: { gte: start, lte: end } },
        orderBy: { date: "desc" },
        take: 1,
      },
    },
    orderBy: { updatedAt: "desc" },
  });

  // Pull the most recent snapshot for each competitor *regardless of
  // range* so the card always shows the real last-crawl numbers even
  // when the date filter excludes them. Single batched query.
  const competitorIds = competitors.map((c) => c.id);
  const latestUnbounded = competitorIds.length > 0
    ? await prisma.competitorSnapshot.findMany({
        where: { competitorId: { in: competitorIds } },
        orderBy: [{ competitorId: "asc" }, { crawledAt: "desc" }],
        select: {
          competitorId: true, crawledAt: true, totalPages: true,
          newPages: true, removedPages: true, changedPages: true,
        },
      })
    : [];
  const latestByCompetitor = new Map<string, typeof latestUnbounded[number]>();
  for (const s of latestUnbounded) {
    if (!latestByCompetitor.has(s.competitorId)) latestByCompetitor.set(s.competitorId, s);
  }

  return competitors.map((c) => {
    const latest = c.snapshots[0] || null;
    const newest = latestByCompetitor.get(c.id) ?? null;
    return {
      id: c.id,
      name: c.name,
      domain: c.domain,
      type: c.type,
      strongestPage: c.strongestPage,
      whyWinning: c.whyWinning,
      watchlist: c.watchlist,
      // ── Fields the page-card normaliser reads ──
      isActive: c.isActive,
      detectedCms: c.detectedCms,
      lastCrawledAt: c.lastCrawledAt ?? newest?.crawledAt ?? null,
      // Newest snapshot summary, regardless of `range`. The card
      // populates per-status counts via /pages?limit=0 separately, so
      // these are the fallback when that hasn't loaded yet.
      snapshots: newest ? [{
        totalPages: newest.totalPages,
        newPages: newest.newPages,
        changedPages: newest.changedPages,
        removedPages: newest.removedPages,
        crawledAt: newest.crawledAt,
      }] : [],
      // ── Legacy `latest` field ──
      latest: latest ? {
        movement: latest.movement,
        rankChange: latest.rankChange,
        aiMentions: latest.aiMentions,
        aiMentionChange: latest.aiMentionChange,
        trafficEstimate: latest.trafficEstimate,
        topKeyword: latest.topKeyword,
        threat: latest.threat,
        date: latest.date,
      } : null,
    };
  });
}

/**
 * Get competitor trend data over time.
 */
export async function getCompetitorTrend(competitorId: string, range: DateRange = "90d") {
  const { start, end } = getDateRange(range);

  return prisma.competitorSnapshot.findMany({
    where: { competitorId, date: { gte: start, lte: end } },
    orderBy: { date: "asc" },
  });
}

/**
 * Add or update a competitor for a brand.
 */
export async function upsertCompetitor(data: {
  brandId: string;
  name: string;
  domain: string;
  type?: CompetitorType;
  strongestPage?: string;
  whyWinning?: string;
  watchlist?: boolean;
}) {
  return prisma.competitor.upsert({
    where: { brandId_domain: { brandId: data.brandId, domain: data.domain } },
    update: {
      name: data.name,
      type: data.type,
      strongestPage: data.strongestPage,
      whyWinning: data.whyWinning,
      watchlist: data.watchlist,
    },
    create: {
      brandId: data.brandId,
      name: data.name,
      domain: data.domain,
      type: data.type ?? "national",
      strongestPage: data.strongestPage,
      whyWinning: data.whyWinning,
      watchlist: data.watchlist ?? false,
    },
  });
}

/**
 * Get recommendation-ready competitor insights.
 */
export async function getCompetitorRecommendations(brandId: string) {
  const competitors = await getCompetitors(brandId);
  const recs: Array<{ title: string; detail: string; severity: string; competitor: string }> = [];

  for (const c of competitors) {
    if (!c.latest) continue;

    if (c.latest.threat === "high") {
      recs.push({
        title: `${c.name} is a high threat`,
        detail: c.whyWinning || `${c.name} is gaining ground. Their strongest page is ${c.strongestPage || c.domain}. Monitor their content strategy.`,
        severity: "high",
        competitor: c.name,
      });
    }

    if (c.latest.aiMentionChange > 0) {
      recs.push({
        title: `${c.name} gained ${c.latest.aiMentionChange} AI mentions`,
        detail: `${c.name} is increasing their AI visibility. Review their content for citation patterns you can replicate.`,
        severity: c.latest.aiMentionChange >= 3 ? "high" : "medium",
        competitor: c.name,
      });
    }

    if (c.latest.rankChange > 0) {
      recs.push({
        title: `${c.name} moved up ${c.latest.rankChange} positions`,
        detail: `${c.name} improved rankings for "${c.latest.topKeyword || "core keywords"}". Review their page: ${c.strongestPage || c.domain}`,
        severity: c.latest.rankChange >= 3 ? "high" : "medium",
        competitor: c.name,
      });
    }
  }

  return recs;
}

/**
 * Get a condensed summary of competitors for a brand.
 */
export async function getCompetitorSummary(brandId: string, range: DateRange = "30d") {
  const competitors = await getCompetitors(brandId, range);

  const total = competitors.length;
  const national = competitors.filter((c) => c.type.toLowerCase() === "national").length;
  const local = competitors.filter((c) => c.type.toLowerCase() === "local").length;
  const emerging = competitors.filter((c) => c.type.toLowerCase() === "emerging").length;

  // Watchlist
  const watchlist = competitors
    .filter((c) => c.watchlist)
    .map((c) => ({
      name: c.name,
      domain: c.domain,
      threat: c.latest?.threat ?? "unknown",
      aiMentions: c.latest?.aiMentions ?? 0,
    }));

  // Top movement (biggest rank change in current period)
  const withMovement = competitors
    .filter((c) => c.latest && c.latest.rankChange !== 0)
    .sort((a, b) => Math.abs(b.latest!.rankChange) - Math.abs(a.latest!.rankChange));

  const topMovement = withMovement.length > 0
    ? {
        name: withMovement[0].name,
        domain: withMovement[0].domain,
        rankChange: withMovement[0].latest!.rankChange,
        movement: withMovement[0].latest!.movement,
        keyword: withMovement[0].latest!.topKeyword,
      }
    : null;

  // Strongest pages (one per competitor that has one)
  const strongestPages = competitors
    .filter((c) => c.strongestPage)
    .map((c) => ({
      competitor: c.name,
      domain: c.domain,
      page: c.strongestPage!,
      whyWinning: c.whyWinning,
      threat: c.latest?.threat ?? "unknown",
    }))
    .sort((a, b) => {
      const order: Record<string, number> = { high: 0, medium: 1, low: 2, unknown: 3 };
      return (order[a.threat] ?? 3) - (order[b.threat] ?? 3);
    });

  // High threats
  const highThreats = competitors.filter((c) => c.latest?.threat === "high").length;

  // Total AI mentions across competitors
  const totalAiMentions = competitors.reduce((s, c) => s + (c.latest?.aiMentions ?? 0), 0);

  return {
    total,
    byType: { national, local, emerging },
    highThreats,
    totalAiMentions,
    watchlist,
    topMovement,
    strongestPages,
  };
}
