import { z } from "zod"; import { REVIEW_ULAE_LIMITS, calibrateReviewUlae, calculateReviewUlaeReserve, replayReviewUlaeCalibration, replayReviewUlaeReserve, validateReviewUlaeCalibrationRequest, validateReviewUlaeReserveRequest, isDiagnosticPlainRecord, isDiagnosticToken, isRealIsoDate, type ReviewUlaeCalibration, type ReviewUlaeCalibrationRequest, type ReviewUlaeReserve, type ReviewUlaeReserveRequest, } from "@actuarial-ts/core"; import { analysisContextSchema, analysisReferenceSchema, analysisSnapshotSchema, assumptionSnapshotSchema, quantitySemanticsSchema } from "./reviewContracts.js"; import { reviewOriginSeriesRequestSchema } from "./reviewOriginSeriesContracts.js"; import { parseReviewBoundary } from "./reviewBoundary.js"; const token = z.string().refine(isDiagnosticToken), date = z.string().refine(isRealIsoDate); const declaration = z.object({ actorId: token, knownDate: date, rationale: token }).strict(); const relationship = z.object({ relationship: z.literal("corresponding"), rationale: token }).strict(); const limits = z.object({ maximumPeriods: z.number().int().positive().max(REVIEW_ULAE_LIMITS.maximumPeriods), maximumOrigins: z.number().int().positive().max(REVIEW_ULAE_LIMITS.maximumOrigins), maximumInputCharacters: z.number().int().positive().max(REVIEW_ULAE_LIMITS.maximumInputCharacters), maximumOutputCharacters: z.number().int().positive().max(REVIEW_ULAE_LIMITS.maximumOutputCharacters) }).strict(); const measure = z.object({ source: analysisReferenceSchema, quantity: quantitySemanticsSchema }).strict(); export const reviewUlaeCalibrationRequestSchema = z.object({ id: token, version: token, framework: z.enum(["generalized", "kittel", "classical-paid-to-paid"]), measures: z.object({ ulaePaid: measure, reportedUltimate: measure, paid: measure, closedUltimate: measure }).strict(), financialBases: z.object({ expense: analysisReferenceSchema, loss: analysisReferenceSchema }).strict(), populations: z.object({ expense: analysisReferenceSchema, loss: analysisReferenceSchema }).strict(), expenseComponentIds: z.array(token).min(1), periods: z.array(z.object({ id: token, startDate: date, endDate: date, ulaePaid: z.number().finite().nonnegative(), reportedUltimate: z.number().finite().nonnegative(), paid: z.number().finite().nonnegative(), closedUltimate: z.number().finite().nonnegative() }).strict()).min(1).max(REVIEW_ULAE_LIMITS.maximumPeriods), weights: z.object({ opening: assumptionSnapshotSchema, maintaining: assumptionSnapshotSchema, closing: assumptionSnapshotSchema }).strict(), relationship, knownDate: date, declaration, policies: z.object({ unadoptedAssumption: z.enum(["reject", "allow-qualified"]) }).strict(), snapshots: z.array(analysisSnapshotSchema), limits, }).strict(); const retainedCalibration = z.unknown().superRefine((value, context) => { const parsed = z.object({ request: reviewUlaeCalibrationRequestSchema }).passthrough().safeParse(value); if (!parsed.success) parsed.error.issues.forEach(issue => context.addIssue(issue)); }); const retainedSeries = z.unknown().superRefine((value, context) => { const parsed = z.object({ request: reviewOriginSeriesRequestSchema }).passthrough().safeParse(value); if (!parsed.success) parsed.error.issues.forEach(issue => context.addIssue(issue)); }); export const reviewUlaeReserveRequestSchema = z.object({ id: token, version: token, form: z.enum(["expected", "bornhuetterFerguson", "development"]), calibration: retainedCalibration, selectedRatio: assumptionSnapshotSchema, inputs: z.object({ ultimateLosses: retainedSeries, reportedToDate: retainedSeries.nullable(), paidToDate: retainedSeries.nullable(), closedToDate: retainedSeries.nullable(), ulaePaidToDate: retainedSeries.nullable() }).strict(), context: analysisContextSchema, quantity: quantitySemanticsSchema, relationship, policies: z.object({ unadoptedAssumption: z.enum(["reject", "allow-qualified"]) }).strict(), declaration, snapshots: z.array(analysisSnapshotSchema), limits, }).strict(); export function parseReviewUlaeCalibrationRequest(input: unknown): ReviewUlaeCalibrationRequest { return validateReviewUlaeCalibrationRequest(parseReviewBoundary(reviewUlaeCalibrationRequestSchema, input) as ReviewUlaeCalibrationRequest).request; } export function parseReviewUlaeReserveRequest(input: unknown): ReviewUlaeReserveRequest { return validateReviewUlaeReserveRequest(parseReviewBoundary(reviewUlaeReserveRequestSchema, input) as ReviewUlaeReserveRequest).request; } export function calibrateReviewUlaeFromInput(input: unknown): ReviewUlaeCalibration { return calibrateReviewUlae(parseReviewUlaeCalibrationRequest(input)); } export function calculateReviewUlaeReserveFromInput(input: unknown): ReviewUlaeReserve { return calculateReviewUlaeReserve(parseReviewUlaeReserveRequest(input)); } export function replayReviewUlaeCalibrationFromInput(input: unknown): ReviewUlaeCalibration { return replayReviewUlaeCalibration(parseReviewBoundary(z.custom(isDiagnosticPlainRecord), input)); } export function replayReviewUlaeReserveFromInput(input: unknown): ReviewUlaeReserve { return replayReviewUlaeReserve(parseReviewBoundary(z.custom(isDiagnosticPlainRecord), input)); }