import { getBenchmarkForIndustry } from "../audit/data/industry-benchmarks";
import type { PerPageSpeedAnalysis, PageSpeedInsights, WebVitalGrade } from "./types";

// Speed score thresholds by overall PSI performance score (0-1)
const TIER_THRESHOLDS = {
  excellent: 0.9,
  good: 0.7,
  needs_improvement: 0.5,
} as const;

// Monthly traffic loss estimates by score tier -- fraction of estimated traffic
const TRAFFIC_LOSS_FRACTION: Record<PageSpeedInsights["overallScoreTier"], number> = {
  excellent: 0,
  good: 0.03,
  needs_improvement: 0.12,
  poor: 0.22,
};

function tierFromScore(score: number): PageSpeedInsights["overallScoreTier"] {
  if (score >= TIER_THRESHOLDS.excellent) return "excellent";
  if (score >= TIER_THRESHOLDS.good) return "good";
  if (score >= TIER_THRESHOLDS.needs_improvement) return "needs_improvement";
  return "poor";
}

function aggregateCwvGrade(
  analyses: PerPageSpeedAnalysis[],
  metric: "LCP" | "INP" | "CLS" | "FCP" | "TTFB",
): WebVitalGrade | null {
  const grades: WebVitalGrade[] = [];
  for (const analysis of analyses) {
    const vital = analysis.coreWebVitals.find((v) => v.metric === metric);
    const grade = vital?.fieldGrade ?? vital?.labGrade;
    if (grade) grades.push(grade);
  }
  if (grades.length === 0) return null;
  if (grades.some((g) => g === "poor")) return "poor";
  if (grades.some((g) => g === "needs_improvement")) return "needs_improvement";
  return "good";
}

export function scorePageSpeed(
  perPageAnalyses: PerPageSpeedAnalysis[],
  vertical: string,
  start: number,
): PageSpeedInsights {
  if (perPageAnalyses.length === 0) {
    return {
      hasInsights: false,
      windowDate: new Date().toISOString(),
      pagesAnalyzed: 0,
      overallScoreTier: "poor",
      estimatedMonthlyTrafficLoss: 0,
      estimatedMonthlyRevenueLoss: 0,
      perPageAnalyses: [],
      aggregateCwvGrades: { LCP: null, INP: null, CLS: null, FCP: null, TTFB: null },
      topRenderBlockingResources: [],
      topImageOpportunities: [],
      patentRelevance:
        "Page speed directly affects crawl budget allocation and conversion probability.",
      costMetrics: { pagesAnalyzed: 0, runtimeSeconds: Math.round((Date.now() - start) / 1000), estimatedCostUsd: 0 },
    };
  }

  const avgScore =
    perPageAnalyses.reduce((sum, p) => sum + p.overallScore, 0) / perPageAnalyses.length;

  const tier = tierFromScore(avgScore / 100);
  const benchmark = getBenchmarkForIndustry(vertical);

  // Rough estimated monthly sessions = benchmark conversion rate denominator proxy
  const estimatedMonthlySessions = Math.round(
    (benchmark.avgCustomerValue * 12) / Math.max(benchmark.avgConversionRate, 0.01),
  );
  const trafficLossFraction = TRAFFIC_LOSS_FRACTION[tier];
  const estimatedMonthlyTrafficLoss = Math.round(
    estimatedMonthlySessions * trafficLossFraction,
  );
  const estimatedMonthlyRevenueLoss = Math.round(
    estimatedMonthlyTrafficLoss * benchmark.avgConversionRate * benchmark.avgCustomerValue,
  );

  // Aggregate CWV grades across pages
  const aggregateCwvGrades: PageSpeedInsights["aggregateCwvGrades"] = {
    LCP: aggregateCwvGrade(perPageAnalyses, "LCP"),
    INP: aggregateCwvGrade(perPageAnalyses, "INP"),
    CLS: aggregateCwvGrade(perPageAnalyses, "CLS"),
    FCP: aggregateCwvGrade(perPageAnalyses, "FCP"),
    TTFB: aggregateCwvGrade(perPageAnalyses, "TTFB"),
  };

  // Deduplicate and top-rank render-blocking resources
  const rbResourceMap = new Map<string, number>();
  for (const p of perPageAnalyses) {
    for (const r of p.renderBlockingResources) {
      rbResourceMap.set(r.url, Math.max(rbResourceMap.get(r.url) ?? 0, r.wastedMs));
    }
  }
  const topRenderBlockingResources = Array.from(rbResourceMap.entries())
    .sort((a, b) => b[1] - a[1])
    .slice(0, 10)
    .map(([url, wastedMs]) => ({
      url,
      wastedMs,
      resourceType: /\.js(\?|$)/i.test(url)
        ? ("script" as const)
        : /\.css(\?|$)/i.test(url)
          ? ("stylesheet" as const)
          : ("other" as const),
    }));

  // Deduplicate image opportunities
  const imgOppMap = new Map<string, number>();
  for (const p of perPageAnalyses) {
    for (const o of p.imageOpportunities) {
      imgOppMap.set(o.url, Math.max(imgOppMap.get(o.url) ?? 0, o.wastedBytes));
    }
  }
  const topImageOpportunities = Array.from(imgOppMap.entries())
    .sort((a, b) => b[1] - a[1])
    .slice(0, 5)
    .map(([url, wastedBytes]) => ({
      url,
      wastedBytes,
      wastedKb: Math.round(wastedBytes / 1024),
      issue: "Image optimization opportunity identified",
    }));

  return {
    hasInsights: true,
    windowDate: new Date().toISOString(),
    pagesAnalyzed: perPageAnalyses.length,
    overallScoreTier: tier,
    estimatedMonthlyTrafficLoss,
    estimatedMonthlyRevenueLoss,
    perPageAnalyses,
    aggregateCwvGrades,
    topRenderBlockingResources,
    topImageOpportunities,
    patentRelevance:
      "Page speed directly affects crawl budget allocation and conversion probability.",
    costMetrics: {
      pagesAnalyzed: perPageAnalyses.length,
      runtimeSeconds: Math.round((Date.now() - start) / 1000),
      estimatedCostUsd: 0,
    },
  };
}
