import { z } from "zod";
import { singaporeDate } from "./application-particulars";
import { cleanText, hasVisibleText, singleLineText } from "./application-text";
export const loanPurposes = [
  "Medical bills",
  "Education expenses",
  "Household bills",
  "Credit card bills",
  "Business expenses",
  "Home renovation",
  "Debt consolidation",
  "Other",
] as const;
export const proofOptions = ["Yes", "No"] as const;
export const repaymentSources = [
  "Monthly salary",
  "Upcoming bonus",
  "Business income",
  "Sale of an asset",
  "Other",
] as const;
export const referralSources = [
  "Google search",
  "Social media",
  "Friend or family",
  "Walk-in",
  "Loan comparison website",
  "Returning customer",
  "Other",
] as const;
const dayWithSuffix = (day: number) =>
  `${day}${day % 10 === 1 && day !== 11 ? "st" : day % 10 === 2 && day !== 12 ? "nd" : day % 10 === 3 && day !== 13 ? "rd" : "th"}`;
export const paydayOptions = [
  "Month end",
  "Twice a month",
  "Weekly",
  "Daily",
  "Varies",
  ...Array.from({ length: 31 }, (_, index) => dayWithSuffix(index + 1)),
] as [string, ...string[]];
export const statementDeliveries = [
  "Email",
  "Home address",
  "Company address",
] as const;

export const loanContextKeys = [
  "purpose",
  "purposeOther",
  "purposeProof",
  "repaymentSource",
  "repaymentOther",
  "referral",
  "dateJoined",
  "companyAddress",
  "payday",
  "statementDelivery",
] as const;
export type LoanContextKey = (typeof loanContextKeys)[number];
export type LoanContext = Record<LoanContextKey, string>;
export const emptyLoanContext = Object.fromEntries(
  loanContextKeys.map((key) => [key, ""]),
) as LoanContext;
export const loanContextSections = {
  loan: [
    "purpose",
    "purposeOther",
    "purposeProof",
    "repaymentSource",
    "repaymentOther",
    "referral",
  ],
  employment: ["dateJoined", "companyAddress", "payday"],
  statements: ["statementDelivery"],
} as const satisfies Record<string, readonly LoanContextKey[]>;
export const loanContextLabels: Record<LoanContextKey, string> = {
  purpose: "Purpose of the loan",
  purposeOther: "Describe the purpose",
  purposeProof: "Do you have documents showing the purpose?",
  repaymentSource: "How will you repay the loan?",
  repaymentOther: "Describe how you will repay",
  referral: "How did you hear about us?",
  dateJoined: "Date joined current employer",
  companyAddress: "Company address",
  payday: "Usual payday",
  statementDelivery: "Send statements and repayment receipts by",
};
export const loanContextOptions: Partial<
  Record<LoanContextKey, readonly string[]>
> = {
  purpose: loanPurposes,
  purposeProof: proofOptions,
  repaymentSource: repaymentSources,
  referral: referralSources,
  payday: paydayOptions,
  statementDelivery: statementDeliveries,
};
export const visibleLoanContextKeys = (
  value: Partial<Pick<LoanContext, "purpose" | "repaymentSource">>,
) =>
  loanContextKeys.filter(
    (key) =>
      (key !== "purposeOther" || value.purpose === "Other") &&
      (key !== "repaymentOther" || value.repaymentSource === "Other"),
  );
export const loanContextOptional = (key: LoanContextKey, value: LoanContext) =>
  key === "referral" ||
  key === "dateJoined" ||
  key === "payday" ||
  (key === "companyAddress" && value.statementDelivery !== "Company address");

const text = singleLineText;
const optionalChoice = <T extends readonly [string, ...string[]]>(options: T) =>
  z.union([z.literal(""), z.enum(options)], "Choose an option from the list.");
const shape = {
  purpose: z.enum(loanPurposes, "Choose the purpose of the loan."),
  purposeOther: text(100),
  purposeProof: z.enum(
    proofOptions,
    "Choose whether you have documents showing the purpose.",
  ),
  repaymentSource: z.enum(
    repaymentSources,
    "Choose how you will repay the loan.",
  ),
  repaymentOther: text(100),
  referral: optionalChoice(referralSources),
  dateJoined: z.union(
    [z.literal(""), z.iso.date("Enter a valid date.")],
    "Enter a valid date.",
  ),
  companyAddress: text(200),
  payday: optionalChoice(paydayOptions),
  statementDelivery: z.enum(
    statementDeliveries,
    "Choose how to receive statements and receipts.",
  ),
} satisfies Record<LoanContextKey, z.ZodType<string>>;

function crossIssues(input: LoanContext) {
  const issues: { key: LoanContextKey; message: string }[] = [];
  for (const [choice, detail, message] of [
    ["purpose", "purposeOther", "Describe the purpose of the loan."],
    [
      "repaymentSource",
      "repaymentOther",
      "Describe how you will repay the loan.",
    ],
  ] as const) {
    const described = hasVisibleText(input[detail]);
    if (input[choice] === "Other" && !described)
      issues.push({ key: detail, message });
    if (input[choice] !== "Other" && cleanText(input[detail]))
      issues.push({
        key: detail,
        message: "Choose Other to add a description.",
      });
  }
  if (z.iso.date().safeParse(input.dateJoined).success) {
    if (input.dateJoined < "1900-01-01")
      issues.push({ key: "dateJoined", message: "Enter a valid date." });
    else if (input.dateJoined > singaporeDate())
      issues.push({
        key: "dateJoined",
        message: "Enter a date that is not in the future.",
      });
  }
  if (
    input.statementDelivery === "Company address" &&
    !hasVisibleText(input.companyAddress)
  )
    issues.push({
      key: "companyAddress",
      message: "Enter your company address to receive statements there.",
    });
  return issues;
}
const baseSchema = z.object(shape).strict();
export const loanContextSchema = baseSchema.superRefine((value, ctx) => {
  for (const issue of crossIssues(value))
    ctx.addIssue({ code: "custom", path: [issue.key], message: issue.message });
});
export const loanContextRecordSchema = z
  .object(
    Object.fromEntries(
      loanContextKeys.map((key) => [key, z.string().max(250)]),
    ) as Record<LoanContextKey, z.ZodString>,
  )
  .partial()
  .catchall(z.string().max(250));
export const loanContextDraftSchema = z
  .object(
    Object.fromEntries(
      loanContextKeys.map((key) => [key, z.string().max(250)]),
    ) as Record<LoanContextKey, z.ZodString>,
  )
  .strict();

export function loanContextFailures(
  value: LoanContext,
): Partial<Record<LoanContextKey, string>> {
  const failures: Partial<Record<LoanContextKey, string>> = {};
  const parsed = baseSchema.safeParse(value);
  if (!parsed.success)
    for (const issue of parsed.error.issues) {
      const key = issue.path[0];
      if (
        typeof key === "string" &&
        (loanContextKeys as readonly string[]).includes(key)
      )
        failures[key as LoanContextKey] ??= issue.message;
    }
  for (const issue of crossIssues(value)) failures[issue.key] ??= issue.message;
  return failures;
}
