import 'server-only'

import { REGIONS } from '../data/regions'
import { environmentFor, resourcesFor } from '../engine/grid'
import { forwardWalk, predictFailure, TIME_MACHINE_OFFSETS_MIN } from '../engine/prediction'
import { buildStrategy, evaluateCandidates, generateCandidates } from '../engine/actions'
import type { Perturbation } from '../engine/telemetry'
import { round } from '../engine/rng'
import { getGridSnapshot, loadRiskWeights } from './grid-service'

export interface TimeMachineFrame {
  ts: number
  offsetMin: number
  risk: number
  riskWithAction: number | null
  loadPct: number
  loadWithActionPct: number | null
  tempC: number
  tempWithActionC: number | null
  voltageKv: number
  ambientC: number
  state: string
  breached: boolean
}

export interface TimeMachineResult {
  assetCode: string
  assetName: string
  assetNameAr: string
  regionCode: string
  capacityMw: number
  ratedTempC: number
  ts: number
  etaMinutes: number | null
  breachReason: string | null
  probability: number
  confidence: number
  frames: TimeMachineFrame[]
  offsets: number[]
  strategyActions: Array<{ key: string; title: string; titleAr: string; magnitudeMw: number }>
}

/**
 * Energy Time Machine (§5).
 *
 * Produces two trajectories over the same horizon — the grid left alone, and the grid
 * with the recommended strategy in force. Both come from the same forward walk the
 * prediction engine uses, so the "what happens if we do nothing" line on screen is
 * literally the line the ETA was read off.
 *
 * The step is coarse (fifteen minutes) because the horizon runs to a full day; the
 * minute-accurate crossing still comes from `predictFailure`, which refines it.
 */
export async function getTimeMachine(
  assetCode: string,
  options: { ts?: number; perturbation?: Perturbation; horizonMin?: number } = {},
): Promise<TimeMachineResult | null> {
  const ts = options.ts ?? Date.now()
  const horizonMin = options.horizonMin ?? 1440
  const stepMin = 15
  const perturbation = options.perturbation ?? {}

  const [snapshot, weights] = await Promise.all([
    getGridSnapshot({ ts, perturbation }),
    loadRiskWeights(),
  ])

  const target = snapshot.assets.find((entry) => entry.profile.code === assetCode)
  if (!target) return null

  const region = REGIONS.find((entry) => entry.code === target.profile.regionCode)!
  const envAt = environmentFor(region, perturbation)

  const prediction = predictFailure(target.profile, ts, envAt, {
    perturbation,
    dnaSimilarity: target.dnaSimilarity,
    neighbourStress: target.neighbourStress,
    weights,
    horizonMin: 180,
    stepMin: 5,
  })

  const walkOptions = {
    perturbation,
    dnaSimilarity: target.dnaSimilarity,
    neighbourStress: target.neighbourStress,
    weights,
    horizonMin,
    stepMin,
  }

  const baseline = forwardWalk(target.profile, ts, envAt, walkOptions)

  // The mitigated trajectory: the strategy the engine would recommend, held in force for
  // the whole horizon.
  const context = {
    profile: target.profile,
    ts,
    envAt,
    perturbation,
    dnaSimilarity: target.dnaSimilarity,
    neighbourStress: target.neighbourStress,
    weights,
    resources: resourcesFor(target.profile, snapshot.assets),
    evaluateAtMin: prediction.etaMinutes ?? 45,
  }
  const candidates = evaluateCandidates(context, generateCandidates(context))
  const strategy = buildStrategy(context, candidates, 30)

  const mitigated =
    strategy.evaluation.actions.length > 0
      ? forwardWalk(target.profile, ts, envAt, {
          ...walkOptions,
          perturbation: { ...perturbation, actions: strategy.evaluation.actions },
        })
      : null

  const frames: TimeMachineFrame[] = baseline.map((frame, index) => {
    const withAction = mitigated?.[index]
    return {
      ts: frame.ts,
      offsetMin: frame.offsetMin,
      risk: frame.risk.score,
      riskWithAction: withAction ? withAction.risk.score : null,
      loadPct: frame.sample.loadPct,
      loadWithActionPct: withAction ? withAction.sample.loadPct : null,
      tempC: frame.sample.tempC,
      tempWithActionC: withAction ? withAction.sample.tempC : null,
      voltageKv: frame.sample.voltageKv,
      ambientC: round(frame.env.tempC, 1),
      state: frame.state,
      breached: frame.breached,
    }
  })

  return {
    assetCode: target.profile.code,
    assetName: assetCode,
    assetNameAr: assetCode,
    regionCode: target.profile.regionCode,
    capacityMw: target.profile.capacityMw,
    ratedTempC: target.profile.ratedTempC,
    ts,
    etaMinutes: prediction.etaMinutes,
    breachReason: prediction.breachReason,
    probability: prediction.probability,
    confidence: prediction.confidence,
    frames,
    offsets: [...TIME_MACHINE_OFFSETS_MIN],
    strategyActions: strategy.chosen.map((action) => ({
      key: action.key,
      title: action.title,
      titleAr: action.titleAr,
      magnitudeMw: action.magnitudeMw,
    })),
  }
}
