import {
  assessmentSchema,
  myinfoSnapshotSchema,
} from "./application-assessment";
import { z } from "zod";
import {
  applicationFieldSchema,
  documentKinds,
  documentMetadataSchema,
  staffDocumentKinds,
} from "./application-schema";
import {
  loanContextRecordSchema,
  loanContextSchema,
} from "./application-loan-context";
import {
  declarationRecordSchema,
  declarationSchema,
} from "./application-declaration";
import { retentionRecordSchema } from "./application-retention";
import {
  particularsRecordSchema,
  particularsSchema,
} from "./application-particulars";
export const capabilitySchema = z.string().regex(/^[a-f0-9]{64}$/);
export const submissionDocumentSchema = documentMetadataSchema
  .extend({ sha256: capabilitySchema })
  .strict();
export const prepareApplicationSchema = z
  .object({
    requestId: z.uuid().toLowerCase(),
    capability: capabilitySchema,
    fields: applicationFieldSchema,
    particulars: particularsSchema,
    loanContext: loanContextSchema,
    declaration: declarationSchema,
    assessment: assessmentSchema.optional(),
    myinfoReceipt: z.string().min(1).max(400000).optional(),
    documents: z
      .array(submissionDocumentSchema)
      .max(documentKinds.length)
      .refine(
        (docs) => new Set(docs.map((doc) => doc.kind)).size === docs.length,
      ),
    turnstileToken: z.string().min(1).max(2048),
  })
  .strict();
export const ownedApplicationSchema = z
  .object({ requestId: z.uuid().toLowerCase(), capability: capabilitySchema })
  .strict();
export const applicationStatuses = [
  "pending",
  "new",
  "in_review",
  "approved",
  "rejected",
  "withdrawn",
] as const;
const receiptDocumentSchema = submissionDocumentSchema
  .extend({
    kind: z.enum(staffDocumentKinds),
    uploadedBy: z.email().max(254).optional(),
    ticket: z.string().max(4096),
    path: z.string().regex(/^applications\/uploads\/[a-f0-9-]{36}$/),
    generation: z
      .string()
      .regex(/^[1-9][0-9]{0,19}$/)
      .nullable(),
  })
  .strict();
export const applicationRecordSchema = z
  .object({
    id: z.uuid(),
    reference: z.string().regex(/^MC-[A-F0-9]{32}$/),
    ownerDigest: capabilitySchema,
    fingerprint: capabilitySchema,
    fields: applicationFieldSchema,
    particulars: particularsRecordSchema.optional(),
    loanContext: loanContextRecordSchema.optional(),
    declaration: declarationRecordSchema.optional(),
    assessment: assessmentSchema.optional(),
    myinfo: myinfoSnapshotSchema.optional(),
    documents: z.array(receiptDocumentSchema).max(staffDocumentKinds.length),
    createdAt: z.iso.datetime(),
    submittedAt: z.iso.datetime().nullable(),
    status: z.enum(applicationStatuses),
    revision: z.number().int().nonnegative(),
    retention: retentionRecordSchema,
    deleting: z.boolean(),
    archivedAt: z.iso.datetime().nullable().default(null),
    notes: z
      .array(
        z
          .object({
            id: z.uuid(),
            author: z.string().min(1).max(254),
            at: z.iso.datetime(),
            text: z.string().min(1).max(1000),
          })
          .strict(),
      )
      .max(100)
      .default([]),
    assignee: z.email().max(254).nullable().default(null),
    source: z.enum(["applicant", "staff"]).default("applicant"),
    retentionReview: z
      .object({
        basis: z.string().max(500),
        nextReviewAt: z.iso.datetime().nullable(),
      })
      .strict()
      .default({ basis: "Awaiting classification", nextReviewAt: null }),
    notification: z
      .object({
        state: z.enum(["waiting", "pending", "sent", "review"]),
        leaseUntil: z.number().nonnegative(),
        attempts: z.number().int().nonnegative(),
      })
      .strict(),
    history: z
      .array(
        z
          .object({
            actor: z.string().min(1).max(128),
            at: z.iso.datetime(),
            field: z.enum([
              "status",
              "retention",
              "archive",
              "assignment",
              "details",
              "document",
              "created",
            ]),
            before: z.string().max(1500),
            after: z.string().max(1500),
          })
          .strict(),
      )
      .max(500),
  })
  .strict();
export type ApplicationRecord = z.infer<typeof applicationRecordSchema>;
export type PrepareApplication = z.infer<typeof prepareApplicationSchema>;
export class ApplicationError extends Error {
  constructor(
    public readonly status: number,
    message: string,
  ) {
    super(message);
  }
}
