import { z } from "zod";
export function parseLoanAmount(value: string) {
  return /^\$?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d{0,2})?$/.test(value)
    ? Number(value.replaceAll(",", "").replace(/^\$/, ""))
    : NaN;
}
const groupDigits = (digits: string) =>
  digits.replace(/\B(?=(\d{3})+(?!\d))/g, ",");

export function editLoanAmount(
  previous: string,
  next: string,
  caret = next.length,
  inputType = "",
  collapsed = true,
) {
  const before = `$${previous}`;
  const deletesChar =
    inputType === "deleteContentBackward" ||
    inputType === "deleteContentForward";
  if (
    collapsed &&
    deletesChar &&
    before[caret] === "," &&
    next === before.slice(0, caret) + before.slice(caret + 1)
  ) {
    const target = inputType === "deleteContentForward" ? caret : caret - 1;
    next = next.slice(0, target) + next.slice(target + 1);
    caret = target;
  }
  const tailMatches = before.endsWith(next.slice(caret));
  let start = 0;
  if (tailMatches)
    while (
      start < Math.min(caret, before.length - (next.length - caret)) &&
      before[start] === next[start]
    )
      start++;
  const rejected = {
    amount: previous,
    caret: tailMatches ? Math.max(1, start) : before.length,
  };
  const ascii = next
    .replace(/[！-～]/g, (char) =>
      String.fromCharCode(char.charCodeAt(0) - 0xfee0),
    )
    .replaceAll("　", " ");
  const labelEnd = /^\s*\$?\s*(?:sgd|sg?\$)/i.exec(ascii)?.[0].length ?? 0;
  const suffixStart = ascii.length - (/sgd\s*$/i.exec(ascii)?.[0].length ?? 0);
  let amount = "";
  let keptBeforeCaret = 0;
  let decimals = -1;
  for (let index = 0; index < ascii.length; index++) {
    const char = ascii[index];
    if (
      index < labelEnd ||
      index >= suffixStart ||
      char === "," ||
      char === "$" ||
      /\s/.test(char)
    )
      continue;
    if (char === ".") {
      if (decimals >= 0) return rejected;
      decimals = 0;
    } else if (/^[0-9]$/.test(char)) {
      if (decimals === 2) return rejected;
      if (decimals >= 0) decimals++;
    } else return rejected;
    amount += char;
    if (index < caret) keptBeforeCaret++;
  }
  const [whole, fraction] = amount.split(".");
  const formatted =
    groupDigits(whole) + (fraction === undefined ? "" : `.${fraction}`);
  let position = 1;
  for (let seen = 0; seen < keptBeforeCaret; position++)
    if (formatted[position - 1] !== ",") seen++;
  return { amount: formatted, caret: position };
}

export function normalizeLoanAmount(amount: string) {
  if (!/\d/.test(amount)) return "";
  const [whole, fraction] = amount.replaceAll(",", "").split(".");
  return (
    groupDigits(whole.replace(/^0+(?=\d)/, "") || "0") +
    (fraction ? `.${fraction}` : "")
  );
}
export const calculatorSchema = z.object({
  amount: z.number().finite().min(1).max(1_000_000),
  months: z.number().int().min(1).max(120),
  rate: z.number().int().min(1).max(4),
});
export function calculateLoan(input: z.input<typeof calculatorSchema>) {
  const { amount, months, rate } = calculatorSchema.parse(input);
  const monthlyRate = rate / 100;
  const paymentCents = Math.round(
    ((amount * monthlyRate) / -Math.expm1(-months * Math.log1p(monthlyRate))) *
      100,
  );
  const repaymentCents = paymentCents * months;
  return {
    monthly_payment: paymentCents / 100,
    total_repayment: repaymentCents / 100,
    total_interest: (repaymentCents - Math.round(amount * 100)) / 100,
  };
}
