import 'server-only'

import { cache } from 'react'

import { prisma } from '../db'
import { getGridSnapshot } from './grid-service'
import type { MapAsset, MapRegion } from '@/components/map/national-map'

/**
 * Geometry and identity for the map, joined to live risk.
 *
 * Split from `getGridSnapshot` because the two halves change at completely different
 * rates: coordinates and names are effectively static and are cached per request, while
 * risk is recomputed on every tick.
 */
const loadGeometry = cache(async () => {
  const [assets, regions] = await Promise.all([
    prisma.asset.findMany({
      select: {
        code: true,
        name: true,
        nameAr: true,
        lat: true,
        lon: true,
        capacityMw: true,
        assetType: { select: { key: true } },
        region: { select: { code: true } },
      },
    }),
    prisma.region.findMany({
      select: { code: true, name: true, nameAr: true, lat: true, lon: true },
    }),
  ])
  return { assets, regions }
})

export async function getMapData(options: { ts?: number; includeForecast?: boolean } = {}): Promise<{
  assets: MapAsset[]
  regions: MapRegion[]
}> {
  const ts = options.ts ?? Date.now()

  // The prediction layer needs the same fleet an hour out. It is one extra snapshot, and
  // it is what makes "failure prediction" a layer with something in it rather than a
  // relabelling of the current risk.
  const [geometry, snapshot, forecast] = await Promise.all([
    loadGeometry(),
    getGridSnapshot({ ...options, ts }),
    options.includeForecast === false
      ? Promise.resolve(null)
      : getGridSnapshot({ ...options, ts: ts + 60 * 60_000 }),
  ])

  const byCode = new Map(snapshot.assets.map((entry) => [entry.profile.code, entry]))
  const forecastByCode = new Map(
    (forecast?.assets ?? []).map((entry) => [entry.profile.code, entry.risk.score]),
  )
  const regionByCode = new Map(snapshot.regions.map((entry) => [entry.code, entry]))

  return {
    assets: geometry.assets.map((asset) => {
      const live = byCode.get(asset.code)
      return {
        code: asset.code,
        name: asset.name,
        nameAr: asset.nameAr,
        typeKey: asset.assetType.key,
        regionCode: asset.region.code,
        lat: asset.lat,
        lon: asset.lon,
        capacityMw: asset.capacityMw,
        risk: live?.risk.score ?? 0,
        state: live?.state ?? 'normal',
        loadPct: live?.sample.loadPct ?? 0,
        tempC: live?.sample.tempC ?? 0,
        predictedRisk: forecastByCode.get(asset.code),
        maintenanceDebtDays: live ? Math.round(live.maintenanceDebt) : undefined,
      }
    }),
    regions: geometry.regions.map((region) => {
      const live = regionByCode.get(region.code)
      return {
        code: region.code,
        name: region.name,
        nameAr: region.nameAr,
        lat: region.lat,
        lon: region.lon,
        peakRisk: live?.peakRisk ?? 0,
        state: live?.state ?? 'normal',
        loadMw: live?.loadMw ?? 0,
      }
    }),
  }
}
