import { prisma } from '@/lib/db'
import { AUDIT_ACTIONS, writeAudit } from '@/lib/audit'
import { apiRoute, pagination, paginated } from '@/lib/api/handler'
import { ApiError, ERROR_CODES } from '@/lib/api/errors'
import { runSimulationSchema, type RunSimulationInput } from '@/lib/api/schemas'
import {
  runActionSimulation,
  runScenarioSimulation,
  type ScenarioParams,
} from '@/lib/services/simulation-service'

export const dynamic = 'force-dynamic'

export const GET = apiRoute({ permission: 'simulations.read' }, async ({ searchParams }) => {
  const { page, pageSize, skip, take } = pagination(searchParams)

  const [rows, total] = await Promise.all([
    prisma.simulation.findMany({
      orderBy: { startedAt: 'desc' },
      skip,
      take,
      include: {
        result: true,
        scenario: { select: { key: true, name: true, nameAr: true } },
        createdBy: { select: { name: true, nameAr: true } },
      },
    }),
    prisma.simulation.count(),
  ])

  return paginated(rows, total, page, pageSize)
})

/**
 * Run a simulation.
 *
 * Both simulation kinds land here because they share the same guarantee: nothing is
 * recommended to an operator that has not been run through the digital twin first, and
 * every run is persisted with its inputs so the number on screen can be reproduced.
 */
export const POST = apiRoute<Record<string, string>, RunSimulationInput>(
  {
    permission: 'simulations.run',
    bodySchema: runSimulationSchema,
    limit: 60,
  },
  async ({ body, user, request }) => {
    if (body.kind === 'action') {
      const outcome = await runActionSimulation({
        assetCode: body.assetCode,
        actionKeys: body.actionKeys,
        userId: user.id,
      })

      await writeAudit({
        action: AUDIT_ACTIONS.simulationStarted,
        entity: 'simulation',
        entityId: outcome.simulationId,
        user,
        request,
        after: {
          assetCode: outcome.assetCode,
          actions: outcome.chosen,
          riskBefore: outcome.riskBefore,
          riskAfter: outcome.riskAfter,
        },
        detail: `Preventive action simulation on ${outcome.assetCode}`,
      })

      return outcome
    }

    // Scenario simulation: either a stored scenario or an ad-hoc parameter set.
    let params: ScenarioParams
    let name = body.name ?? 'Custom scenario'
    let nameAr = body.name ?? 'سيناريو مخصص'
    let scenarioId: string | null = null

    if (body.scenarioKey) {
      const scenario = await prisma.scenario.findUnique({
        where: { key: body.scenarioKey },
        include: { targetAsset: { select: { code: true } } },
      })
      if (!scenario) throw new ApiError(ERROR_CODES.notFound)

      scenarioId = scenario.id
      name = scenario.name
      nameAr = scenario.nameAr
      params = {
        loadDeltaPct: scenario.loadDeltaPct,
        solarDeltaPct: scenario.solarDeltaPct,
        windDeltaPct: scenario.windDeltaPct,
        tempDeltaC: scenario.tempDeltaC,
        assetOutage: scenario.assetOutage,
        lineOutage: scenario.lineOutage,
        batteryOutage: scenario.batteryOutage,
        evSurgeMw: scenario.evSurgeMw,
        industrialMw: scenario.industrialMw,
        regionCode: scenario.targetRegion,
        assetCode: scenario.targetAsset?.code ?? null,
        durationMin: scenario.durationMin,
        // A stored scenario may narrow scope through the request as well.
        ...(body.params ?? {}),
      }
    } else if (body.params) {
      params = body.params
    } else {
      throw new ApiError(ERROR_CODES.invalidScenario)
    }

    const outcome = await runScenarioSimulation({
      params,
      name,
      nameAr,
      scenarioId,
      userId: user.id,
    })

    await writeAudit({
      action: AUDIT_ACTIONS.simulationStarted,
      entity: 'simulation',
      entityId: outcome.simulationId,
      user,
      request,
      after: {
        scenario: body.scenarioKey ?? 'custom',
        riskBefore: outcome.riskBefore,
        riskAfter: outcome.riskAfter,
        customersImpacted: outcome.customersImpacted,
      },
      detail: `What-if simulation: ${name}`,
    })

    return outcome
  },
)
