/**
 * Norton-Bass Multigenerational Diffusion Model
 *
 * Mathematical engine for forecasting transceiver technology adoption.
 * Based on Norton & Bass (1987, Management Science).
 *
 * Key equations:
 *   Bass: f(t) / [1 - F(t)] = p + q * F(t)
 *   Logistic: S(t) = L / (1 + e^(-k(t - t0)))
 *
 * Parameters:
 *   p = Innovation coefficient (~0.03 for network hardware)
 *   q = Imitation coefficient (~0.3–0.5)
 *   m = Total market potential (addressable port shipments)
 */

/** Technology generation definition */
export interface TechGeneration {
  readonly name: string;          // e.g. "100G QSFP28"
  readonly speedGbps: number;
  readonly formFactor: string;
  readonly introYear: number;     // Year first shipped commercially
  readonly peakYear: number;      // Year of peak shipments (estimated)
  readonly p: number;             // Innovation coefficient
  readonly q: number;             // Imitation coefficient
  readonly m: number;             // Market potential (millions of ports)
  readonly k: number;             // Logistic growth rate
  readonly t0: number;            // Inflection point year
}

/** Hype Cycle phases per Gartner methodology */
export type HypeCyclePhase =
  | "INNOVATION_TRIGGER"
  | "PEAK_OF_INFLATED_EXPECTATIONS"
  | "TROUGH_OF_DISILLUSIONMENT"
  | "SLOPE_OF_ENLIGHTENMENT"
  | "PLATEAU_OF_PRODUCTIVITY"
  | "LEGACY_DECLINE";

/** Result of a hype cycle computation */
export interface HypeCycleResult {
  readonly technology: string;
  readonly phase: HypeCyclePhase;
  readonly phaseLabel: string;
  readonly positionPct: number;       // 0–100 on the hype cycle curve
  readonly adoptionPct: number;       // Current market adoption %
  readonly compositeScore: number;    // 0–100
  readonly forecast: AdoptionForecast;
  readonly metrics: PhaseMetrics;
}

export interface AdoptionForecast {
  readonly currentYear: number;
  readonly yearsToPlateauFromNow: number;
  readonly peakShipmentYear: number;
  readonly cumulativeAdoptionPct: number;
  readonly yearlyAdoptionPct: number;
  readonly fiveYearProjection: ReadonlyArray<{ year: number; adoptionPct: number; phase: HypeCyclePhase }>;
}

export interface PhaseMetrics {
  readonly shipmentShare: number;      // 0–1
  readonly aspDeclineRate: number;     // % per year
  readonly standardsMaturity: number;  // 0–100
  readonly interopLevel: number;       // 0–100
  readonly vendorCount: number;
  readonly vendorTrend: "increasing" | "stable" | "decreasing";
  readonly mediaHypeIndex: number;     // 0–100
}

