import 'server-only'

import { round } from '../engine/rng'
import {
  simulateDispatch,
  sustainableMinutes,
  type BatterySpec,
  type DispatchResult,
} from '../engine/bess'
import { getLiveGrid } from './live-grid-service'

/**
 * The battery dispatch simulator's server side (5.0 §19).
 *
 * It reads the storage units out of the twin, hands their state to the simulator, and
 * returns the trajectory. **It sends nothing.** There is no connector call here, no action
 * write and no control verb — §19 forbids commanding a battery, and the way to keep that
 * true is to have no code that could.
 *
 * State of charge comes from the same estimate the rest of the platform uses, and arrives
 * labelled as an estimate. The twin has no SOC meter to read, and presenting a derived
 * figure as a measurement is exactly what §21 forbids.
 */

export interface BatteryOption {
  code: string
  name: string
  nameAr: string
  ratedPowerMw: number
  socPct: number
  /** Always `estimated` — the platform has no state-of-charge instrument. */
  socOrigin: 'estimated'
  state: string
  isOutage: boolean
}

export interface DispatchView extends DispatchResult {
  batteryName: string
  batteryNameAr: string
  ratedPowerMw: number
  /** The longest this unit could hold the request, independent of the window asked for. */
  maxSustainMin: number
  socOrigin: 'estimated'
}

/** Storage units in one twin, with the state a dispatch would start from. */
export async function listBatteries(options: {
  twinCode?: string
  ts?: number
}): Promise<BatteryOption[]> {
  const payload = await getLiveGrid({ twinCode: options.twinCode, ts: options.ts })
  if (!payload) return []

  return payload.assets
    .filter((asset) => asset.assetTypeKey === 'battery_storage')
    .map((asset) => ({
      code: asset.code,
      name: asset.name,
      nameAr: asset.nameAr,
      ratedPowerMw: round(asset.capacityMw, 2),
      socPct: asset.socPct ?? 50,
      socOrigin: 'estimated' as const,
      state: asset.state,
      isOutage: asset.isOutage,
    }))
}

/**
 * Simulate one dispatch against one unit.
 *
 * Returns null when the asset is not storage rather than coercing it: a "dispatch" from a
 * transformer is not a degraded answer, it is a different question.
 */
export async function simulateBatteryDispatch(input: {
  twinCode?: string
  assetCode: string
  requestMw: number
  durationMin: number
  ts?: number
}): Promise<DispatchView | null> {
  const batteries = await listBatteries({ twinCode: input.twinCode, ts: input.ts })
  const unit = batteries.find((entry) => entry.code === input.assetCode)
  if (!unit) return null

  const spec: BatterySpec = {
    code: unit.code,
    ratedPowerMw: unit.ratedPowerMw,
    // The platform stores no energy rating; the simulator derives one and declares it.
    ratedEnergyMwh: null,
    socPct: unit.socPct,
    roundTripEfficiency: null,
    isOutage: unit.isOutage,
  }

  const result = simulateDispatch({
    battery: spec,
    requestMw: input.requestMw,
    durationMin: input.durationMin,
    // A minute per step up to four hours, then coarser, so a long horizon stays a payload
    // rather than a thousand points nobody plots.
    stepMin: input.durationMin > 240 ? 5 : 1,
  })

  return {
    ...result,
    batteryName: unit.name,
    batteryNameAr: unit.nameAr,
    ratedPowerMw: unit.ratedPowerMw,
    maxSustainMin: input.requestMw > 0 ? sustainableMinutes(spec, input.requestMw) : 0,
    socOrigin: 'estimated',
  }
}
