import type { Perturbation } from './telemetry'

/**
 * The named scenarios the Live 3D Grid can be driven with (§94–§98).
 *
 * Each one is a perturbation the platform's existing engines already understand, not a
 * separate simulation. That is what makes them reproducible: the same scenario at the same
 * virtual instant produces the same scene, every time, on any machine — there is no
 * random draw anywhere in this file, and a test enforces that.
 *
 * They are also *coherent*, which is the point of §92. A heat wave raises ambient
 * temperature; the telemetry engine turns that into higher load, higher current, a longer
 * thermal lag and a rising risk score on its own. Nothing here sets a risk number
 * directly — if it did, the demonstration would be a slideshow of numbers rather than a
 * model responding to a condition.
 */

export const SCENARIO_KEYS = [
  'normal',
  'heat_wave',
  'demand_surge',
  'transformer_degradation',
  'renewable_drop',
  'line_overload',
  'battery_support',
  'cascade_event',
] as const

export type ScenarioKey = (typeof SCENARIO_KEYS)[number]

export interface GridScenario {
  key: ScenarioKey
  label: string
  labelAr: string
  /** One sentence on what an operator should watch, not on what the code does. */
  watchFor: string
  watchForAr: string
  perturbation: Perturbation
  /** Where to point the camera, when the scenario has an obvious subject. */
  focusAssetCode: string | null
  /** Rough minutes before the effect is fully visible, for the demo pacing. */
  developsOverMin: number
  /**
   * Megawatts of storage discharge this scenario calls for, applied to whatever storage
   * the twin in view holds. Zero on every scenario that does not dispatch storage.
   */
  dispatchStorageMw?: number
}

const RIYADH = 'RYD'

export const GRID_SCENARIOS: Record<ScenarioKey, GridScenario> = {
  normal: {
    key: 'normal',
    label: 'Normal operation',
    labelAr: 'تشغيل طبيعي',
    watchFor: 'Every asset inside its envelope and power moving along its usual paths.',
    watchForAr: 'كل أصل داخل نطاقه والطاقة تسير في مساراتها المعتادة.',
    perturbation: {},
    focusAssetCode: null,
    developsOverMin: 0,
  },

  heat_wave: {
    key: 'heat_wave',
    label: 'Heat wave',
    labelAr: 'موجة حر',
    watchFor:
      'Ambient temperature rises first. Load, current and oil temperature follow — with the lag that makes early detection possible.',
    watchForAr:
      'ترتفع حرارة الجو أولًا. ثم يتبعها الحمل والتيار وحرارة الزيت — بالتأخر الذي يجعل الاكتشاف المبكر ممكنًا.',
    // Only the ambient is set. Everything downstream is the model's own response.
    perturbation: { tempDeltaC: 6, regionCode: RIYADH },
    focusAssetCode: 'T-108',
    developsOverMin: 45,
  },

  demand_surge: {
    key: 'demand_surge',
    label: 'Demand surge',
    labelAr: 'ارتفاع مفاجئ في الطلب',
    watchFor: 'Feeder utilisation climbing and the first paths to reach ninety per cent.',
    watchForAr: 'ارتفاع استغلال المغذيات وأول المسارات التي تبلغ تسعين بالمئة.',
    perturbation: { loadDeltaPct: 28, regionCode: RIYADH },
    focusAssetCode: null,
    developsOverMin: 20,
  },

  transformer_degradation: {
    key: 'transformer_degradation',
    label: 'Transformer under stress',
    labelAr: 'محول تحت إجهاد',
    watchFor:
      'One unit carrying more than its neighbours while the ambient is already high — the combination the Failure DNA library recognises.',
    watchForAr:
      'وحدة تحمل أكثر من جاراتها بينما الجو مرتفع أصلًا — وهي التركيبة التي تتعرف عليها مكتبة الحمض النووي للأعطال.',
    perturbation: { tempDeltaC: 4.5, loadDeltaPct: 16, regionCode: RIYADH },
    focusAssetCode: 'T-108',
    developsOverMin: 35,
  },

  renewable_drop: {
    key: 'renewable_drop',
    label: 'Renewable drop',
    labelAr: 'انخفاض الطاقة المتجددة',
    watchFor:
      'Solar and wind falling away, and the conventional plant that has to pick the difference up.',
    watchForAr: 'تراجع الشمسية والرياح، والمحطات التقليدية التي تعوض الفارق.',
    perturbation: { solarDeltaPct: -55, windDeltaPct: -35 },
    focusAssetCode: null,
    developsOverMin: 25,
  },

  line_overload: {
    key: 'line_overload',
    label: 'Line overload',
    labelAr: 'تحميل زائد على خط',
    watchFor: 'A single path past its rating, and where the flow goes instead.',
    watchForAr: 'مسار واحد يتجاوز تصنيفه، وإلى أين تذهب الطاقة بدلًا عنه.',
    perturbation: { loadDeltaPct: 34, tempDeltaC: 3, regionCode: RIYADH },
    focusAssetCode: null,
    developsOverMin: 15,
  },

  battery_support: {
    key: 'battery_support',
    label: 'Storage supporting the grid',
    labelAr: 'التخزين يدعم الشبكة',
    watchFor:
      'Storage discharging, and the feeder into it reversing — power leaving the battery towards the network.',
    watchForAr: 'التخزين يفرّغ، والمغذي المتصل به ينعكس — الطاقة تغادر البطارية نحو الشبكة.',
    // Storage discharge is expressed as an applied action on the asset carrying the
    // relief, which is the channel the telemetry engine already honours — the battery's
    // own loading then goes positive and the flow solver reverses the feeder into it.
    // The target is resolved at request time from the scene, so this scenario works on
    // whatever storage the twin in view actually contains.
    perturbation: { loadDeltaPct: 18, regionCode: RIYADH },
    dispatchStorageMw: 45,
    focusAssetCode: null,
    developsOverMin: 10,
  },

  cascade_event: {
    key: 'cascade_event',
    label: 'Cascade conditions',
    labelAr: 'ظروف تتالٍ',
    watchFor:
      'A unit out of service while demand and ambient are both high — the conditions under which one failure reaches the next.',
    watchForAr:
      'وحدة خارج الخدمة بينما الطلب والحرارة مرتفعان — وهي الظروف التي يصل فيها عطل إلى الذي يليه.',
    perturbation: { tempDeltaC: 5, loadDeltaPct: 24, regionCode: RIYADH, batteryOutage: true },
    focusAssetCode: 'T-108',
    developsOverMin: 40,
  },
}

export function scenarioFor(key: string | null | undefined): GridScenario {
  return GRID_SCENARIOS[(key ?? 'normal') as ScenarioKey] ?? GRID_SCENARIOS.normal
}

/**
 * Pick a scenario for the "surprise me" control (§98).
 *
 * Deliberately not random. The index is derived from whatever the caller passes as a
 * seed — a click counter, a timestamp taken at the edge — so the same seed always gives
 * the same scenario, and the control cycles through coherent set-pieces rather than
 * producing noise.
 */
export function surpriseScenario(seed: number): GridScenario {
  const interesting = SCENARIO_KEYS.filter((key) => key !== 'normal')
  const index = Math.abs(Math.trunc(seed)) % interesting.length
  return GRID_SCENARIOS[interesting[index]]
}