// ============================================================
// Known technology generations (seed data, March 2026)
// ============================================================
export const TECH_GENERATIONS: ReadonlyArray<TechGeneration> = [
  {
    name: "1G SFP",
    speedGbps: 1,
    formFactor: "SFP",
    introYear: 2001,
    peakYear: 2012,
    p: 0.03, q: 0.38, m: 500, k: 0.45, t0: 2008,
  },
  {
    name: "10G SFP+",
    speedGbps: 10,
    formFactor: "SFP+",
    introYear: 2006,
    peakYear: 2018,
    p: 0.03, q: 0.42, m: 600, k: 0.50, t0: 2014,
  },
  {
    name: "40G QSFP+",
    speedGbps: 40,
    formFactor: "QSFP+",
    introYear: 2010,
    peakYear: 2019,
    p: 0.025, q: 0.35, m: 150, k: 0.40, t0: 2016,
  },
  {
    name: "25G SFP28",
    speedGbps: 25,
    formFactor: "SFP28",
    introYear: 2015,
    peakYear: 2022,
    p: 0.04, q: 0.45, m: 200, k: 0.55, t0: 2019,
  },
  {
    name: "100G QSFP28",
    speedGbps: 100,
    formFactor: "QSFP28",
    introYear: 2014,
    peakYear: 2024,
    p: 0.03, q: 0.40, m: 400, k: 0.48, t0: 2020,
  },
  {
    name: "400G QSFP-DD",
    speedGbps: 400,
    formFactor: "QSFP-DD",
    introYear: 2020,
    peakYear: 2027,
    p: 0.035, q: 0.50, m: 300, k: 0.52, t0: 2025,
  },
  {
    name: "800G OSFP",
    speedGbps: 800,
    formFactor: "OSFP",
    introYear: 2023,
    peakYear: 2029,
    p: 0.04, q: 0.55, m: 250, k: 0.55, t0: 2027,
  },
  {
    name: "1.6T OSFP-XD",
    speedGbps: 1600,
    formFactor: "OSFP-XD",
    introYear: 2025,
    peakYear: 2032,
    p: 0.03, q: 0.45, m: 180, k: 0.48, t0: 2030,
  },
];

// Special tech entries (not speed-generational)
export const SPECIAL_TECHS: ReadonlyArray<TechGeneration> = [
  {
    name: "CPO",
    speedGbps: 1600,
    formFactor: "CPO",
    introYear: 2024,
    peakYear: 2033,
    p: 0.02, q: 0.30, m: 50, k: 0.35, t0: 2031,
  },
  {
    name: "LPO",
    speedGbps: 800,
    formFactor: "LPO",
    introYear: 2024,
    peakYear: 2029,
    p: 0.035, q: 0.48, m: 100, k: 0.50, t0: 2027,
  },
  {
    name: "400ZR Coherent",
    speedGbps: 400,
    formFactor: "QSFP-DD",
    introYear: 2021,
    peakYear: 2026,
    p: 0.04, q: 0.50, m: 80, k: 0.55, t0: 2024,
  },
];

// ============================================================
// Core Bass Diffusion Functions
// ============================================================

/**
 * Bass diffusion: cumulative adoption F(t)
 *
 * F(t) = (1 - e^(-(p+q)*t)) / (1 + (q/p)*e^(-(p+q)*t))
 */
export function bassCumulativeAdoption(
  t: number,
  p: number,
  q: number,
): number {
  const pq = p + q;
  const exp = Math.exp(-pq * t);
  return (1 - exp) / (1 + (q / p) * exp);
}

/**
 * Bass diffusion: instantaneous adoption rate f(t)
 *
 * f(t) = ((p+q)^2 / q) * (e^(-(p+q)*t) / (1 + (q/p)*e^(-(p+q)*t))^2)
 */
export function bassAdoptionRate(
  t: number,
  p: number,
  q: number,
): number {
  const pq = p + q;
  const exp = Math.exp(-pq * t);
  const denominator = 1 + (q / p) * exp;
  return (pq * pq / q) * (exp / (denominator * denominator));
}

/**
 * Logistic adoption curve: S(t) = L / (1 + e^(-k*(t - t0)))
 */
export function logisticAdoption(
  year: number,
  L: number,
  k: number,
  t0: number,
): number {
  return L / (1 + Math.exp(-k * (year - t0)));
}

// ============================================================
// Phase Classification Engine
// ============================================================

