import { z } from "zod";
import { cleanText, hasVisibleText, singleLineText } from "./application-text";
export const DECLARATION_VERSION = "2026-09-15";
export const answerOptions = ["Yes", "No"] as const;

export const dueDiligenceKeys = [
  "stillEmployed",
  "otherBeneficiary",
  "politicallyExposed",
  "unlicensedLoans",
  "badDebt",
  "bankruptcy",
  "convicted",
] as const;
export type DueDiligenceKey = (typeof dueDiligenceKeys)[number];
export const dueDiligenceQuestions: Record<DueDiligenceKey, string> = {
  stillEmployed:
    "I am still under employment and did not tender a resignation from the above mentioned job.",
  otherBeneficiary:
    "Is there any other individual or company that will benefit from the loan?",
  politicallyExposed: "Are you a politically-exposed person?",
  unlicensedLoans:
    "Have you ever applied any loans from unlicensed moneylenders?",
  badDebt:
    "Do you have any bad debt / default / restructured payment history with other licensed moneylenders?",
  bankruptcy: "Any Bankruptcy / Pending Writ of Summons / Lawsuits?",
  convicted: "Have you ever been convicted of a crime before?",
};
export const detailFor = {
  bankruptcy: "bankruptcyDetails",
  convicted: "convictionDetails",
} as const;

export const declarationKeys = [
  "truthful",
  "noBankruptcy",
  "disclosure",
  "creditBureau",
  "creditChecks",
  "keepUpdated",
  "noApproval",
  "informedDecision",
  "agreeAll",
] as const;
export type DeclarationKey = (typeof declarationKeys)[number];
export const disclosureRecipients = [
  "Registry of Moneylenders",
  "Moneylenders Credit Bureau (MLCB)",
  "My Employer / My Company",
  "Debt Collection agencies or approved debt collectors",
  "Any law firm",
  "Our application software, hosting, database, document storage and email service providers, acting as data intermediaries",
  "Third Party Accountants / Auditors",
  "Prescribed Credit Bureaus and Social Service Agencies which are prescribed under rule 22CA and rule 22CB of the Moneylenders Rules 2009 respectively",
  "Credit Association of Singapore (CAS)",
] as const;
export const declarationText: Record<DeclarationKey, string> = {
  truthful:
    "I hereby declare that all information given by me is true and correct. I further acknowledge that any attempt to furnish Max Credit Pte Ltd with false information shall be considered as cheating and shall be referred to the police.",
  noBankruptcy:
    "I affirm that at the time of this application, I am not involved in any un-discharged bankruptcy case and no statutory demand has been served on me nor any legal proceedings commenced against me.",
  disclosure:
    'I hereby consent and authorise Max Credit Pte Ltd (the "Company") to release all or some of my personal/loan/contract details to the following Company/organization or parties:',
  creditBureau:
    "I hereby agree that some/all information is to be submitted to MLCB for the purpose of producing a credit report and the information may be disclosed by MLCB to the Registry of Moneylenders and any public agency (when deemed necessary by Registry of Moneylenders).",
  creditChecks:
    "I hereby authorise the Company to conduct credit checks and verify information given in this application with the above companies/organizations or parties without prior reference to me; I expressly permit and authorise the Company and the Company's officers to disclose, reveal and divulge any and all information and particulars relating to and in connection with me, any and all of my accounts with the Company, any transactions or dealings between me and the Company, this application and/or any facilities granted to me to the above companies/organizations or parties at any time and from time to time, including any governmental agencies and authorities in Singapore.",
  keepUpdated:
    "I hereby accept that it is my full responsibility to keep the Company updated on any changes to my personal particulars on an immediate basis and the Company reserve the right to demand me to repay the full loan amount as well as late interest and late fee/s incurred if any in the event of failure to adhere to any of the other terms and conditions as per my loan application and agreement. I am aware that any overdue payment/s will be listed in Credit Bureau's records and these maybe assessed by financial institutions and other approving credit companies.",
  noApproval:
    "I hereby agree that this application does not construe or imply an approval for the loan. The Company reserves the right to reject the application or vary the terms of the loan granted, at the Company's sole discretion and without giving any reason for doing so. In the event that this application is not approved, I consent the Company to retain any or all documents submitted.",
  informedDecision:
    "I hereby acknowledge that I will fully utilize the provided information to make an informed decision regarding the acceptance of the loan.",
  agreeAll:
    "I hereby acknowledge, consent and agree to all the above-mentioned information.",
};
export const disclosurePurpose =
  "for the purpose of collating such information for credit management, risk assessment and debt recovery, which information may be accessible by subscribers to the above-mentioned parties. I am informed and understand that the Company is a contributor and subscriber of information with the above-mentioned parties. My personal/loan/contract details shall include inter alia, my personal particulars including name (alias name), identity card/passport/FIN number, nationality, date of birth, telephone number, home address, income, assets, liabilities, loan amount, interest rate, loan disbursement date, tenure of loan and loan-servicing history.";

