import "server-only";

import { prisma } from "@/lib/prisma";
import type { SeoSection } from "@/lib/audit/types";
import type { ErrorPattern } from "@/lib/services/site-tag-analytics";
import type {
  HeroViewModel,
  StatsViewModel,
  PriorityViewModel,
  PriorityItemViewModel,
  MatrixViewModel,
  MatrixRowViewModel,
  AnnotationViewModel,
  AnnotationFindingViewModel,
  BehavioralViewModel,
  BehavioralCardViewModel,
} from "./types";

// ─── Prisma query ─────────────────────────────────────────────────────────────

export interface AuditResult {
  auditId: string;
  createdAt: string;
  brandName: string;
  brandDomain: string;
  degradedMode: boolean;
  /** Crawl failure reason -- present only when degradedMode is true. */
  crawlErrorReason?: string;
  seoSection: SeoSection;
  previousHealthScore: number | null;
}

export async function getLatestAudit(brandId: string): Promise<AuditResult | null> {
  const rows = await prisma.audit.findMany({
    where: { brandId, status: "completed" },
    orderBy: { createdAt: "desc" },
    take: 2,
    select: {
      id: true,
      createdAt: true,
      degradedMode: true,
      seoSection: true,
      brand: { select: { name: true, domain: true } },
    },
  });

  if (!rows.length || !rows[0].seoSection) return null;

  const latest = rows[0];
  const prior = rows[1];
  const seoSection = latest.seoSection as unknown as SeoSection;
  const previousHealthScore =
    prior?.seoSection != null
      ? (prior.seoSection as unknown as SeoSection).healthScore ?? null
      : null;

  return {
    auditId: latest.id,
    createdAt: latest.createdAt.toISOString(),
    brandName: latest.brand?.name ?? "",
    brandDomain: latest.brand?.domain ?? "",
    degradedMode: latest.degradedMode ?? false,
    crawlErrorReason: seoSection.crawlErrorReason,
    seoSection,
    previousHealthScore,
  };
}

// ─── Hero ─────────────────────────────────────────────────────────────────────

export function buildHeroViewModel(
  seoSection: SeoSection,
  previousHealthScore: number | null,
  brandName: string,
  brandDomain: string,
): HeroViewModel {
  const ds = seoSection.dimensionScores;
  const dimensions = ds
    ? [
        { label: "TECHNICAL",  score: ds.technicalSeo },
        { label: "CONTENT",    score: ds.contentMetadata },
        { label: "AI VIS.",    score: ds.aiVisibility ?? 0 },
        { label: "CONVERSION", score: ds.conversionOptimization ?? 0 },
        { label: "ENGAGEMENT", score: ds.userEngagement ?? 0 },
      ]
    : [];

  const narrative =
    seoSection.executiveBrief?.headline?.narrative ??
    seoSection.narrative ??
    "";

  const cl = seoSection.competitiveLandscape;
  const topCompetitorDomains = cl?.topCompetitorDetails?.map((c) => c.domain) ??
    cl?.topCompetitors ?? [];

  return {
    healthScore: seoSection.healthScore,
    healthDelta:
      previousHealthScore != null
        ? seoSection.healthScore - previousHealthScore
        : null,
    dimensions,
    narrative,
    hasCompetitors: topCompetitorDomains.length > 0,
    topCompetitorDomains,
    brandName,
    brandDomain,
  };
}

// ─── Stats ────────────────────────────────────────────────────────────────────

export interface SiteTagStatCounts {
  errors: number | null;    // total js_error events in last 30d (null = timed out)
  friction: number | null;  // total rage_click / dead_click events in last 30d
  notFound: number | null;  // total broken_link / http_error events in last 30d
  errorPatterns?: ErrorPattern[] | null;
  frictionPageCount?: number | null;
}

