import type { TestMetrics } from "./sitespeed";

export type Grade = "A" | "B" | "C" | "D" | "E" | "F";

/**
 * Lighthouse log-normal scoring curves.
 * Each metric has median (p50) and p10 values defining the curve.
 */
const METRIC_CURVES: Record<string, { median: number; p10: number }> = {
  fcp: { median: 3000, p10: 1800 },
  si: { median: 5800, p10: 3387 },
  lcp: { median: 4000, p10: 2500 },
  tbt: { median: 600, p10: 200 },
  cls: { median: 0.25, p10: 0.1 },
  fid: { median: 300, p10: 100 },
};

/**
 * GTmetrix-style weights for performance score.
 */
const WEIGHTS: Record<string, number> = {
  fcp: 0.1,
  si: 0.1,
  lcp: 0.25,
  tbt: 0.3,
  cls: 0.15,
  fid: 0.1,
};

function erf(x: number): number {
  const sign = x >= 0 ? 1 : -1;
  const a = Math.abs(x);
  const t = 1 / (1 + 0.3275911 * a);
  const poly =
    t *
    (0.254829592 + t * (-0.284496736 + t * (1.421413741 + t * (-1.453152027 + t * 1.061405429))));
  return sign * (1 - poly * Math.exp(-a * a));
}

function scoreMetric(value: number, median: number, p10: number): number {
  if (value <= 0) return 1;
  const logMedian = Math.log(median);
  const logP10 = Math.log(p10);
  const sigma = (logMedian - logP10) / 0.9061;
  const z = (logMedian - Math.log(value)) / (sigma * Math.SQRT2);
  return Math.max(0, Math.min(1, 0.5 * (1 + erf(z))));
}

export function calculatePerformanceScore(metrics: TestMetrics): number {
  const values: Record<string, number | null> = {
    fcp: metrics.firstContentfulPaint,
    si: metrics.speedIndex,
    lcp: metrics.largestContentfulPaint,
    tbt: metrics.totalBlockingTime,
    cls: metrics.cumulativeLayoutShift,
    fid: metrics.firstInputDelay,
  };

  let totalWeight = 0;
  let weightedScore = 0;

  for (const [key, weight] of Object.entries(WEIGHTS)) {
    const value = values[key];
    const curve = METRIC_CURVES[key];
    if (value == null || !curve) continue;

    weightedScore += scoreMetric(value, curve.median, curve.p10) * weight;
    totalWeight += weight;
  }

  if (totalWeight === 0) return 0;
  return Math.round((weightedScore / totalWeight) * 100);
}

export function scoreToGrade(score: number): Grade {
  if (score >= 90) return "A";
  if (score >= 80) return "B";
  if (score >= 70) return "C";
  if (score >= 50) return "D";
  if (score >= 25) return "E";
  return "F";
}

export function getGradeColor(grade: Grade): string {
  switch (grade) {
    case "A":
      return "text-perf-good";
    case "B":
      return "text-perf-good";
    case "C":
      return "text-perf-warning";
    case "D":
      return "text-perf-warning";
    case "E":
    case "F":
      return "text-perf-danger";
  }
}

export function getGradeBg(grade: Grade): string {
  switch (grade) {
    case "A":
      return "bg-perf-good";
    case "B":
      return "bg-perf-good";
    case "C":
      return "bg-perf-warning";
    case "D":
      return "bg-perf-warning";
    case "E":
    case "F":
      return "bg-perf-danger";
  }
}

export type CwvStatus = "good" | "needs-improvement" | "poor";

export function getCwvStatus(metric: string, value: number): CwvStatus {
  const thresholds: Record<string, { good: number; ni: number }> = {
    lcp: { good: 2500, ni: 4000 },
    fcp: { good: 1800, ni: 3000 },
    cls: { good: 0.1, ni: 0.25 },
    tbt: { good: 200, ni: 600 },
    ttfb: { good: 800, ni: 1800 },
    fid: { good: 100, ni: 300 },
    si: { good: 3400, ni: 5800 },
  };
  const t = thresholds[metric];
  if (!t) return "good";
  if (value <= t.good) return "good";
  if (value <= t.ni) return "needs-improvement";
  return "poor";
}

export function getCwvStatusColor(status: CwvStatus): string {
  switch (status) {
    case "good":
      return "text-perf-good bg-perf-good/10 border-perf-good/20";
    case "needs-improvement":
      return "text-perf-warning bg-perf-warning/10 border-perf-warning/20";
    case "poor":
      return "text-perf-danger bg-perf-danger/10 border-perf-danger/20";
  }
}

export function getCwvStatusLabel(status: CwvStatus): string {
  switch (status) {
    case "good":
      return "Good";
    case "needs-improvement":
      return "Needs Improvement";
    case "poor":
      return "Poor";
  }
}

export function formatMetricValue(metric: string, value: number): string {
  if (metric === "cls") return value.toFixed(3);
  if (value >= 1000) return `${(value / 1000).toFixed(2)} s`;
  return `${Math.round(value)} ms`;
}

export function formatBytes(bytes: number): string {
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
}