export function classifyPhase(metrics: PhaseMetrics): HypeCyclePhase {
  const { shipmentShare, aspDeclineRate, standardsMaturity, vendorTrend, mediaHypeIndex } = metrics;

  if (shipmentShare < 0.01 && standardsMaturity < 30) {
    return "INNOVATION_TRIGGER";
  }

  if (shipmentShare < 0.05 && mediaHypeIndex > 70 && vendorTrend === "increasing") {
    return "PEAK_OF_INFLATED_EXPECTATIONS";
  }

  if (aspDeclineRate > 30 && vendorTrend === "decreasing" && mediaHypeIndex < 40) {
    return "TROUGH_OF_DISILLUSIONMENT";
  }

  if (
    shipmentShare >= 0.05 && shipmentShare <= 0.30 &&
    aspDeclineRate >= 10 && aspDeclineRate <= 25 &&
    (vendorTrend === "stable" || vendorTrend === "increasing")
  ) {
    return "SLOPE_OF_ENLIGHTENMENT";
  }

  if (shipmentShare > 0.30 && aspDeclineRate < 10) {
    return "PLATEAU_OF_PRODUCTIVITY";
  }

  // Fallback: use composite score approach
  return compositePhaseClassification(metrics);
}

/**
 * Composite score phase classification
 *
 * Phase_Score = 0.30 * Normalize(PortShipment_share)
 *             + 0.20 * Normalize(ASP_decline_rate)
 *             + 0.15 * Normalize(Standards_maturity)
 *             + 0.15 * Normalize(InteropValidation_level)
 *             + 0.10 * Normalize(VendorCount_trajectory)
 *             + 0.10 * Normalize(MediaSentiment_score)
 */
function compositePhaseClassification(metrics: PhaseMetrics): HypeCyclePhase {
  const score =
    0.30 * normalize(metrics.shipmentShare, 0, 0.5) +
    0.20 * normalize(metrics.aspDeclineRate, 0, 50) +
    0.15 * normalize(metrics.standardsMaturity, 0, 100) +
    0.15 * normalize(metrics.interopLevel, 0, 100) +
    0.10 * (metrics.vendorTrend === "increasing" ? 0.3 : metrics.vendorTrend === "stable" ? 0.6 : 0.9) +
    0.10 * normalize(100 - metrics.mediaHypeIndex, 0, 100);

  if (score < 0.15) return "INNOVATION_TRIGGER";
  if (score < 0.30) return "PEAK_OF_INFLATED_EXPECTATIONS";
  if (score < 0.45) return "TROUGH_OF_DISILLUSIONMENT";
  if (score < 0.70) return "SLOPE_OF_ENLIGHTENMENT";
  return "PLATEAU_OF_PRODUCTIVITY";
}

function normalize(value: number, min: number, max: number): number {
  return Math.max(0, Math.min(1, (value - min) / (max - min)));
}

// ============================================================
// Phase position on the hype cycle curve (0–100)
// ============================================================

const PHASE_POSITIONS: Record<HypeCyclePhase, [number, number]> = {
  INNOVATION_TRIGGER: [0, 15],
  PEAK_OF_INFLATED_EXPECTATIONS: [15, 30],
  TROUGH_OF_DISILLUSIONMENT: [30, 50],
  SLOPE_OF_ENLIGHTENMENT: [50, 80],
  PLATEAU_OF_PRODUCTIVITY: [80, 95],
  LEGACY_DECLINE: [95, 100],
};

const PHASE_LABELS: Record<HypeCyclePhase, string> = {
  INNOVATION_TRIGGER: "Innovation Trigger",
  PEAK_OF_INFLATED_EXPECTATIONS: "Peak of Inflated Expectations",
  TROUGH_OF_DISILLUSIONMENT: "Trough of Disillusionment",
  SLOPE_OF_ENLIGHTENMENT: "Slope of Enlightenment",
  PLATEAU_OF_PRODUCTIVITY: "Plateau of Productivity",
  LEGACY_DECLINE: "Legacy / Decline",
};

// ============================================================
// Main computation: compute hype cycle for a technology
// ============================================================

