// Indian tax identifiers. Each check returns an error message, or null when valid.
// The tenant app applies the same rules (src/lib/taxIds.ts); keep them in step.

const ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';

// 4th character of a PAN: the holder type (Person, Company, HUF, Firm, AOP,
// Trust, BOI, Local authority, artificial Juridical person, Government).
const PAN_HOLDER_TYPES = 'PCHFATBLJG';

// GST state codes: 01–38 (38 = Ladakh), 97 Other Territory, 99 Centre Jurisdiction.
function isStateCode(code: string) {
  const n = Number(code);
  return /^\d{2}$/.test(code) && ((n >= 1 && n <= 38) || n === 97 || n === 99);
}

export function panError(pan: string): string | null {
  if (!/^[A-Z]{5}[0-9]{4}[A-Z]$/.test(pan)) {
    return 'PAN has 10 characters: 5 letters, 4 digits, then a letter (e.g. AABCE1234F)';
  }
  if (!PAN_HOLDER_TYPES.includes(pan[3])) {
    return 'The 4th letter of a PAN is the holder type: P, C, H, F, A, T, B, L, J or G';
  }
  return null;
}

// GSTIN check character: mod-36 over the first 14 characters.
export function gstinCheckChar(first14: string): string {
  let sum = 0;
  for (let i = 0; i < 14; i++) {
    const product = ALPHABET.indexOf(first14[i]) * (i % 2 === 0 ? 1 : 2);
    sum += Math.floor(product / 36) + (product % 36);
  }
  return ALPHABET[(36 - (sum % 36)) % 36];
}

export function gstinError(gstin: string): string | null {
  if (!/^[0-9A-Z]{15}$/.test(gstin)) return 'GSTIN has 15 letters and digits';
  if (!isStateCode(gstin.slice(0, 2))) {
    return 'GSTIN starts with a state code: 01–38, 97 or 99 (e.g. 27 for Maharashtra)';
  }
  if (panError(gstin.slice(2, 12))) return "Characters 3–12 of a GSTIN are the holder's PAN";
  if (!/[1-9A-Z]/.test(gstin[12])) return 'The 13th character of a GSTIN is 1–9 or A–Z';
  if (gstin[13] !== 'Z') return 'The 14th character of a GSTIN is always Z';
  if (gstin[14] !== gstinCheckChar(gstin.slice(0, 14))) {
    return "This GSTIN's check digit doesn't match. Check it for a typo.";
  }
  return null;
}

// A GSTIN carries its holder's PAN; for one organization the two must agree.
export function gstinPanMismatch(gstin: string, pan: string): string | null {
  return gstin.slice(2, 12) === pan ? null : `This GSTIN belongs to PAN ${gstin.slice(2, 12)}, not ${pan}`;
}
