import 'server-only'

import { prisma } from '../db'
import { simulateCascade, type CascadeResult } from '../engine/cascade'
import type { Perturbation } from '../engine/telemetry'
import { getGridSnapshot } from './grid-service'
import { cascadeStates, loadGridGraph } from './graph-service'

/**
 * Cascade runs (§5) and their persistence.
 *
 * The walk itself is pure and fast enough to run on demand, so pages compute it live.
 * Storing a run is a separate, explicit act — it happens when an operator asks for one,
 * and it exists so the timeline behind a decision can be produced again later exactly as
 * it was presented.
 */

export interface CascadeRunOptions {
  ts?: number
  perturbation?: Perturbation
  horizonMin?: number
  triggerKind?: 'failure' | 'outage' | 'overload'
}

export interface CascadeRun {
  result: CascadeResult
  summary: string
  summaryAr: string
}

function summarise(result: CascadeResult, originName: string): { en: string; ar: string } {
  const propagating = result.steps.filter((step) => step.outcome === 'failed' && step.depth > 0)
  if (propagating.length === 0) {
    return {
      en: `A failure on ${originName} stays contained: adjacent units absorb the transfer without exceeding their own limits.`,
      ar: `العطل على ${originName} يبقى محصورًا: الوحدات المجاورة تستوعب التحويل دون تجاوز حدودها.`,
    }
  }
  const last = propagating.at(-1)!
  return {
    en: `A failure on ${originName} propagates through ${propagating.length} further assets over ${last.offsetMin} minutes, reaching ${result.affectedCustomers.toLocaleString('en-US')} customers and ${result.affectedMw} MW of load.`,
    ar: `العطل على ${originName} ينتشر عبر ${propagating.length} أصول إضافية خلال ${last.offsetMin} دقيقة، ويصل إلى ${result.affectedCustomers.toLocaleString('en-US')} مشترك و${result.affectedMw} ميجاواط من الحمل.`,
  }
}

export async function runCascade(
  originCode: string,
  options: CascadeRunOptions = {},
): Promise<CascadeRun | null> {
  const ts = options.ts ?? Date.now()
  const [snapshot, graph] = await Promise.all([
    getGridSnapshot({ ts, perturbation: options.perturbation }),
    loadGridGraph(),
  ])

  const node = graph.byCode.get(originCode)
  if (!node) return null

  const result = simulateCascade(graph, cascadeStates(snapshot.assets), originCode, {
    horizonMin: options.horizonMin ?? 60,
  })
  const summary = summarise(result, node.name)
  return { result, summary: summary.en, summaryAr: summary.ar }
}

export async function persistCascade(
  run: CascadeRun,
  options: { userId?: string | null; triggerKind?: string; ts?: number } = {},
): Promise<string> {
  const { result } = run
  const asset = await prisma.asset.findUnique({
    where: { code: result.originCode },
    select: { id: true },
  })
  const ts = options.ts ?? Date.now()
  const code = `CSC-${ts.toString(36).toUpperCase()}-${result.originCode}`

  await prisma.cascadeSimulation.create({
    data: {
      code,
      originAssetId: asset?.id ?? null,
      originCode: result.originCode,
      triggerKind: options.triggerKind ?? 'failure',
      horizonMin: result.horizonMin,
      ts: new Date(ts),
      affectedAssets: result.affectedAssets,
      affectedMw: result.affectedMw,
      affectedCustomers: result.affectedCustomers,
      maxDepth: result.maxDepth,
      regionalRisk: result.regionalRisk,
      containedAtMin: result.containedAtMin,
      summary: run.summary,
      summaryAr: run.summaryAr,
      createdById: options.userId ?? null,
      steps: {
        create: result.steps.map((step) => ({
          ordinal: step.ordinal,
          depth: step.depth,
          offsetMin: step.offsetMin,
          assetCode: step.assetCode,
          assetName: step.assetName,
          assetNameAr: step.assetNameAr,
          effect: step.outcome,
          effectAr: step.outcome,
          mechanism: step.mechanism,
          loadPct: step.loadPctAfter,
          probability: step.probability,
          severity: step.severity,
          customers: step.customers,
          capacityMw: step.capacityMw,
        })),
      },
    },
  })

  return code
}

/** Stored cascade runs, newest first, for the history panel. */
export async function listCascadeRuns(limit = 20) {
  return prisma.cascadeSimulation.findMany({
    orderBy: { createdAt: 'desc' },
    take: limit,
    include: { steps: { orderBy: { ordinal: 'asc' } } },
  })
}

export async function getCascadeRun(code: string) {
  return prisma.cascadeSimulation.findUnique({
    where: { code },
    include: { steps: { orderBy: { ordinal: 'asc' } } },
  })
}