export function computeHypeCycle(
  tech: TechGeneration,
  currentYear: number = new Date().getFullYear(),
  overrideMetrics?: Partial<PhaseMetrics>,
): HypeCycleResult {
  // Time since introduction
  const t = currentYear - tech.introYear;
  const tNorm = Math.max(0, t);

  // Bass diffusion adoption
  const cumulativeAdoption = bassCumulativeAdoption(tNorm, tech.p, tech.q);
  const adoptionRate = bassAdoptionRate(tNorm, tech.p, tech.q);

  // Logistic adoption (port shipments)
  const logisticShipments = logisticAdoption(currentYear, tech.m, tech.k, tech.t0);
  const shipmentShare = logisticShipments / 1000; // Normalize to 0–1 range (1000M total market)

  // Estimate metrics from model
  const yearsAfterIntro = currentYear - tech.introYear;
  const yearsToPeak = tech.peakYear - tech.introYear;
  const progressRatio = yearsAfterIntro / yearsToPeak;

  const metrics: PhaseMetrics = {
    shipmentShare: overrideMetrics?.shipmentShare ?? Math.min(0.5, shipmentShare),
    aspDeclineRate: overrideMetrics?.aspDeclineRate ?? estimateAspDecline(progressRatio),
    standardsMaturity: overrideMetrics?.standardsMaturity ?? estimateStandardsMaturity(progressRatio),
    interopLevel: overrideMetrics?.interopLevel ?? estimateInteropLevel(progressRatio),
    vendorCount: overrideMetrics?.vendorCount ?? estimateVendorCount(progressRatio),
    vendorTrend: overrideMetrics?.vendorTrend ?? estimateVendorTrend(progressRatio),
    mediaHypeIndex: overrideMetrics?.mediaHypeIndex ?? estimateMediaHype(progressRatio),
  };

  const phase = classifyPhase(metrics);

  // Check for legacy/decline
  const finalPhase = (currentYear > tech.peakYear + 8 && metrics.shipmentShare < 0.05)
    ? "LEGACY_DECLINE"
    : phase;

  // Position on curve (0-100)
  const [phaseMin, phaseMax] = PHASE_POSITIONS[finalPhase];
  const intraPhaseProgress = Math.min(1, cumulativeAdoption);
  const positionPct = phaseMin + (phaseMax - phaseMin) * intraPhaseProgress;

  // Composite score
  const compositeScore = Math.round(
    0.30 * normalize(metrics.shipmentShare, 0, 0.5) * 100 +
    0.20 * normalize(metrics.aspDeclineRate, 0, 50) * 100 +
    0.15 * normalize(metrics.standardsMaturity, 0, 100) * 100 +
    0.15 * normalize(metrics.interopLevel, 0, 100) * 100 +
    0.10 * (metrics.vendorTrend === "increasing" ? 30 : metrics.vendorTrend === "stable" ? 60 : 90) +
    0.10 * (100 - metrics.mediaHypeIndex)
  );

  // 5-year forecast
  const fiveYearProjection = Array.from({ length: 5 }, (_, i) => {
    const futureYear = currentYear + i + 1;
    const futureT = futureYear - tech.introYear;
    const futureAdoption = bassCumulativeAdoption(futureT, tech.p, tech.q);
    const futureProgressRatio = (futureYear - tech.introYear) / yearsToPeak;
    const futureMetrics: PhaseMetrics = {
      ...metrics,
      shipmentShare: Math.min(0.5, logisticAdoption(futureYear, tech.m, tech.k, tech.t0) / 1000),
      aspDeclineRate: estimateAspDecline(futureProgressRatio),
      vendorTrend: estimateVendorTrend(futureProgressRatio),
    };
    return {
      year: futureYear,
      adoptionPct: Math.round(futureAdoption * 100),
      phase: (futureYear > tech.peakYear + 8 && futureMetrics.shipmentShare < 0.05)
        ? "LEGACY_DECLINE" as HypeCyclePhase
        : classifyPhase(futureMetrics),
    };
  });

  return {
    technology: tech.name,
    phase: finalPhase,
    phaseLabel: PHASE_LABELS[finalPhase],
    positionPct: Math.round(positionPct),
    adoptionPct: Math.round(cumulativeAdoption * 100),
    compositeScore,
    forecast: {
      currentYear,
      yearsToPlateauFromNow: Math.max(0, tech.peakYear + 3 - currentYear),
      peakShipmentYear: tech.peakYear,
      cumulativeAdoptionPct: Math.round(cumulativeAdoption * 100),
      yearlyAdoptionPct: Math.round(adoptionRate * 100),
      fiveYearProjection,
    },
    metrics,
  };
}

