export type CapacityMode = 'capacity' | 'demand'
export type ResidualDemand = 'strong' | 'base' | 'weak'

export interface ScenarioInputs {
  capacityMode: CapacityMode
  tpsUplift: number
  cloudCapture: number
  utilization: number
  gpuLife: number
  gpuHourPrice: number
  tokenPrice: number
  residualDemand: ResidualDemand
}

export interface ScenarioResult {
  activeMW: number
  weightedTpsPerMW: number
  tokensQ: number
  cloudRevenueM: number
  cashOpexM: number
  depreciationM: number
  ebitM: number
  ebitMargin: number
  grossGpuCapexM: number
  grossRoce: number
  labRevenueM: number
  costPerMillionTokens: number
  realizedGpuHourPrice: number
  physicalCapexB: number
  gpuBuildCapexB: number
  allInBuildCapexB: number
  dcRentalRevenueM: number
}

export const SHEET_URL =
  'https://docs.google.com/spreadsheets/d/12Gd0fFg_-QJ4rWND9-SVhesH4Qphv_UaA4rMrP4fCS8/edit'

export const BASE_2030 = {
  activeMW: 1000,
  weightedTpsPerMW: 1_146_106.551160705,
  tokensQ: 36.14361619740399,
  cloudRevenueM: 12_709.550877476982,
  cashOpexM: 3_270.833333333333,
  depreciationM: 6_052.762058336157,
  grossGpuCapexM: 30_263.810291680784,
  labRevenueM: 99_394.94454286099,
  utilization: 0.7358498555345222,
  realizedGpuHourPrice: 4.222991138630559,
  physicalCapexPerMWM: 12.5,
  gpuBuildCapexPerMWM: 23.333333333333332,
  dcRentPerMWM: 2.15,
  tokenPrice: 2.75,
}

export const DEFAULT_INPUTS: ScenarioInputs = {
  capacityMode: 'capacity',
  tpsUplift: 0.25,
  cloudCapture: 0.25,
  utilization: BASE_2030.utilization,
  gpuLife: 5,
  gpuHourPrice: BASE_2030.realizedGpuHourPrice,
  tokenPrice: BASE_2030.tokenPrice,
  residualDemand: 'strong',
}

const residualFactor: Record<ResidualDemand, number> = {
  strong: 1,
  base: 0.94,
  weak: 0.86,
}

export function calculateScenario(inputs: ScenarioInputs): ScenarioResult {
  const capacityDivisor =
    inputs.capacityMode === 'demand' ? 1 + inputs.tpsUplift : 1
  const utilizationFactor = inputs.utilization / BASE_2030.utilization
  const priceFactor = inputs.gpuHourPrice / BASE_2030.realizedGpuHourPrice
  const demandFactor = residualFactor[inputs.residualDemand]
  const performancePriceFactor = 1 + inputs.tpsUplift * inputs.cloudCapture

  const activeMW = BASE_2030.activeMW / capacityDivisor
  const weightedTpsPerMW =
    BASE_2030.weightedTpsPerMW * (1 + inputs.tpsUplift)
  const tokensQ =
    (BASE_2030.tokensQ *
      (1 + inputs.tpsUplift) *
      utilizationFactor *
      demandFactor) /
    capacityDivisor
  const cloudRevenueM =
    (BASE_2030.cloudRevenueM *
      utilizationFactor *
      priceFactor *
      performancePriceFactor *
      demandFactor) /
    capacityDivisor
  const cashOpexM = BASE_2030.cashOpexM / capacityDivisor
  const depreciationM =
    (BASE_2030.depreciationM * (5 / inputs.gpuLife)) / capacityDivisor
  const ebitM = cloudRevenueM - cashOpexM - depreciationM
  const grossGpuCapexM = BASE_2030.grossGpuCapexM / capacityDivisor
  const labRevenueM = tokensQ * inputs.tokenPrice * 1000
  const physicalCapexB =
    (activeMW * BASE_2030.physicalCapexPerMWM) / 1000
  const gpuBuildCapexB =
    (activeMW * BASE_2030.gpuBuildCapexPerMWM) / 1000

  return {
    activeMW,
    weightedTpsPerMW,
    tokensQ,
    cloudRevenueM,
    cashOpexM,
    depreciationM,
    ebitM,
    ebitMargin: cloudRevenueM === 0 ? 0 : ebitM / cloudRevenueM,
    grossGpuCapexM,
    grossRoce: grossGpuCapexM === 0 ? 0 : ebitM / grossGpuCapexM,
    labRevenueM,
    costPerMillionTokens:
      tokensQ === 0 ? 0 : (cashOpexM + depreciationM) / tokensQ / 1000,
    realizedGpuHourPrice:
      inputs.gpuHourPrice * performancePriceFactor,
    physicalCapexB,
    gpuBuildCapexB,
    allInBuildCapexB: physicalCapexB + gpuBuildCapexB,
    dcRentalRevenueM: activeMW * BASE_2030.dcRentPerMWM,
  }
}

