export type RiExtracted = {
  policyholderName: string;
  birthDate: string;
  address: string;
  insurer: string;
  policyNumber: string;
  coverageStart: string;
  coverageEnd: string;
  vehicleCategory: string;
  vehicleMake: string;
  registration: string;
  crm: string;
  lastRenewal: string;
  yearsAt050: string;
  professionalUse: string;
  claimsSummary: string;
  mainDriver: string;
  licenceFirstIssueDate: string;
  documentDate: string;
};

export const emptyRi: RiExtracted = {
  policyholderName: "",
  birthDate: "",
  address: "",
  insurer: "",
  policyNumber: "",
  coverageStart: "",
  coverageEnd: "",
  vehicleCategory: "",
  vehicleMake: "",
  registration: "",
  crm: "",
  lastRenewal: "",
  yearsAt050: "",
  professionalUse: "",
  claimsSummary: "",
  mainDriver: "",
  licenceFirstIssueDate: "",
  documentDate: "",
};

const months = [
  "janvier", "février", "mars", "avril", "mai", "juin",
  "juillet", "août", "septembre", "octobre", "novembre", "décembre",
];

function clean(value = "") {
  return value.replace(/\s+/g, " ").replace(/^[\s:;,-]+|[\s:;,-]+$/g, "").trim();
}