// ============================================================
// Estimation heuristics (from model parameters)
// ============================================================

function estimateAspDecline(progressRatio: number): number {
  // ASP decline accelerates through Trough, stabilizes at Plateau
  if (progressRatio < 0.3) return 5;    // Early: slow decline
  if (progressRatio < 0.6) return 35;   // Peak/Trough: rapid decline
  if (progressRatio < 1.0) return 15;   // Slope: moderate decline
  return 5;                              // Plateau: minimal decline
}

function estimateStandardsMaturity(progressRatio: number): number {
  if (progressRatio < 0.2) return 20;   // Draft standards
  if (progressRatio < 0.5) return 60;   // Published, some revisions
  if (progressRatio < 0.8) return 85;   // Mature standards
  return 95;                             // Fully mature
}

function estimateInteropLevel(progressRatio: number): number {
  if (progressRatio < 0.3) return 25;   // Limited interop testing
  if (progressRatio < 0.6) return 55;   // Growing interop
  if (progressRatio < 0.9) return 80;   // Broad interop
  return 95;                             // Universal interop
}

function estimateVendorCount(progressRatio: number): number {
  if (progressRatio < 0.3) return 5;
  if (progressRatio < 0.6) return 15;
  if (progressRatio < 1.0) return 25;
  return 20;                             // Consolidation
}

function estimateVendorTrend(progressRatio: number): "increasing" | "stable" | "decreasing" {
  if (progressRatio < 0.5) return "increasing";
  if (progressRatio < 1.2) return "stable";
  return "decreasing";
}

function estimateMediaHype(progressRatio: number): number {
  // Hype peaks early (Peak of Inflated Expectations)
  if (progressRatio < 0.15) return 40;  // Innovation: moderate buzz
  if (progressRatio < 0.30) return 85;  // Peak: maximum hype
  if (progressRatio < 0.50) return 25;  // Trough: hype collapse
  if (progressRatio < 0.80) return 50;  // Slope: balanced coverage
  return 30;                             // Plateau: boring = good
}

// ============================================================
// Compute all technologies at once
// ============================================================

export function computeAllHypeCycles(
  currentYear: number = new Date().getFullYear(),
): ReadonlyArray<HypeCycleResult> {
  const allTechs = [...TECH_GENERATIONS, ...SPECIAL_TECHS];
  return allTechs.map((tech) => computeHypeCycle(tech, currentYear));
}

export function findTechnology(query: string): TechGeneration | undefined {
  const q = query.toLowerCase().trim();
  const allTechs = [...TECH_GENERATIONS, ...SPECIAL_TECHS];

  // 1. Exact name match
  const exact = allTechs.find((t) => t.name.toLowerCase() === q);
  if (exact) return exact;

  // 2. Name contains query or query contains name
  const nameMatch = allTechs.find((t) => t.name.toLowerCase().includes(q));
  if (nameMatch) return nameMatch;

  // 3. Fuzzy: match by speed prefix (e.g. "400G", "1.6T", "800G")
  const speedMatch = q.match(/^(\d+(?:\.\d+)?)\s*(g|t)\b/i);
  if (speedMatch) {
    const num = parseFloat(speedMatch[1]);
    const unit = speedMatch[2].toLowerCase();
    const targetGbps = unit === "t" ? num * 1000 : num;
    const found = allTechs.find((t) => t.speedGbps === targetGbps);
    if (found) return found;
  }

  // 4. Match by form factor
  return allTechs.find((t) => q.includes(t.formFactor.toLowerCase()));
}