export const BASE_ANNUAL = [
  { year: 2026, activeMW: 24.8829, cloudB: 0.2579, labB: 0.7769, ebitB: 0.0604 },
  { year: 2027, activeMW: 64.8849, cloudB: 0.6884, labB: 2.5275, ebitB: 0.1584 },
  { year: 2028, activeMW: 154.852, cloudB: 1.7102, labB: 7.974, ebitB: 0.3963 },
  { year: 2029, activeMW: 411.0292, cloudB: 4.8823, labB: 29.6436, ebitB: 1.2244 },
  { year: 2030, activeMW: 1000, cloudB: 12.7096, labB: 99.3949, ebitB: 3.386 },
  { year: 2031, activeMW: 1200, cloudB: 14.483, labB: 129.8087, ebitB: 3.25 },
  { year: 2032, activeMW: 1425.1171, cloudB: 16.445, labB: 176.9374, ebitB: 2.8259 },
]

export function calculateAnnualScenario(inputs: ScenarioInputs) {
  const capacityDivisor =
    inputs.capacityMode === 'demand' ? 1 + inputs.tpsUplift : 1
  const utilizationFactor = inputs.utilization / BASE_2030.utilization
  const priceFactor = inputs.gpuHourPrice / BASE_2030.realizedGpuHourPrice
  const demandFactor = residualFactor[inputs.residualDemand]
  const performancePriceFactor = 1 + inputs.tpsUplift * inputs.cloudCapture
  const depreciationScale = 5 / inputs.gpuLife

  return BASE_ANNUAL.map((year) => {
    const cloudB =
      (year.cloudB *
        utilizationFactor *
        priceFactor *
        performancePriceFactor *
        demandFactor) /
      capacityDivisor
    const baselineCostsB = year.cloudB - year.ebitB
    const adjustedCostsB =
      (baselineCostsB * (0.35 + 0.65 * depreciationScale)) /
      capacityDivisor

    return {
      ...year,
      activeMW: year.activeMW / capacityDivisor,
      cloudB,
      labB:
        (year.labB *
          (1 + inputs.tpsUplift) *
          utilizationFactor *
          demandFactor *
          (inputs.tokenPrice / BASE_2030.tokenPrice)) /
        capacityDivisor,
      ebitB: cloudB - adjustedCostsB,
    }
  })
}

export const COHORTS = [
  { age: 0, state: 'Newest / premium', premium: 0.7, mainstream: 0.25, residual: 0.05, price: 0.88, utilization: 0.72, tps: 1 },
  { age: 1, state: 'Current / prior generation', premium: 0.45, mainstream: 0.45, residual: 0.1, price: 0.78, utilization: 0.76, tps: 0.95 },
  { age: 2, state: 'Mainstream inference', premium: 0.2, mainstream: 0.55, residual: 0.25, price: 0.65, utilization: 0.78, tps: 0.85 },
  { age: 3, state: 'Value inference', premium: 0.05, mainstream: 0.55, residual: 0.4, price: 0.55, utilization: 0.75, tps: 0.7 },
  { age: 4, state: 'Residual inference', premium: 0, mainstream: 0.4, residual: 0.6, price: 0.47, utilization: 0.68, tps: 0.55 },
  { age: 5, state: 'Fully depreciated residual', premium: 0, mainstream: 0.25, residual: 0.75, price: 0.43, utilization: 0.6, tps: 0.4 },
]

export function formatBillions(valueM: number, decimals = 1) {
  return `$${(valueM / 1000).toFixed(decimals)}B`
}

export function formatNumber(value: number, decimals = 0) {
  return new Intl.NumberFormat('en-US', {
    maximumFractionDigits: decimals,
    minimumFractionDigits: decimals,
  }).format(value)
}

export function formatPercent(value: number, decimals = 1) {
  return `${(value * 100).toFixed(decimals)}%`
}
