/**
 * Typed Zod schemas for scenario simulation data structures.
 *
 * The scenario feature does not persist scenarioData in JSONB — it computes
 * everything on-the-fly from assignments, projects, and resources. These schemas
 * describe the shapes that flow in and out of the simulation layer so that any
 * deserialization (e.g. from a future scenario snapshot) can be validated at
 * the API boundary rather than silently returning wrong-typed data.
 *
 * TODO(scenario-snapshots): if we ever persist scenario results to a JSONB
 * column or a dedicated table, add `.parse()` at the DB read-boundary here.
 */

import { z } from "zod";

// ---------------------------------------------------------------------------
// Input schemas (mirrored from scenario-procedure-support for central reference)
// ---------------------------------------------------------------------------

export const ScenarioChangeInputSchema = z.object({
  assignmentId: z.string().optional(),
  resourceId: z.string().optional(),
  roleId: z.string().optional(),
  startDate: z.coerce.date(),
  endDate: z.coerce.date(),
  hoursPerDay: z.number().positive().max(24),
  remove: z.boolean().optional(),
});

export const ScenarioSimulationRequestSchema = z.object({
  projectId: z.string(),
  changes: z.array(ScenarioChangeInputSchema).min(1),
});

// ---------------------------------------------------------------------------
// Output schemas (describe the shape returned by simulateProjectScenario)
// ---------------------------------------------------------------------------

const ScenarioSummarySchema = z.object({
  totalCostCents: z.number().int(),
  totalHours: z.number(),
  headcount: z.number().int(),
  skillCount: z.number().int(),
});

const ScenarioResourceImpactSchema = z.object({
  resourceId: z.string(),
  resourceName: z.string(),
  chargeabilityTarget: z.number().nullable(),
  currentUtilization: z.number(),
  scenarioUtilization: z.number(),
  utilizationDelta: z.number(),
  isOverallocated: z.boolean(),
});

export const ScenarioSimulationResultSchema = z.object({
  baseline: ScenarioSummarySchema,
  scenario: ScenarioSummarySchema,
  delta: z.object({
    costCents: z.number().int(),
    hours: z.number(),
    headcount: z.number().int(),
    skillCoveragePct: z.number().int(),
  }),
  resourceImpacts: z.array(ScenarioResourceImpactSchema),
  warnings: z.array(z.string()),
  budgetCents: z.number().int(),
});

// ---------------------------------------------------------------------------
// Baseline output schema (describe the shape returned by readProjectScenarioBaseline)
// ---------------------------------------------------------------------------

const ScenarioBaselineAllocationSchema = z.object({
  id: z.string(),
  resourceId: z.string().nullable(),
  resourceName: z.string(),
  resourceEid: z.string(),
  lcrCents: z.number().int(),
  roleId: z.string().nullable(),
  roleName: z.string(),
  roleColor: z.string().nullable(),
  startDate: z.string(),
  endDate: z.string(),
  hoursPerDay: z.number(),
  status: z.string(),
  costCents: z.number().int(),
  totalHours: z.number(),
  workingDays: z.number(),
});

const ScenarioBaselineDemandSchema = z.object({
  id: z.string(),
  roleId: z.string().nullable(),
  roleName: z.string(),
  roleColor: z.string().nullable(),
  startDate: z.string(),
  endDate: z.string(),
  hoursPerDay: z.number(),
  headcount: z.number().int(),
  status: z.string(),
});

export const ScenarioBaselineResultSchema = z.object({
  project: z.object({
    id: z.string(),
    name: z.string(),
    shortCode: z.string().nullable(),
    startDate: z.date(),
    endDate: z.date(),
    budgetCents: z.number().int().nullable(),
    orderType: z.string().nullable(),
  }),
  assignments: z.array(ScenarioBaselineAllocationSchema),
  demands: z.array(ScenarioBaselineDemandSchema),
  totalCostCents: z.number().int(),
  totalHours: z.number(),
  budgetCents: z.number().int().nullable(),
});

// ---------------------------------------------------------------------------
// Helper: validate scenario simulation result at the API boundary
//
// Usage:
//   const result = parseScenarioSimulationResult(await simulateProjectScenario(db, input));
// ---------------------------------------------------------------------------

export function parseScenarioSimulationResult(
  raw: unknown,
): z.infer<typeof ScenarioSimulationResultSchema> {
  return ScenarioSimulationResultSchema.parse(raw);
}

export function parseScenarioBaselineResult(
  raw: unknown,
): z.infer<typeof ScenarioBaselineResultSchema> {
  return ScenarioBaselineResultSchema.parse(raw);
}

export type ScenarioChangeInput = z.infer<typeof ScenarioChangeInputSchema>;
export type ScenarioSimulationRequest = z.infer<typeof ScenarioSimulationRequestSchema>;
export type ScenarioSimulationResult = z.infer<typeof ScenarioSimulationResultSchema>;
export type ScenarioBaselineResult = z.infer<typeof ScenarioBaselineResultSchema>;