export function buildStatsViewModel(
  seoSection: SeoSection,
  siteTag: SiteTagStatCounts = { errors: 0, friction: 0, notFound: 0 },
): StatsViewModel {
  const sc = seoSection.signalCenter;
  const friction = sc?.topFrictionEvents ?? [];

  const scCritical = friction.filter((e) => e.severity === "critical").length;
  const scHigh = friction.filter((e) => e.severity === "high").length;
  const scMedium = friction.filter((e) => e.severity === "medium").length;

  const auditCritical = seoSection.scoringBreakdown.critical;
  const auditHigh = seoSection.scoringBreakdown.high;
  const auditMedium = seoSection.scoringBreakdown.medium;
  const auditQuickWins = seoSection.quickWins?.length ?? 0;

  // Null counts mean the Site Tag count() query timed out.
  // We degrade gracefully: use 0 for total computation but flag isUpdating.
  const errSiteTag = siteTag.errors ?? 0;
  const frictionSiteTag = siteTag.friction ?? 0;
  const notFoundSiteTag = siteTag.notFound ?? 0;

  // Pattern-level stats: collapse raw event counts to distinct error patterns
  // and affected page counts. Falls back to raw counts when patterns are absent.
  const errorPatternCount = siteTag.errorPatterns != null
    ? siteTag.errorPatterns.length
    : null;
  const affectedErrorPageCount = siteTag.errorPatterns != null
    ? new Set(siteTag.errorPatterns.flatMap((p) => p.topPages.map((pg) => pg.pageUrl))).size
    : null;
  const frictionPatternCount = siteTag.frictionPageCount != null
    ? siteTag.frictionPageCount
    : null;

  return {
    errors: {
      total: auditCritical + errSiteTag + scCritical,
      audit: auditCritical,
      siteTag: errSiteTag,
      signal: scCritical,
      patternCount: errorPatternCount,
      affectedPageCount: affectedErrorPageCount,
      isUpdating: siteTag.errors === null,
    },
    warnings: {
      total: auditHigh + frictionSiteTag + scHigh,
      audit: auditHigh,
      siteTag: frictionSiteTag,
      signal: scHigh,
      patternCount: frictionPatternCount,
      affectedPageCount: frictionPatternCount,
      isUpdating: siteTag.friction === null,
    },
    notices: {
      total: auditMedium + scMedium,
      audit: auditMedium,
      siteTag: 0,
      signal: scMedium,
      isUpdating: false,
    },
    quickWins: {
      total: auditQuickWins + notFoundSiteTag,
      audit: auditQuickWins,
      siteTag: notFoundSiteTag,
      signal: 0,
      isUpdating: siteTag.notFound === null,
    },
  };
}

// ─── Priority Stack ───────────────────────────────────────────────────────────

export function buildPriorityViewModel(seoSection: SeoSection): PriorityViewModel {
  const findings = (seoSection.prioritizedFindings ?? []).filter(
    (f) => f.priority === "critical" || f.priority === "high",
  );

  const items: PriorityItemViewModel[] = findings.map((f) => {
    const action = seoSection.priorityActions?.find((a) =>
      a.title.toLowerCase().startsWith(f.title.toLowerCase().slice(0, 25)),
    );

    const affectedPages = f.affectedUrls ?? [];

    const dimensionByCategory: Record<string, string> = {
      tech: "REAL-USER",
      multilingual: "I18N",
      compliance: "REGULATORY",
      ecommerce: "ECOM",
      b2b: "B2B",
    };
    const cat = f.category ?? "seo";
    return {
      severity: f.priority as "critical" | "high",
      dimension: dimensionByCategory[cat] ?? ((f.dataSourceWeave ?? []).join(" · ") || "SEO"),
      scoreLift: action?.expectedLiftPoints ?? null,
      title: f.title,
      subline: f.whereItFallsShort,
      recommendedNextSteps: f.recommendedNextSteps ?? [],
      affectedPages,
      fixCode: action?.implementation?.codeSnippet ?? null,
      fixAction: action?.implementation?.implementationSteps?.[0] ?? null,
      category: f.category ?? "seo",
    };
  });

  return { items };
}

// ─── Matrix ───────────────────────────────────────────────────────────────────

function pageTypeToIntent(
  pageType: string,
): "info" | "commercial" | "transactional" {
  if (pageType === "contact" || pageType === "product") return "transactional";
  if (pageType === "service" || pageType === "pricing") return "commercial";
  return "info";
}

function issueLevel(count: number, hasCritical: boolean): "critical" | "issues" | "none" {
  if (hasCritical) return "critical";
  if (count > 0) return "issues";
  return "none";
}