export type Declaration = Record<
  DueDiligenceKey | "bankruptcyDetails" | "convictionDetails",
  string
> &
  Record<DeclarationKey, boolean> & { version: string };
export const emptyDeclaration: Declaration = {
  ...(Object.fromEntries(dueDiligenceKeys.map((key) => [key, ""])) as Record<
    DueDiligenceKey,
    string
  >),
  bankruptcyDetails: "",
  convictionDetails: "",
  ...(Object.fromEntries(declarationKeys.map((key) => [key, false])) as Record<
    DeclarationKey,
    boolean
  >),
  version: DECLARATION_VERSION,
};
export const requiredDeclarations = (
  value: Pick<Declaration, "bankruptcy">,
): readonly DeclarationKey[] =>
  declarationKeys.filter(
    (key) => key !== "noBankruptcy" || value.bankruptcy !== "Yes",
  );

const details = singleLineText(300);
const shape = {
  ...(Object.fromEntries(
    dueDiligenceKeys.map((key) => [
      key,
      z.enum(answerOptions, "Choose Yes or No."),
    ]),
  ) as Record<DueDiligenceKey, z.ZodEnum<{ Yes: "Yes"; No: "No" }>>),
  bankruptcyDetails: details,
  convictionDetails: details,
  ...(Object.fromEntries(
    declarationKeys.map((key) => [key, z.boolean()]),
  ) as Record<DeclarationKey, z.ZodBoolean>),
  version: z.string().max(40),
};
function crossIssues(input: Declaration, checkVersion = true) {
  const issues: { key: string; message: string }[] = [];
  for (const [question, detail] of Object.entries(detailFor) as [
    keyof typeof detailFor,
    (typeof detailFor)[keyof typeof detailFor],
  ][]) {
    const described = hasVisibleText(input[detail]);
    if (input[question] === "Yes" && !described)
      issues.push({
        key: detail,
        message: "Give details for your Yes answer.",
      });
    if (input[question] !== "Yes" && cleanText(input[detail]))
      issues.push({ key: detail, message: "Answer Yes to add details." });
  }
  const required = requiredDeclarations(input);
  for (const key of declarationKeys) {
    if (required.includes(key) && input[key] !== true)
      issues.push({ key, message: "Tick this declaration to continue." });
    if (!required.includes(key) && input[key])
      issues.push({
        key,
        message: "This declaration does not apply to your answers.",
      });
  }
  if (checkVersion && input.version !== DECLARATION_VERSION)
    issues.push({
      key: "version",
      message:
        "The declarations have been updated. Review and accept them again.",
    });
  return issues;
}
const baseSchema = z.object(shape).strict();
const withChecks = (checkVersion: boolean) =>
  baseSchema.superRefine((value, ctx) => {
    for (const issue of crossIssues(value, checkVersion))
      ctx.addIssue({
        code: "custom",
        path: [issue.key],
        message: issue.message,
      });
  });
export const declarationSchema = withChecks(true);
export const declarationRecordSchema = z
  .object({ version: z.string().min(1).max(40) })
  .catchall(z.union([z.string().max(400), z.boolean()]));
export const refreshDeclaration = (value: Declaration): Declaration =>
  value.version === DECLARATION_VERSION
    ? value
    : {
        ...value,
        ...(Object.fromEntries(
          declarationKeys.map((key) => [key, false]),
        ) as Record<DeclarationKey, boolean>),
        version: DECLARATION_VERSION,
      };
export const declarationDraftSchema = baseSchema
  .extend({
    ...(Object.fromEntries(
      dueDiligenceKeys.map((key) => [key, z.string().max(3)]),
    ) as Record<DueDiligenceKey, z.ZodString>),
    bankruptcyDetails: z.string().max(400),
    convictionDetails: z.string().max(400),
  })
  .strict();
export function declarationVersionOutdated(body: unknown) {
  const declaration =
    body && typeof body === "object"
      ? (body as { declaration?: unknown }).declaration
      : undefined;
  const version =
    declaration && typeof declaration === "object"
      ? (declaration as { version?: unknown }).version
      : undefined;
  return typeof version === "string" && version !== DECLARATION_VERSION;
}
export function declarationFailures(
  value: Declaration,
): Record<string, string> {
  const failures: Record<string, 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") failures[key] ??= issue.message;
    }
  for (const issue of crossIssues(value)) failures[issue.key] ??= issue.message;
  return failures;
}
