import { z } from "zod";

export const RETENTION_POLICY_VERSION = "sg-moneylenders-v1";
export const BROWSER_DRAFT_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;

const instant = z.iso
  .datetime()
  .refine((value) => Number.isFinite(Date.parse(value)));
const lifecycle = z.discriminatedUnion("outcome", [
  z.object({ outcome: z.literal("unapproved") }),
  z.object({
    outcome: z.literal("approved"),
    loanEndedAt: instant.nullable(),
    borrowerRelationshipEndedAt: instant.nullable(),
  }),
]);

export const retentionRecordSchema = z
  .object({
    policyVersion: z.literal(RETENTION_POLICY_VERSION),
    receivedAt: instant,
    latestDocumentReceivedAt: instant,
    lifecycle,
    classificationReviewed: z.boolean(),
    hold: z.enum([
      "none",
      "investigation",
      "litigation",
      "regulatory",
      "business-review",
    ]),
  })
  .strict()
  .superRefine((record, context) => {
    if (
      Date.parse(record.latestDocumentReceivedAt) <
      Date.parse(record.receivedAt)
    ) {
      context.addIssue({
        code: "custom",
        path: ["latestDocumentReceivedAt"],
        message: "Document receipt cannot precede the record receipt boundary",
      });
    }
    if (record.lifecycle.outcome === "approved") {
      for (const key of [
        "loanEndedAt",
        "borrowerRelationshipEndedAt",
      ] as const) {
        const value = record.lifecycle[key];
        if (value && Date.parse(value) < Date.parse(record.receivedAt)) {
          context.addIssue({
            code: "custom",
            path: ["lifecycle", key],
            message: "End date cannot precede receipt",
          });
        }
      }
      const { loanEndedAt, borrowerRelationshipEndedAt } = record.lifecycle;
      if (
        loanEndedAt &&
        borrowerRelationshipEndedAt &&
        Date.parse(borrowerRelationshipEndedAt) < Date.parse(loanEndedAt)
      ) {
        context.addIssue({
          code: "custom",
          path: ["lifecycle", "borrowerRelationshipEndedAt"],
          message: "Borrower relationship cannot end before this loan",
        });
      }
    }
  });

export type RetentionRecord = z.infer<typeof retentionRecordSchema>;
export type RetentionDecision =
  | {
      action: "retain";
      reason:
        | "invalid-record"
        | "classification-required"
        | "hold"
        | "active-loan-or-relationship"
        | "within-period";
      eligibleAt?: string;
    }
  | { action: "delete"; eligibleAt: string };

function fiveYearsAfter(value: string): number {
  const singaporeOffset = 8 * 60 * 60 * 1000;
  const local = new Date(Date.parse(value) + singaporeOffset);
  local.setUTCFullYear(local.getUTCFullYear() + 5);
  return local.getTime() - singaporeOffset;
}

export function retentionDecision(
  input: unknown,
  now: number,
): RetentionDecision {
  const parsed = retentionRecordSchema.safeParse(input);
  if (!parsed.success || !Number.isFinite(now))
    return { action: "retain", reason: "invalid-record" };
  const record = parsed.data;
  if (!record.classificationReviewed)
    return { action: "retain", reason: "classification-required" };
  if (record.hold !== "none") return { action: "retain", reason: "hold" };
  let boundary = Math.max(
    Date.parse(record.receivedAt),
    Date.parse(record.latestDocumentReceivedAt),
  );
  if (record.lifecycle.outcome === "approved") {
    const { loanEndedAt, borrowerRelationshipEndedAt } = record.lifecycle;
    if (!loanEndedAt || !borrowerRelationshipEndedAt)
      return { action: "retain", reason: "active-loan-or-relationship" };
    boundary = Math.max(
      boundary,
      Date.parse(loanEndedAt),
      Date.parse(borrowerRelationshipEndedAt),
    );
  }
  const eligible = fiveYearsAfter(new Date(boundary).toISOString());
  const eligibleAt = new Date(eligible).toISOString();
  return now >= eligible
    ? { action: "delete", eligibleAt }
    : { action: "retain", reason: "within-period", eligibleAt };
}

export function draftHasExpired(savedAt: number, now: number): boolean {
  return (
    !Number.isFinite(savedAt) ||
    !Number.isFinite(now) ||
    savedAt > now ||
    now - savedAt >= BROWSER_DRAFT_MAX_AGE_MS
  );
}