function categorize(issue: string): "tech" | "schema" | "eeat" | "snippet" {
  const t = issue.toLowerCase();
  if (t.includes("schema") || t.includes("structured data") || t.includes("json-ld")) return "schema";
  if (t.includes("eeat") || t.includes("e-e-a-t") || t.includes("author") || t.includes("expertise") || t.includes("trust")) return "eeat";
  if (t.includes("snippet") || t.includes("faq") || t.includes("speakable") || t.includes("featured")) return "snippet";
  return "tech";
}

export function buildMatrixViewModel(seoSection: SeoSection): MatrixViewModel {
  const pages = seoSection.pageStrategicPass?.pages ?? [];

  const rows: MatrixRowViewModel[] = pages.map((p) => {
    const issues = p.topIssues ?? [];

    const buckets = { tech: 0, schema: 0, eeat: 0, snippet: 0 };
    const criticals = { tech: false, schema: false, eeat: false, snippet: false };

    for (const issue of issues) {
      const cat = categorize(issue.issue);
      buckets[cat]++;
      if (issue.severity === "critical") criticals[cat] = true;
    }

    const traffic =
      p.behavioralSignals?.bounceRate != null
        ? `${Math.round((1 - p.behavioralSignals.bounceRate) * 100)}% eng.`
        : "--";

    return {
      page: p.url,
      intent: pageTypeToIntent(p.pageType),
      traffic,
      tech:    { count: buckets.tech,    level: issueLevel(buckets.tech,    criticals.tech)    },
      schema:  { count: buckets.schema,  level: issueLevel(buckets.schema,  criticals.schema)  },
      eeat:    { count: buckets.eeat,    level: issueLevel(buckets.eeat,    criticals.eeat)    },
      snippet: { count: buckets.snippet, level: issueLevel(buckets.snippet, criticals.snippet) },
      voice: null,
      aiVis:
        p.aiSignals?.aiVisits30d != null && p.aiSignals.aiVisits30d > 0
          ? `${p.aiSignals.aiVisits30d} visits`
          : null,
    };
  });

  return { rows };
}

// ─── Annotation ───────────────────────────────────────────────────────────────

export function buildAnnotationViewModel(seoSection: SeoSection): AnnotationViewModel {
  const lva = seoSection.liveVisualAnnotations;
  if (!lva?.hasAnnotations || !lva.perPageAnnotations.length) {
    return { available: false, pageTitle: "", findings: [] };
  }

  const page = lva.perPageAnnotations[0];
  const findings: AnnotationFindingViewModel[] = page.annotations
    .filter((a) => a.severity !== "positive" && a.severity !== "low")
    .slice(0, 4)
    .map((a) => ({
      num: a.number,
      severity: a.severity as "critical" | "high" | "moderate",
      title: a.shortLabel,
      desc: a.description,
    }));

  return { available: true, pageTitle: page.pageLabel, findings };
}

// ─── Behavioral ───────────────────────────────────────────────────────────────

export function buildBehavioralViewModel(seoSection: SeoSection): BehavioralViewModel {
  const sc = seoSection.signalCenter;
  if (!sc) return { available: false, cards: [] };

  const frictionEvents = sc.topFrictionEvents ?? [];
  const cards: BehavioralCardViewModel[] = [];

  const blocking = frictionEvents.filter(
    (e) => e.severity === "critical" || e.severity === "high",
  );
  if (blocking.length > 0) {
    const totalCount = blocking.reduce((s, e) => s + e.count, 0);
    const items = blocking
      .flatMap((e) => (e.topAffectedPages ?? []).slice(0, 1).map((p) => ({
        url: p.url,
        count: `${p.count} events`,
      })))
      .slice(0, 3);

    cards.push({
      title: "Conversion-blocking issues",
      stat: totalCount,
      statMeta: "friction events recorded this week",
      items,
    });
  }

  const dropoff = frictionEvents.filter(
    (e) => e.eventType === "quick_bounce" || e.eventType === "excessive_back",
  );
  if (dropoff.length > 0) {
    const totalCount = dropoff.reduce((s, e) => s + e.count, 0);
    const items = dropoff
      .flatMap((e) => (e.topAffectedPages ?? []).slice(0, 1).map((p) => ({
        url: p.url,
        count: `${p.count} hits`,
      })))
      .slice(0, 3);

    cards.push({
      title: "High drop-off pages",
      stat: totalCount,
      statMeta: "real users bounced or hit back this week",
      items,
    });
  }

  return { available: cards.length > 0, cards };
}