function escapeRegExp(value: string) {
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

function isoDate(value = "") {
  const numeric = value.match(/(\d{1,2})[/.\-](\d{1,2})[/.\-](\d{4})/);
  if (numeric) {
    return `${numeric[3]}-${numeric[2].padStart(2, "0")}-${numeric[1].padStart(2, "0")}`;
  }
  const words = value.toLocaleLowerCase("fr-FR").match(/(\d{1,2})\s+([a-zà-ÿ]+)\s+(\d{4})/i);
  if (words) {
    const index = months.indexOf(words[2]);
    if (index >= 0) return `${words[3]}-${String(index + 1).padStart(2, "0")}-${words[1].padStart(2, "0")}`;
  }
  return "";
}

function section(text: string, start: RegExp, end: RegExp) {
  const startMatch = start.exec(text);
  if (!startMatch) return "";
  const after = text.slice(startMatch.index + startMatch[0].length);
  const endMatch = end.exec(after);
  return endMatch ? after.slice(0, endMatch.index) : after;
}

function dates(value: string) {
  return [...value.matchAll(/\b\d{1,2}[/.\-]\d{1,2}[/.\-]\d{4}\b/g)].map((m) => isoDate(m[0]));
}

function valueBetween(text: string, startLabel: string, endLabel: string) {
  const pattern = new RegExp(`${escapeRegExp(startLabel)}\\s+(.{1,180}?)\\s+${escapeRegExp(endLabel)}`, "i");
  return clean(text.match(pattern)?.[1] || "");
}

function findVehicleMake(text: string) {
  const makes = [
    "ABARTH", "ALFA ROMEO", "AUDI", "BMW", "CITROEN", "CUPRA", "DACIA", "DS",
    "FIAT", "FORD", "HONDA", "HYUNDAI", "JEEP", "KIA", "LEXUS", "MAZDA",
    "MERCEDES", "MINI", "MITSUBISHI", "NISSAN", "OPEL", "PEUGEOT", "PORSCHE",
    "RENAULT", "SEAT", "SKODA", "SMART", "SUZUKI", "TESLA", "TOYOTA",
    "VOLKSWAGEN", "VOLVO",
  ];
  const upper = text.toLocaleUpperCase("fr-FR");
  return makes.find((make) => upper.includes(make)) || "";
}

export function parseRiText(rawText: string): RiExtracted {
  const text = clean(rawText);
  const identity = section(text, /B\.?\s*Identité et coordonnées du preneur d['’]assurance/i, /C\.?\s*Véhicule/i);
  const vehicle = section(text, /C\.?\s*Véhicule/i, /D\.?\s*Contrat/i);
  const contract = section(text, /D\.?\s*Contrat/i, /E\.?\s*Sinistre/i);
  const bonus = section(text, /CRM\s*[-–]?\s*BONUS\s*MALUS/i, /Sinistres survenus/i);
  const claims = section(text, /Sinistres survenus/i, /Conducteurs/i);
  const drivers = section(text, /Conducteurs/i, /H\.?\s*Date et signature/i);
  const signature = section(text, /H\.?\s*Date et signature/i, /(?:$)/i);

  const name = valueBetween(identity, "5. Nom et prénom(s), ou dénomination sociale", "6. Date de naissance")
    || valueBetween(identity, "5. Nom et prénom(s), ou", "6. Date de naissance");
  const birthDate = isoDate(identity.match(/6\.\s*Date de naissance\s+([^0-9]{0,20})?(\d{1,2}[/.\-]\d{1,2}[/.\-]\d{4})/i)?.[2] || "");
  const address = clean(identity.match(/8\.\s*Coordonnées\s+(.+)$/i)?.[1] || "");
  const insurer = valueBetween(text, "1. Dénomination sociale", "2. État membre")
    || clean(text.match(/\b(?:ACM|CIC|GMF|MAIF|MACIF|AXA|ALLIANZ|GENERALI|AVIVA|ABEILLE|MATMUT|MMA|GROUPAMA)\s+(?:IARD|ASSURANCES?)?(?:\s+SA)?\b/i)?.[0] || "");
  const policyNumber = clean(text.match(/N[°º]?\s*Contrat\s*:\s*([A-Z0-9][A-Z0-9 .\-]{4,24})/i)?.[1] || "");
  const contractDates = dates(contract);
  const crm = bonus.match(/\b(0[,.]\d{2}|1[,.]00)\b/)?.[1]?.replace(",", ".") || "";
  const bonusDates = dates(bonus);
  const yearsAt050 = clean(bonus.match(/Nombre d['’]années à 0[,.]50.{0,300}?\b(\d{1,2})\b\s+(?:Oui|Non|Yes|No)\b/i)?.[1] || "");
  const plate = vehicle.match(/\b[A-HJ-NP-TV-Z]{2}[- ]?\d{3}[- ]?[A-HJ-NP-TV-Z]{2}\b/i)?.[0] || text.match(/\b[A-HJ-NP-TV-Z]{2}[- ]?\d{3}[- ]?[A-HJ-NP-TV-Z]{2}\b/i)?.[0] || "";
  const driverDates = dates(drivers);
  const documentDate = dates(signature).at(-1) || dates(text).at(-1) || "";
  const noClaim = /N[EÉ]ANT\s*\/\s*NIL|AUCUN\s+SINISTRE/i.test(claims);

  return {
    policyholderName: name,
    birthDate,
    address,
    insurer,
    policyNumber,
    coverageStart: contractDates[0] || "",
    coverageEnd: contractDates[1] || "",
    vehicleCategory: clean(vehicle.match(/\b(?:A|B|C|D|M1|N1)\b/)?.[0] || ""),
    vehicleMake: findVehicleMake(vehicle || text),
    registration: plate.toLocaleUpperCase("fr-FR").replace(/\s/g, ""),
    crm,
    lastRenewal: bonusDates[0] || "",
    yearsAt050,
    professionalUse: /Non\s*\/\s*No/i.test(bonus) ? "Non" : /Oui\s*\/\s*Yes/i.test(bonus) ? "Oui" : "",
    claimsSummary: noClaim ? "Aucun sinistre déclaré sur la période du relevé" : "Sinistre(s) mentionné(s) sur le relevé - vérification nécessaire",
    mainDriver: name,
    licenceFirstIssueDate: driverDates[1] || "",
    documentDate,
  };
}

export async function extractRiFromFile(file: File) {
  if (file.type !== "application/pdf" && !file.name.toLocaleLowerCase("fr-FR").endsWith(".pdf")) {
    return { text: "", extracted: { ...emptyRi } };
  }

  const pdfjs = await import("pdfjs-dist/legacy/build/pdf.mjs");
  pdfjs.GlobalWorkerOptions.workerSrc = new URL(
    "pdfjs-dist/legacy/build/pdf.worker.min.mjs",
    import.meta.url,
  ).toString();
  const bytes = new Uint8Array(await file.arrayBuffer());
  const pdf = await pdfjs.getDocument({ data: bytes }).promise;
  const pages: string[] = [];
  for (let pageNumber = 1; pageNumber <= pdf.numPages; pageNumber += 1) {
    const page = await pdf.getPage(pageNumber);
    const content = await page.getTextContent();
    pages.push(content.items.map((item) => ("str" in item ? item.str : "")).join(" "));
  }
  const text = pages.join("\n");
  return { text, extracted: parseRiText(text) };
}

export function isRiRecent(isoValue: string) {
  if (!isoValue) return false;
  const issue = new Date(`${isoValue}T12:00:00`);
  if (Number.isNaN(issue.getTime())) return false;
  const limit = new Date();
  limit.setHours(0, 0, 0, 0);
  limit.setMonth(limit.getMonth() - 3);
  return issue >= limit && issue <= new Date();
}
