"use client";

import { FormEvent, useEffect, useMemo, useState } from "react";
import Image from "next/image";
import Link from "next/link";
import * as XLSX from "xlsx";
import { EnterpriseCollectionsCenter } from "./enterprise-collections-center";
import { COLLECTION_EMAIL_AUTOMATIC_NOTICE, formatCollectionEmailContent } from "@/lib/collections-email";

type Risk = "low" | "medium" | "high" | "critical";
type CollectionSection = "portfolio" | "worklist" | "automation" | "reporting";
type AgingBucket = "current" | "days1to30" | "days31to60" | "days61to90" | "days90plus";
type DocumentRow = { id:number; cashFlowId:number; fileName:string; mimeType:string; sizeBytes:number; uploadedBy:string; uploadedAt:string };
type Invoice = { id:number; companyId:string; party:string; reference:string; amount:number; invoiceDate?:string|null; dueDate:string; plannedDate:string; delayDays:number; isOverdue:boolean; note?:string|null; documents:DocumentRow[] };
type Contact = { id:number; email?:string|null; phone?:string|null; contactName?:string|null; ownerName?:string|null; riskOverride?:Risk|null; notes?:string|null };
type Reminder = { id:number; alertLevel:Risk; channel:"email"|"phone"|"note"; templateId?:number|null; recipient?:string|null; subject?:string|null; content:string; status:"draft"|"queued"|"sent"|"failed"|"logged"; sentBy:string; sentAt?:string|null; failureReason?:string|null; createdAt:string; invoiceIds:number[] };
type CollectionCase = { id:number; caseType:"promise"|"dispute"; amount:number; dueDate?:string|null; status:"open"|"kept"|"broken"|"resolved"|"cancelled"; effectiveStatus:"open"|"kept"|"broken"|"resolved"|"cancelled"; reason:string; ownerName?:string|null; createdBy:string; createdAt:string; updatedAt:string };
type Customer = { companyId:string; customerKey:string; customerName:string; invoices:Invoice[]; contact:Contact|null; totalDue:number; overdueAmount:number; currentAmount:number; collectibleAmount:number; promisedAmount:number; disputedAmount:number; brokenPromises:number; openDisputes:number; collected30d:number; cases:CollectionCase[]; overdueCount:number; invoiceCount:number; maxDelay:number; computedRisk:Risk; riskLevel:Risk; lastReminderAt?:string|null; reminders:Reminder[] };
type Template = { id:number; code:string; name:string; alertLevel:Risk; minDaysOverdue:number; maxDaysOverdue?:number|null; subjectTemplate:string; bodyTemplate:string; accentColor:string };
type Payload = { generatedAt:string; emailDeliveryConfigured:boolean; companies:Array<{id:string;name:string;shortName:string}>; templates:Template[]; customers:Customer[]; metrics:{totalDue:number;overdueTotal:number;currentTotal:number;overdueInvoices:number;criticalCustomers:number} };
type EmailSettings = { provider:"resend"; enabled:boolean; configured:boolean; source:"database"|"environment"|"none"; fromName:string; fromEmail:string; apiUrl:string; apiKeyConfigured:boolean; lastTestStatus:"untested"|"success"|"failed"; lastTestMessage?:string|null; lastTestAt?:string|null; updatedBy?:string|null; updatedAt?:string|null };

const emptyPayload:Payload={generatedAt:"",emailDeliveryConfigured:false,companies:[],templates:[],customers:[],metrics:{totalDue:0,overdueTotal:0,currentTotal:0,overdueInvoices:0,criticalCustomers:0}};
const money=(value:number)=>new Intl.NumberFormat("fr-MA",{style:"currency",currency:"MAD",maximumFractionDigits:2}).format(value);
const shortMoney=(value:number)=>new Intl.NumberFormat("fr-MA",{notation:"compact",maximumFractionDigits:1}).format(value)+" MAD";
const dateFr=(value?:string|null)=>value?new Date(value.length===10?`${value}T00:00:00`:value).toLocaleDateString("fr-FR"):"—";
const riskCopy:Record<Risk,{label:string;hint:string}>={low:{label:"Faible",hint:"Échéance à venir"},medium:{label:"Modéré",hint:"Retard jusqu’à 14 jours"},high:{label:"Élevé",hint:"Retard de 15 à 44 jours"},critical:{label:"Critique",hint:"Retard de 45 jours ou plus"}};
const agingLabels:Record<AgingBucket,string>={current:"Non échu",days1to30:"1–30 jours",days31to60:"31–60 jours",days61to90:"61–90 jours",days90plus:"90 jours et +"};
const invoiceAgingBucket=(invoice:Invoice):AgingBucket=>!invoice.isOverdue?"current":invoice.delayDays<=30?"days1to30":invoice.delayDays<=60?"days31to60":invoice.delayDays<=90?"days61to90":"days90plus";

function fillTemplate(template:string,customer:Customer,companyName:string,selected:Invoice[]){
  const overdue=selected.filter(invoice=>invoice.isOverdue);
  const total=selected.reduce((sum,invoice)=>sum+Number(invoice.amount),0);
  const values:Record<string,string>={
    customer:customer.customerName,company:companyName,contact:customer.contact?.contactName||"Madame, Monsieur",
    invoice_count:String(selected.length),total_due:money(total),max_delay:String(overdue.reduce((max,invoice)=>Math.max(max,invoice.delayDays),0)),
  };
  return template.replace(/{{(\w+)}}/g,(_,key:string)=>values[key]??"");
}

function fillReminderTemplate(template:string,customer:Customer,companyName:string,selected:Invoice[]){
  return formatCollectionEmailContent(fillTemplate(template,customer,companyName,selected));
}

export function CollectionsDashboard({initialEmbedded=false}:{initialEmbedded?:boolean}={}){
  const [embedded,setEmbedded]=useState(initialEmbedded);
  const [navigationReady,setNavigationReady]=useState(false);
  const [data,setData]=useState<Payload>(emptyPayload);
  const [loading,setLoading]=useState(true);
  const [error,setError]=useState("");
  const [toast,setToast]=useState("");
  const [search,setSearch]=useState("");
  const [filter,setFilter]=useState<"all"|"overdue"|"current"|"critical">("all");
  const [section,setSection]=useState<CollectionSection>("portfolio");
  const [companyFilter,setCompanyFilter]=useState("all");
  const [selectedKey,setSelectedKey]=useState("");
  const [selectedInvoiceIds,setSelectedInvoiceIds]=useState<number[]>([]);
  const [templateId,setTemplateId]=useState(0);
  const [recipient,setRecipient]=useState("");
  const [subject,setSubject]=useState("");
  const [content,setContent]=useState("");
  const [editingReminderId,setEditingReminderId]=useState<number|null>(null);
  const [includeAttachments,setIncludeAttachments]=useState(true);
  const [busy,setBusy]=useState(false);
  const [historyMode,setHistoryMode]=useState(false);
  const [statementCustomer,setStatementCustomer]=useState<Customer|null>(null);
  const [agingDetail,setAgingDetail]=useState<AgingBucket|null>(null);
  const [emailSettingsOpen,setEmailSettingsOpen]=useState(false);
  const [emailSettings,setEmailSettings]=useState<EmailSettings|null>(null);
  const [emailSettingsLoading,setEmailSettingsLoading]=useState(false);
  const [testRecipient,setTestRecipient]=useState("");

  const load=async()=>{
    setLoading(true);setError("");
    try{const response=await fetch("/api/treasury/collections",{cache:"no-store"});const result=await response.json();if(!response.ok)throw new Error(result.error||"Chargement impossible");setData(result)}
    catch(loadError){setError(loadError instanceof Error?loadError.message:"Chargement impossible")}
    finally{setLoading(false)}
  };
  useEffect(()=>{const timer=window.setTimeout(()=>setEmbedded(initialEmbedded||new URLSearchParams(window.location.search).get("embedded")==="1"||window.parent!==window),0);return()=>window.clearTimeout(timer)},[initialEmbedded]);
  useEffect(()=>{
    const timer=window.setTimeout(()=>{const params=new URLSearchParams(window.location.search),requestedSection=params.get("section"),requestedFilter=params.get("filter");
      if(requestedSection==="worklist"||requestedSection==="automation"||requestedSection==="reporting")setSection(requestedSection);
      if(requestedFilter==="overdue"||requestedFilter==="current"||requestedFilter==="critical"||requestedFilter==="all"){setSection("portfolio");setFilter(requestedFilter)}
      setNavigationReady(true);},0);
    return()=>window.clearTimeout(timer);
  },[]);
  useEffect(()=>{const timer=window.setTimeout(()=>void load(),0);return()=>window.clearTimeout(timer)},[]);
  useEffect(()=>{if(!toast)return;const timer=window.setTimeout(()=>setToast(""),5000);return()=>window.clearTimeout(timer)},[toast]);

  async function openEmailSettings(){
    setEmailSettingsOpen(true);setEmailSettingsLoading(true);
    try{const response=await fetch("/api/treasury/collections/email-settings",{cache:"no-store"});const result=await response.json();if(!response.ok)throw new Error(result.error||"Configuration inaccessible");setEmailSettings(result);if(!testRecipient)setTestRecipient(result.fromEmail||"")}
    catch(reason){setEmailSettingsOpen(false);setToast(reason instanceof Error?reason.message:"Configuration inaccessible")}
    finally{setEmailSettingsLoading(false)}
  }

  async function saveEmailSettings(event:FormEvent<HTMLFormElement>){
    event.preventDefault();const form=new FormData(event.currentTarget);
    if(!window.confirm("Enregistrer cette configuration et l’utiliser pour les prochaines relances validées ?"))return;
    setBusy(true);try{const response=await fetch("/api/treasury/collections/email-settings",{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({confirmed:true,enabled:form.get("enabled")==="on",fromName:form.get("fromName"),fromEmail:form.get("fromEmail"),apiKey:form.get("apiKey"),apiUrl:form.get("apiUrl")})});const result=await response.json();if(!response.ok)throw new Error(result.error||"Enregistrement impossible");setEmailSettings(result);await load();setToast("Configuration e-mail enregistrée. Effectuez un test avant les envois clients.")}
    catch(reason){setToast(reason instanceof Error?reason.message:"Enregistrement impossible")}finally{setBusy(false)}
  }

  async function testEmailDelivery(){
    if(!window.confirm(`Envoyer maintenant un message de test à ${testRecipient} ?`))return;
    setBusy(true);try{const response=await fetch("/api/treasury/collections/email-settings",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({confirmed:true,recipient:testRecipient})});const result=await response.json();if(!response.ok)throw new Error(result.error||"Test impossible");setToast(result.message);const refreshed=await fetch("/api/treasury/collections/email-settings",{cache:"no-store"});if(refreshed.ok)setEmailSettings(await refreshed.json())}
    catch(reason){setToast(reason instanceof Error?reason.message:"Test impossible")}finally{setBusy(false)}
  }

  const selectedCustomer=data.customers.find(customer=>`${customer.companyId}:${customer.customerKey}`===selectedKey)||null;
  const selectedInvoices=selectedCustomer?.invoices.filter(invoice=>selectedInvoiceIds.includes(invoice.id))||[];
  const selectedTemplate=data.templates.find(template=>template.id===templateId)||null;
  const companyName=(id:string)=>data.companies.find(company=>company.id===id)?.name||id;
  const visibleCustomers=useMemo(()=>data.customers.filter(customer=>{
    if(companyFilter!=="all"&&customer.companyId!==companyFilter)return false;
    if(search&&!`${customer.customerName} ${customer.contact?.email||""}`.toLowerCase().includes(search.toLowerCase()))return false;
    if(filter==="overdue"&&!customer.overdueCount)return false;
    if(filter==="current"&&customer.overdueCount)return false;
    if(filter==="critical"&&customer.riskLevel!=="critical")return false;
    return true;
  }),[data.customers,companyFilter,search,filter]);
  const scopedCustomers=useMemo(()=>data.customers.filter(customer=>companyFilter==="all"||customer.companyId===companyFilter),[data.customers,companyFilter]);
  const worklist=useMemo(()=>scopedCustomers.filter(customer=>customer.overdueAmount>0).sort((left,right)=>{
    const weight:Record<Risk,number>={low:1,medium:2,high:3,critical:4};
    const score=(customer:Customer)=>weight[customer.riskLevel]*1000+Math.min(customer.maxDelay,180)*8+Math.log10(Math.max(10,customer.collectibleAmount))*80+customer.brokenPromises*900-customer.openDisputes*120;
    return score(right)-score(left)||right.collectibleAmount-left.collectibleAmount;
  }),[scopedCustomers]);
  const reporting=useMemo(()=>{
    const overdue=scopedCustomers.filter(customer=>customer.overdueAmount>0),reminders=scopedCustomers.flatMap(customer=>customer.reminders);
    const aging={current:0,days1to30:0,days31to60:0,days61to90:0,days90plus:0};
    for(const customer of scopedCustomers)for(const invoice of customer.invoices)aging[invoiceAgingBucket(invoice)]+=Math.abs(Number(invoice.amount));
    const total=Object.values(aging).reduce((sum,value)=>sum+value,0),overdueTotal=aging.days1to30+aging.days31to60+aging.days61to90+aging.days90plus;
    const weightedDelay=overdue.reduce((sum,customer)=>sum+customer.overdueAmount*customer.maxDelay,0);
    const contacted=overdue.filter(customer=>customer.reminders.length>0).length,contactable=overdue.filter(customer=>Boolean(customer.contact?.email)).length;
    const collectible=overdue.reduce((sum,customer)=>sum+customer.collectibleAmount,0),promised=overdue.reduce((sum,customer)=>sum+customer.promisedAmount,0),disputed=overdue.reduce((sum,customer)=>sum+customer.disputedAmount,0),collected30d=scopedCustomers.reduce((sum,customer)=>sum+customer.collected30d,0);
    const promiseCustomers=overdue.filter(customer=>customer.promisedAmount>0).length,brokenPromises=overdue.reduce((sum,customer)=>sum+customer.brokenPromises,0);
    const scenarioPerformance=(Object.keys(riskCopy) as Risk[]).map(risk=>{const customers=overdue.filter(customer=>customer.riskLevel===risk),actions=customers.flatMap(customer=>customer.reminders);return{risk,customers:customers.length,amount:customers.reduce((sum,customer)=>sum+customer.collectibleAmount,0),sent:actions.filter(action=>action.status==="sent").length,failed:actions.filter(action=>action.status==="failed").length,promises:customers.filter(customer=>customer.promisedAmount>0).length}});
    return{aging,total,overdueTotal,collectible,promised,disputed,collected30d,promiseCustomers,brokenPromises,scenarioPerformance,overdueRatio:total?overdueTotal/total*100:0,averageDelay:overdueTotal?weightedDelay/overdueTotal:0,contactCoverage:overdue.length?contacted/overdue.length*100:0,contactableCoverage:overdue.length?contactable/overdue.length*100:0,promiseCoverage:overdue.length?promiseCustomers/overdue.length*100:0,reminderCount:reminders.length,sentCount:reminders.filter(item=>item.status==="sent").length,queuedCount:reminders.filter(item=>item.status==="queued"||item.status==="draft").length,failedCount:reminders.filter(item=>item.status==="failed").length};
  },[scopedCustomers]);
  const agingDetailRows=useMemo(()=>agingDetail?scopedCustomers.flatMap(customer=>customer.invoices.filter(invoice=>invoiceAgingBucket(invoice)===agingDetail).map(invoice=>({company:data.companies.find(company=>company.id===customer.companyId)?.name||customer.companyId,customer:customer.customerName,reference:invoice.reference,invoiceDate:invoice.invoiceDate||"",dueDate:invoice.dueDate,delayDays:invoice.isOverdue?invoice.delayDays:0,amount:Number(invoice.amount)}))).sort((left,right)=>right.delayDays-left.delayDays||Math.abs(right.amount)-Math.abs(left.amount)):[],[agingDetail,scopedCustomers,data.companies]);

  function openCustomer(customer:Customer){
    setSection("portfolio");
    setSelectedKey(`${customer.companyId}:${customer.customerKey}`);setHistoryMode(false);
    setEditingReminderId(null);
    const defaultInvoices=customer.invoices.filter(invoice=>invoice.isOverdue);
    setSelectedInvoiceIds((defaultInvoices.length?defaultInvoices:customer.invoices).map(invoice=>invoice.id));
    const preferred=data.templates.find(template=>template.alertLevel===customer.riskLevel)||data.templates[0];
    setTemplateId(preferred?.id||0);setRecipient(customer.contact?.email||"");
    const selected=defaultInvoices.length?defaultInvoices:customer.invoices;
    if(preferred){setSubject(fillTemplate(preferred.subjectTemplate,customer,companyName(customer.companyId),selected));setContent(fillReminderTemplate(preferred.bodyTemplate,customer,companyName(customer.companyId),selected))}
  }

  function templateForCustomer(customer:Customer){return data.templates.find(template=>template.alertLevel===customer.riskLevel)||data.templates[0]}

  function saveWorkbook(sheet:XLSX.WorkSheet,name:string,fileName:string,widths:number[]){
    sheet["!cols"]=widths.map(wch=>({wch}));
    const book=XLSX.utils.book_new();XLSX.utils.book_append_sheet(book,sheet,name);XLSX.writeFile(book,fileName);
  }

  function exportReporting(){
    const rows=scopedCustomers.map(customer=>({Client:customer.customerName,Société:companyName(customer.companyId),"Encours total (MAD)":customer.totalDue,"Échu brut (MAD)":customer.overdueAmount,"Recouvrable maintenant (MAD)":customer.collectibleAmount,"Promis (MAD)":customer.promisedAmount,"Litiges (MAD)":customer.disputedAmount,"Encaissé 30 j (MAD)":customer.collected30d,"Promesses rompues":customer.brokenPromises,"Retard max (jours)":customer.maxDelay,Risque:riskCopy[customer.riskLevel].label,"Dernière relance":dateFr(customer.lastReminderAt),"Nombre de relances":customer.reminders.length}));
    saveWorkbook(XLSX.utils.json_to_sheet(rows),"Reporting",`moventra-reporting-recouvrement-${new Date().toISOString().slice(0,10)}.xlsx`,[28,28,19,19,25,17,17,19,18,19,14,18,20]);setToast("Reporting recouvrement téléchargé au format Excel");
  }

  function exportStatementExcel(customer:Customer){
    const rows=customer.invoices.map(invoice=>({Facture:invoice.reference,"Date facture":dateFr(invoice.invoiceDate),Échéance:dateFr(invoice.dueDate),Situation:invoice.isOverdue?`Échue · ${invoice.delayDays} j`:"En cours","Montant restant dû (MAD)":Number(invoice.amount)}));
    const sheet=XLSX.utils.aoa_to_sheet([["MOVENTRA ERP · ÉTAT DE COMPTE CLIENT"],[companyName(customer.companyId)],[customer.customerName],[`Édité le ${dateFr(new Date().toISOString())}`],[],["Encours total",customer.totalDue,"Dont échu",customer.overdueAmount,"Non échu",customer.currentAmount]]);XLSX.utils.sheet_add_json(sheet,rows,{origin:"A8"});
    saveWorkbook(sheet,"État de compte",`etat-compte-${customer.customerName.replace(/[^a-z0-9]+/gi,"-").toLowerCase()}-${new Date().toISOString().slice(0,10)}.xlsx`,[22,18,18,24,24,16]);setToast("État de compte téléchargé au format Excel");
  }

  function exportAgingDetail(){
    if(!agingDetail)return;const rows=agingDetailRows.map(row=>({Société:row.company,Client:row.customer,Facture:row.reference,"Date facture":dateFr(row.invoiceDate),Échéance:dateFr(row.dueDate),"Retard (jours)":row.delayDays,"Montant restant dû (MAD)":row.amount}));
    saveWorkbook(XLSX.utils.json_to_sheet(rows),"Détail balance âgée",`balance-agee-${agingDetail}-${new Date().toISOString().slice(0,10)}.xlsx`,[28,30,18,16,16,16,24]);setToast(`Détail « ${agingLabels[agingDetail]} » téléchargé au format Excel`);
  }

  function printCollectionDocument(mode:"printing-collection-statement"|"printing-aging-detail"|"printing-collections-reporting"){
    const printModes=["printing-collection-statement","printing-aging-detail","printing-collections-reporting"];
    const cleanup=()=>printModes.forEach(className=>document.body.classList.remove(className));
    cleanup();document.body.classList.add(mode);window.addEventListener("afterprint",cleanup,{once:true});
    window.requestAnimationFrame(()=>window.requestAnimationFrame(()=>window.print()));
  }

  function startRecommendedReminders(){
    const eligible=worklist.filter(customer=>customer.contact?.email&&templateForCustomer(customer));
    if(!eligible.length){setToast("Aucun client éligible : renseignez d’abord les adresses e-mail.");return}
    openCustomer(eligible[0]);
    setToast(`Relance de ${eligible[0].customerName} prête à vérifier et à transmettre.`);
  }

  function applyTemplate(nextId:number,invoiceIds=selectedInvoiceIds){
    setTemplateId(nextId);if(!selectedCustomer)return;
    const template=data.templates.find(item=>item.id===nextId);if(!template)return;
    const invoices=selectedCustomer.invoices.filter(invoice=>invoiceIds.includes(invoice.id));
    setSubject(fillTemplate(template.subjectTemplate,selectedCustomer,companyName(selectedCustomer.companyId),invoices));
    setContent(fillReminderTemplate(template.bodyTemplate,selectedCustomer,companyName(selectedCustomer.companyId),invoices));
  }

  function toggleInvoice(id:number){
    const next=selectedInvoiceIds.includes(id)?selectedInvoiceIds.filter(item=>item!==id):[...selectedInvoiceIds,id];
    setSelectedInvoiceIds(next);if(templateId)applyTemplate(templateId,next);
  }

  async function saveContact(event:FormEvent<HTMLFormElement>){
    event.preventDefault();if(!selectedCustomer)return;const form=new FormData(event.currentTarget);
    if(!window.confirm(`Enregistrer les coordonnées et le niveau de risque de ${selectedCustomer.customerName} ?`))return;
    setBusy(true);
    try{const response=await fetch("/api/treasury/collections",{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify({action:"contact",confirmed:true,companyId:selectedCustomer.companyId,customerName:selectedCustomer.customerName,email:form.get("email"),phone:form.get("phone"),contactName:form.get("contactName"),ownerName:form.get("ownerName"),riskOverride:form.get("riskOverride")||null,notes:form.get("notes")})});const result=await response.json();if(!response.ok)throw new Error(result.error);await load();setRecipient(String(form.get("email")||""));setToast("Fiche client mise à jour")}
    catch(saveError){setToast(saveError instanceof Error?saveError.message:"Enregistrement impossible")}finally{setBusy(false)}
  }

  async function sendReminder(event:FormEvent<HTMLFormElement>){
    event.preventDefault();if(!selectedCustomer||!selectedTemplate||!selectedInvoices.length)return;
    const amount=selectedInvoices.reduce((sum,invoice)=>sum+Number(invoice.amount),0);
    if(!window.confirm(`Confirmer l’envoi de la relance « ${selectedTemplate.name} » à ${recipient} ?\n\n${selectedInvoices.length} facture(s) · ${money(amount)}\n\nAucun envoi ne sera effectué sans cette confirmation.`))return;
    setBusy(true);
    try{const response=await fetch("/api/treasury/collections",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({action:editingReminderId?"send_existing":undefined,reminderId:editingReminderId,confirmed:true,companyId:selectedCustomer.companyId,customerName:selectedCustomer.customerName,recipient,subject,content,templateId:selectedTemplate.id,alertLevel:selectedTemplate.alertLevel,invoiceIds:selectedInvoiceIds,includeAttachments})});const result=await response.json();if(!response.ok&&response.status!==202)throw new Error(result.error||"Envoi impossible");await load();setEditingReminderId(null);setToast(result.delivery==="sent"?"Relance envoyée et classée dans l’historique":result.warning||"Relance validée et conservée dans la file d’attente");setHistoryMode(true)}
    catch(sendError){setToast(sendError instanceof Error?sendError.message:"Envoi impossible")}finally{setBusy(false)}
  }

  function reviewReminder(reminder:Reminder){
    if(!selectedCustomer)return;
    const fallback=templateForCustomer(selectedCustomer);
    setEditingReminderId(reminder.id);setTemplateId(reminder.templateId||fallback?.id||0);setRecipient(reminder.recipient||selectedCustomer.contact?.email||"");setSubject(reminder.subject||"");setContent(formatCollectionEmailContent(reminder.content));setSelectedInvoiceIds(reminder.invoiceIds);setHistoryMode(false);setToast("Relance chargée : vérifiez-la avant de la transmettre.");
  }

  async function transmitExistingReminder(reminder:Reminder){
    if(!selectedCustomer)return;
    if(!window.confirm(`Valider et transmettre cette relance à ${reminder.recipient||selectedCustomer.contact?.email||"l’adresse renseignée"} ?\n\nElle sera ensuite classée dans l’historique.`))return;
    setBusy(true);try{const response=await fetch("/api/treasury/collections",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({action:"send_existing",reminderId:reminder.id,confirmed:true,includeAttachments:true})});const result=await response.json();if(!response.ok&&response.status!==202)throw new Error(result.error||"Transmission impossible");await load();setToast(result.delivery==="sent"?"Relance transmise et classée dans l’historique":result.warning||"Relance conservée en attente de transmission")}catch(reason){setToast(reason instanceof Error?reason.message:"Transmission impossible")}finally{setBusy(false)}
  }

  async function deleteReminder(reminder:Reminder){
    if(!window.confirm(`Supprimer définitivement cette relance ${reminder.status==="draft"?"en brouillon":"non transmise"} ?\n\nCette action ne peut pas être annulée.`))return;
    setBusy(true);try{const response=await fetch("/api/treasury/collections",{method:"DELETE",headers:{"content-type":"application/json"},body:JSON.stringify({confirmed:true,reminderId:reminder.id})});const result=await response.json();if(!response.ok)throw new Error(result.error||"Suppression impossible");if(editingReminderId===reminder.id)setEditingReminderId(null);await load();setToast("Relance supprimée avant transmission")}catch(reason){setToast(reason instanceof Error?reason.message:"Suppression impossible")}finally{setBusy(false)}
  }

  async function logAction(event:FormEvent<HTMLFormElement>){
    event.preventDefault();if(!selectedCustomer)return;const form=new FormData(event.currentTarget);const channel=String(form.get("channel"));const note=String(form.get("content")||"");
    if(!window.confirm(`Ajouter ce ${channel==="phone"?"compte rendu d’appel":"suivi interne"} à l’historique de ${selectedCustomer.customerName} ?`))return;
    setBusy(true);try{const response=await fetch("/api/treasury/collections",{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify({action:"log",confirmed:true,companyId:selectedCustomer.companyId,customerName:selectedCustomer.customerName,alertLevel:selectedCustomer.riskLevel,channel,content:note})});const result=await response.json();if(!response.ok)throw new Error(result.error);event.currentTarget.reset();await load();setToast("Action ajoutée au journal") }catch(logError){setToast(logError instanceof Error?logError.message:"Journalisation impossible")}finally{setBusy(false)}
  }

  async function saveCase(event:FormEvent<HTMLFormElement>){
    event.preventDefault();if(!selectedCustomer)return;const form=new FormData(event.currentTarget),caseType=String(form.get("caseType"));
    const label=caseType==="promise"?"la promesse de paiement":"le litige";
    if(!window.confirm(`Enregistrer ${label} pour ${selectedCustomer.customerName} ?\n\nCette action sera tracée dans le dossier client.`))return;
    setBusy(true);try{const response=await fetch("/api/treasury/collections",{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify({action:"case",confirmed:true,companyId:selectedCustomer.companyId,customerName:selectedCustomer.customerName,caseType,amount:form.get("amount"),dueDate:form.get("dueDate"),reason:form.get("reason"),ownerName:form.get("ownerName"),category:form.get("category"),cashFlowId:selectedInvoiceIds.length===1?selectedInvoiceIds[0]:null})});const result=await response.json();if(!response.ok)throw new Error(result.error);event.currentTarget.reset();await load();setToast(caseType==="promise"?"Promesse de paiement enregistrée":"Litige ouvert et relances automatiques suspendues")}catch(caseError){setToast(caseError instanceof Error?caseError.message:"Enregistrement impossible")}finally{setBusy(false)}
  }

  async function updateCaseStatus(item:CollectionCase,status:"kept"|"broken"|"resolved"|"cancelled"){
    if(!selectedCustomer)return;const label=status==="kept"?"honorée":status==="broken"?"rompue":status==="resolved"?"résolu":"annulé";
    if(!window.confirm(`Confirmer que ${item.caseType==="promise"?"la promesse est ":"le litige est "}${label} ?`))return;
    setBusy(true);try{const response=await fetch("/api/treasury/collections",{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify({action:"case_status",confirmed:true,companyId:selectedCustomer.companyId,customerName:selectedCustomer.customerName,id:item.id,status})});const result=await response.json();if(!response.ok)throw new Error(result.error);await load();setToast("Statut du dossier mis à jour")}catch(caseError){setToast(caseError instanceof Error?caseError.message:"Mise à jour impossible")}finally{setBusy(false)}
  }

  async function uploadDocument(invoice:Invoice,file?:File){
    if(!file)return;if(!window.confirm(`Joindre « ${file.name} » à la facture ${invoice.reference} ?`))return;
    const form=new FormData();form.set("confirmed","true");form.set("cashFlowId",String(invoice.id));form.set("file",file);setBusy(true);
    try{const response=await fetch("/api/treasury/collections/documents",{method:"POST",body:form});const result=await response.json();if(!response.ok)throw new Error(result.error);await load();setToast("Document joint à la facture") }catch(uploadError){setToast(uploadError instanceof Error?uploadError.message:"Ajout impossible")}finally{setBusy(false)}
  }

  async function removeDocument(document:DocumentRow){
    if(!window.confirm(`Supprimer définitivement la pièce jointe « ${document.fileName} » ?`))return;
    setBusy(true);try{const response=await fetch("/api/treasury/collections/documents",{method:"DELETE",headers:{"content-type":"application/json"},body:JSON.stringify({id:document.id,confirmed:true})});const result=await response.json();if(!response.ok)throw new Error(result.error);await load();setToast("Pièce jointe supprimée") }catch(deleteError){setToast(deleteError instanceof Error?deleteError.message:"Suppression impossible")}finally{setBusy(false)}
  }

  const agingMax=Math.max(1,reporting.aging.current,reporting.aging.days1to30,reporting.aging.days31to60,reporting.aging.days61to90,reporting.aging.days90plus);
  const agingBuckets:Array<{key:AgingBucket;value:number;tone:string}>=[{key:"current",value:reporting.aging.current,tone:"current"},{key:"days1to30",value:reporting.aging.days1to30,tone:"medium"},{key:"days31to60",value:reporting.aging.days31to60,tone:"high"},{key:"days61to90",value:reporting.aging.days61to90,tone:"critical"},{key:"days90plus",value:reporting.aging.days90plus,tone:"critical"}];
  const worklistPanel=<section className="collection-worklist-card">
    <header><div><p>WORKLIST PRIORISÉE</p><h2>Prochaines actions recommandées</h2><span>Score professionnel : risque, ancienneté, montant recouvrable, promesses rompues et litiges.</span></div><button className="primary" disabled={busy||!worklist.some(customer=>Boolean(customer.contact?.email)&&Boolean(templateForCustomer(customer)))} onClick={startRecommendedReminders}>Commencer les relances à valider</button></header>
    <div className={`collection-delivery-status ${data.emailDeliveryConfigured?"ready":"waiting"}`}>{data.emailDeliveryConfigured?<><b>✓ Envoi e-mail opérationnel</b><span>Après votre confirmation, le message est transmis puis classé automatiquement dans l’historique du client.</span></>:<><b>! Transport e-mail à configurer</b><span>Les validations sont conservées « En attente » jusqu’à l’activation du compte d’envoi.</span></>}<button type="button" onClick={()=>void openEmailSettings()}>{data.emailDeliveryConfigured?"Gérer le compte":"Configurer l’envoi"}</button></div>
    <div className="workflow-rules"><span><b>J−7</b>Préventive</span><i>→</i><span><b>J+1</b>Amiable</span><i>→</i><span><b>J+15</b>Ferme</span><i>→</i><span><b>J+45</b>Mise en demeure</span><i>→</i><span><b>PTP</b>Promesse surveillée</span></div>
    <div className="worklist-head"><span>Priorité / client</span><span>À recouvrer</span><span>Retard</span><span>Action recommandée</span><span>État du dossier</span><span></span></div>
    {loading?<p className="collection-empty">Calcul de la worklist…</p>:worklist.length===0?<p className="collection-empty">Aucune créance échue dans cette sélection.</p>:worklist.map((customer,index)=>{const template=templateForCustomer(customer),lastReminder=customer.reminders.find(reminder=>reminder.channel==="email"&&(reminder.status==="sent"||reminder.status==="queued"||reminder.status==="failed"));return <button className="worklist-row" key={`${customer.companyId}:${customer.customerKey}`} onClick={()=>openCustomer(customer)}><span><em>{index+1}</em><p><b>{customer.customerName}</b><small>{companyName(customer.companyId)} · risque {riskCopy[customer.riskLevel].label}</small></p></span><strong>{money(customer.collectibleAmount)}</strong><span><b>{customer.maxDelay} j</b><small>{customer.overdueCount} facture(s)</small></span><span><b>{customer.brokenPromises?"Promesse rompue · action immédiate":template?.name||"Analyse manuelle"}</b><small>{lastReminder?`${lastReminder.status==="sent"?"Dernière relance envoyée":lastReminder.status==="failed"?"Dernier envoi en échec":"Dernière relance en attente"} · ${dateFr(lastReminder.sentAt||lastReminder.createdAt)}`:customer.promisedAmount?`${money(customer.promisedAmount)} promis`:customer.contact?.email?"Relance prête à vérifier":"Prochaine étape recommandée"}</small></span><span className={customer.brokenPromises?"case-alert":customer.openDisputes?"case-warning":customer.contact?.email?"contact-ready":"contact-missing"}>{customer.brokenPromises?<><b>! {customer.brokenPromises} promesse(s) rompue(s)</b><small>Remonter en priorité</small></>:customer.openDisputes?<><b>◆ {customer.openDisputes} litige(s)</b><small>{money(customer.disputedAmount)} suspendus</small></>:customer.contact?.email?<><b>✓ Contactable</b><small>{customer.contact.email}</small></>:<><b>! E-mail requis</b><small>Compléter la fiche client</small></>}</span><i>›</i></button>})}
  </section>;
  const reportingPanel=<section className="collection-reporting">
    <article className="reporting-policy-map">
      <header><div><p>POLITIQUE MÉTIER ACTIVE</p><h2>Cycle standard de recouvrement</h2><span>Un parcours lisible, contrôlé et traçable de l’échéance à l’encaissement.</span></div><button type="button" onClick={()=>setSection("automation")}>Paramétrer les règles →</button></header>
      <div className="reporting-policy-steps"><section><em>01</em><p><b>Prévenir</b><small>J−7 · rappel courtois avant échéance</small></p></section><i>›</i><section><em>02</em><p><b>Relancer</b><small>J+1 amiable · J+15 ferme</small></p></section><i>›</i><section><em>03</em><p><b>Sécuriser</b><small>Promesse, litige et responsable affecté</small></p></section><i>›</i><section><em>04</em><p><b>Escalader</b><small>J+45 · mise en demeure contrôlée</small></p></section></div>
      <footer><span><b>Exclusions automatiques :</b> litiges ouverts, comptes VIP et factures suspendues.</span><span><b>Validation humaine :</b> obligatoire avant tout envoi critique.</span></footer>
    </article>
    <header className="reporting-section-title"><div><p>PILOTAGE OPÉRATIONNEL</p><h2>Décisions du jour</h2></div><span>Indicateurs calculés sur la société sélectionnée</span></header>
    <div className="reporting-metrics reporting-metrics-six"><article><span>Recouvrable maintenant</span><b>{shortMoney(reporting.collectible)}</b><small>Échu hors promesses et litiges</small></article><article><span>Encaissé sur 30 jours</span><b>{shortMoney(reporting.collected30d)}</b><small>Crédits clients réellement comptabilisés</small></article><article><span>Promesses actives</span><b>{shortMoney(reporting.promised)}</b><small>{reporting.promiseCoverage.toFixed(0)} % des comptes échus engagés</small></article><article><span>Montants en litige</span><b>{shortMoney(reporting.disputed)}</b><small>Exclus de l’action immédiate</small></article><article><span>Retard moyen pondéré</span><b>{reporting.averageDelay.toFixed(0)} j</b><small>Mesure opérationnelle du retard</small></article><article className={reporting.brokenPromises?"metric-alert":""}><span>Promesses rompues</span><b>{reporting.brokenPromises}</b><small>À remonter en tête de worklist</small></article></div>
    <div className="reporting-grid"><article className="aging-report"><header><div><p>ANCIENNETÉ DES CRÉANCES</p><h2>Balance âgée</h2></div><strong>{money(reporting.total)}</strong></header><div className="aging-bars">{agingBuckets.map(bucket=><button type="button" key={bucket.key} onClick={()=>setAgingDetail(bucket.key)} aria-label={`Afficher le détail ${agingLabels[bucket.key]}`}><span><b>{agingLabels[bucket.key]}</b><strong>{money(bucket.value)}</strong></span><i><em className={bucket.tone} style={{width:`${bucket.value/agingMax*100}%`}}/></i><small>{reporting.total?`${(bucket.value/reporting.total*100).toFixed(1)} % de l’encours` : "0 %"}<em>Voir le détail →</em></small></button>)}</div></article><article className="activity-report"><header><p>PERFORMANCE DES RELANCES</p><h2>Entonnoir opérationnel</h2></header><div><span><b>{scopedCustomers.filter(customer=>customer.overdueAmount>0).length}</b><small>Comptes à traiter</small></span><span><b>{reporting.contactCoverage.toFixed(0)} %</b><small>Couverture d’action</small></span><span><b>{reporting.promiseCustomers}</b><small>Clients engagés</small></span><span><b>{reporting.collected30d?money(reporting.collected30d):"0 MAD"}</b><small>Encaissements 30 j</small></span></div><p><b>Qualité des opérations</b><span>{reporting.sentCount} e-mail(s) envoyé(s), {reporting.queuedCount} brouillon(s) ou attente(s), {reporting.failedCount} échec(s) à corriger.</span></p></article></div>
    <article className="scenario-performance"><header><p>EFFICACITÉ PAR SCÉNARIO</p><h2>Relances, engagements et risque</h2></header><div className="scenario-head"><span>Niveau</span><span>Clients</span><span>À recouvrer</span><span>E-mails envoyés</span><span>Promesses</span><span>Échecs</span></div>{reporting.scenarioPerformance.map(row=><div className="scenario-row" key={row.risk}><span><i className={`risk-dot ${row.risk}`}/><b>{riskCopy[row.risk].label}</b></span><strong>{row.customers}</strong><strong>{money(row.amount)}</strong><strong>{row.sent}</strong><strong>{row.promises}</strong><strong className={row.failed?"failure-number":""}>{row.failed}</strong></div>)}</article>
    <article className="benchmark-principles"><header><p>PROCESSUS PROFESSIONNEL INTÉGRÉ</p><h2>Du risque à l’encaissement</h2></header><div><span><b>1. Prioriser</b><small>Score, retard, promesse rompue et montant net</small></span><span><b>2. Agir</b><small>Scénario contextualisé avec validation humaine</small></span><span><b>3. Engager</b><small>Promesse datée ou litige affecté à un responsable</small></span><span><b>4. Mesurer</b><small>Encaissements, couverture et performance par scénario</small></span></div></article>
  </section>;
  useEffect(()=>{if(navigationReady&&embedded&&window.parent!==window)window.parent.postMessage({type:"moventra:financial-navigation",module:"collections",section,filter:section==="portfolio"?filter:"all"},window.location.origin)},[embedded,filter,navigationReady,section]);
  function exitCollections(){
    if(section!=="portfolio"){setSection("portfolio");setSelectedKey("");setHistoryMode(false);return}
    if(selectedCustomer){setSelectedKey("");setHistoryMode(false);return}
    if(embedded&&window.parent!==window){window.parent.postMessage({type:"moventra:exit-module",module:"collections"},window.location.origin);return}
    window.location.assign("/treasury");
  }

  return <div className={`collections-shell ${embedded?"is-embedded":""}`}>
    <aside className="collections-sidebar">
      <div className="collections-brand"><Image src="/brand/moventra-mark.png" alt="Moventra ERP" width={44} height={44}/><div><b>Recouvrement</b><small>Espace financier sécurisé</small></div></div>
      <Link className="collections-back" href="/">← Retour Moventra ERP</Link>
      <p className="collections-nav-label">Pilotage</p>
      <nav aria-label="Navigation recouvrement">
        <button className={section==="portfolio"&&filter==="all"?"active":""} onClick={()=>{setSection("portfolio");setFilter("all")}}><span>⌂</span>Vue d’ensemble<em>{data.customers.length}</em></button>
        <button className={section==="portfolio"&&filter==="overdue"?"active":""} onClick={()=>{setSection("portfolio");setFilter("overdue")}}><span>!</span>Factures échues<em>{data.metrics.overdueInvoices}</em></button>
        <button className={section==="portfolio"&&filter==="current"?"active":""} onClick={()=>{setSection("portfolio");setFilter("current")}}><span>◷</span>Factures en cours</button>
        <button className={section==="portfolio"&&filter==="critical"?"active":""} onClick={()=>{setSection("portfolio");setFilter("critical")}}><span>◆</span>Risque critique<em>{data.metrics.criticalCustomers}</em></button>
      </nav>
      <p className="collections-nav-label collections-nav-separator">Actions clients</p>
      <nav aria-label="Relances et reporting">
        <button className={section==="worklist"?"active":""} onClick={()=>setSection("worklist")}><span>↗</span>Relances recommandées<em>{worklist.length}</em></button>
        <button className={section==="automation"?"active":""} onClick={()=>setSection("automation")}><span>◆</span>Automatisation AR</button>
        <button className={section==="reporting"?"active":""} onClick={()=>setSection("reporting")}><span>↧</span>Reporting recouvrement</button>
      </nav>
      <div className="collections-sidebar-note"><b><i/> Moteur de relance actif</b><span>J−7 préventive · J+1 amiable<br/>J+15 ferme · J+45 mise en demeure</span></div>
      <div className="collections-user"><span>AT</span><p><b>Utilisateur connecté</b><small>Accès recouvrement renforcé</small></p></div>
    </aside>
    <main className={`collections-main section-${section}`}>
      <header className="collections-topbar"><div><p>TRÉSORERIE / RECOUVREMENT</p><h1>{section==="worklist"?"Relances clients recommandées":section==="automation"?"Automatisation Accounts Receivable":section==="reporting"?"Reporting du recouvrement":"Pilotage des créances clients"}</h1><span>{section==="worklist"?"Worklist priorisée selon le retard, le risque et le montant exigible.":section==="automation"?"Scénarios multicanaux, tâches, risque, portail et prévisions en un seul cockpit.":section==="reporting"?"Ancienneté des créances, couverture des actions et efficacité des relances.":"Segmentation des échéances, relances contextualisées et traçabilité complète."}</span></div><div className="collections-top-actions"><label>Société<select value={companyFilter} onChange={event=>setCompanyFilter(event.target.value)}><option value="all">Vue consolidée</option>{data.companies.map(company=><option value={company.id} key={company.id}>{company.name}</option>)}</select></label>{section==="worklist"&&<button onClick={()=>void openEmailSettings()}>⚙ Configuration e-mail</button>}{section==="reporting"&&<><button onClick={exportReporting}>↧ Exporter Excel</button><button onClick={()=>printCollectionDocument("printing-collections-reporting")}>▧ Imprimer le reporting</button></>}<button onClick={exitCollections}>← Sortir</button></div></header>
      {error&&<div className="collections-error" role="alert"><b>Le portefeuille n’a pas pu être chargé.</b><span>{error}</span><button onClick={()=>void load()}>Réessayer</button></div>}
      <section className="collection-kpis" aria-label="Indicateurs de recouvrement">
        <article><span>Encours total</span><b>{shortMoney(data.metrics.totalDue)}</b><small>{data.customers.length} comptes clients ouverts</small></article>
        <article className="late"><span>Créances échues</span><b>{shortMoney(data.metrics.overdueTotal)}</b><small>Impact immédiat sur le BFR</small></article>
        <article className="current"><span>Factures en cours</span><b>{shortMoney(data.metrics.currentTotal)}</b><small>Non échues à ce jour</small></article>
        <article className="risk"><span>Risque critique</span><b>{data.metrics.criticalCustomers}</b><small>Clients à traiter en priorité</small></article>
      </section>
      {section==="worklist"?worklistPanel:section==="automation"?<EnterpriseCollectionsCenter companyFilter={companyFilter}/>:section==="reporting"?reportingPanel:<section className="collections-workspace">
        <div className="collections-list-card">
          <div className="collections-list-head"><div><h2>Portefeuille de créances</h2><p>{visibleCustomers.length} client(s) dans la sélection</p></div><label className="collections-search"><span>⌕</span><input value={search} onChange={event=>setSearch(event.target.value)} placeholder="Rechercher un client ou un e-mail"/></label></div>
          <div className="collection-segments" role="tablist"><button className={filter==="all"?"active":""} onClick={()=>setFilter("all")}>Tous</button><button className={filter==="overdue"?"active":""} onClick={()=>setFilter("overdue")}>Échues</button><button className={filter==="current"?"active":""} onClick={()=>setFilter("current")}>En cours</button><button className={filter==="critical"?"active":""} onClick={()=>setFilter("critical")}>Critiques</button></div>
          <div className="collection-table"><div className="collection-table-head"><span>Client / société</span><span>Échu</span><span>En cours</span><span>Risque</span><span>Dernière relance</span><span></span></div>
            {loading?<div className="collection-empty">Chargement du portefeuille…</div>:visibleCustomers.length===0?<div className="collection-empty">Aucun client ne correspond à ces filtres.</div>:visibleCustomers.map(customer=><button className={`collection-row ${selectedKey===`${customer.companyId}:${customer.customerKey}`?"selected":""}`} key={`${customer.companyId}:${customer.customerKey}`} onClick={()=>openCustomer(customer)}><span><b>{customer.customerName}</b><small>{companyName(customer.companyId)} · {customer.invoiceCount} facture(s)</small></span><strong className={customer.overdueAmount?"amount-late":""}>{money(customer.overdueAmount)}</strong><strong>{money(customer.currentAmount)}</strong><em className={`risk-badge ${customer.riskLevel}`}>{riskCopy[customer.riskLevel].label}</em><time>{dateFr(customer.lastReminderAt)}</time><i>›</i></button>)}
          </div>
        </div>
        <aside className={`collection-detail ${selectedCustomer?"open":""}`} aria-label="Fiche client">
          {!selectedCustomer?<div className="collection-detail-empty"><span>↗</span><h2>Sélectionnez un client</h2><p>Consultez ses factures, préparez une relance et retrouvez tout l’historique.</p></div>:<>
            <div className="detail-head"><div><p>{companyName(selectedCustomer.companyId)}</p><h2>{selectedCustomer.customerName}</h2><span>{money(selectedCustomer.totalDue)} d’encours · retard maximal {selectedCustomer.maxDelay} j</span></div><button aria-label="Fermer la fiche client" onClick={()=>setSelectedKey("")}>×</button></div>
            <div className="detail-tabs"><button className={!historyMode?"active":""} onClick={()=>setHistoryMode(false)}>Action de recouvrement</button><button className={historyMode?"active":""} onClick={()=>setHistoryMode(true)}>Historique <em>{selectedCustomer.reminders.length}</em></button></div>
            {!historyMode?<div className="detail-scroll">
              <section className="customer-risk-strip"><span className={`risk-dot ${selectedCustomer.riskLevel}`}/><div><b>Risque {riskCopy[selectedCustomer.riskLevel].label}</b><small>{riskCopy[selectedCustomer.riskLevel].hint}</small></div><button onClick={()=>setStatementCustomer(selectedCustomer)}>Aperçu état de compte</button></section>
              <section className="collectible-summary"><div><span>Échu brut</span><b>{money(selectedCustomer.overdueAmount)}</b></div><div><span>Promis</span><b>{money(selectedCustomer.promisedAmount)}</b></div><div><span>Litiges</span><b>{money(selectedCustomer.disputedAmount)}</b></div><div className="net"><span>À recouvrer</span><b>{money(selectedCustomer.collectibleAmount)}</b></div></section>
              <details className="collection-accordion collection-cases">
                <summary>Promesses & litiges <span>{selectedCustomer.cases.filter(item=>item.effectiveStatus==="open"||item.effectiveStatus==="broken").length} dossier(s) actif(s)</span></summary>
                <div className="case-list">{selectedCustomer.cases.length===0?<p>Aucun engagement ni litige enregistré.</p>:selectedCustomer.cases.map(item=><article key={item.id} className={item.effectiveStatus==="broken"?"broken":""}><div><span>{item.caseType==="promise"?"Promesse de paiement":"Litige client"}</span><b>{money(item.amount)}</b><small>{item.caseType==="promise"?`Échéance ${dateFr(item.dueDate)}`:item.ownerName?`Responsable : ${item.ownerName}`:"Responsable à affecter"} · {item.reason}</small></div><em>{item.effectiveStatus==="open"?"Actif":item.effectiveStatus==="broken"?"Rompue":item.effectiveStatus==="kept"?"Honorée":item.effectiveStatus==="resolved"?"Résolu":"Annulé"}</em>{(item.effectiveStatus==="open"||item.effectiveStatus==="broken")&&<nav>{item.caseType==="promise"&&item.effectiveStatus==="open"&&<button type="button" onClick={()=>void updateCaseStatus(item,"kept")}>Honorée</button>}{item.caseType==="promise"&&<button type="button" onClick={()=>void updateCaseStatus(item,"broken")}>Rompue</button>}{item.caseType==="dispute"&&<button type="button" onClick={()=>void updateCaseStatus(item,"resolved")}>Résolu</button>}<button type="button" onClick={()=>void updateCaseStatus(item,"cancelled")}>Annuler</button></nav>}</article>)}</div>
                <form className="case-form" onSubmit={saveCase}><label>Type<select name="caseType" defaultValue="promise"><option value="promise">Promesse de paiement</option><option value="dispute">Litige client</option></select></label><label>Montant (MAD)<input name="amount" type="number" min="0.01" step="0.01" required/></label><label>Date promise / revue<input name="dueDate" type="date"/></label><label>Responsable<input name="ownerName" defaultValue={selectedCustomer.contact?.ownerName||""} placeholder="Gestionnaire du dossier"/></label><label>Cause du litige<select name="category" defaultValue="other"><option value="missing_goods">Marchandise manquante</option><option value="pricing_error">Erreur de prix</option><option value="incorrect_po">Bon de commande incorrect</option><option value="quality">Qualité / conformité</option><option value="duplicate">Facture en double</option><option value="administrative">Blocage administratif</option><option value="other">Autre</option></select></label><label className="full">Motif et prochaine étape<textarea name="reason" required rows={2} placeholder="Engagement obtenu, origine du litige, pièce attendue…"/></label><div className="form-actions full"><button type="reset">Annuler la saisie</button><button className="primary" disabled={busy}>Confirmer l’enregistrement</button></div></form>
              </details>
              <details className="collection-accordion"><summary>Coordonnées & responsabilité <span>Configurer</span></summary><form onSubmit={saveContact} className="contact-form"><label>Contact<input name="contactName" defaultValue={selectedCustomer.contact?.contactName||""} placeholder="Nom du contact comptable"/></label><label>E-mail<input name="email" type="email" defaultValue={selectedCustomer.contact?.email||""} placeholder="comptabilite@client.ma"/></label><label>Téléphone<input name="phone" defaultValue={selectedCustomer.contact?.phone||""}/></label><label>Gestionnaire interne<input name="ownerName" defaultValue={selectedCustomer.contact?.ownerName||""}/></label><label>Niveau de risque<select name="riskOverride" defaultValue={selectedCustomer.contact?.riskOverride||""}><option value="">Calcul automatique</option>{Object.entries(riskCopy).map(([value,copy])=><option value={value} key={value}>{copy.label}</option>)}</select></label><label className="full">Notes<textarea name="notes" defaultValue={selectedCustomer.contact?.notes||""} rows={2}/></label><div className="form-actions full"><button type="reset">Annuler les modifications</button><button className="primary" disabled={busy}>Confirmer l’enregistrement</button></div></form></details>
              <section className="invoice-selection"><div className="section-title"><div><h3>Factures à inclure</h3><p>{selectedInvoices.length} sélectionnée(s) · {money(selectedInvoices.reduce((sum,invoice)=>sum+Number(invoice.amount),0))}</p></div><button onClick={()=>setSelectedInvoiceIds(selectedCustomer.invoices.map(invoice=>invoice.id))}>Tout sélectionner</button></div>{selectedCustomer.invoices.map(invoice=><article className={invoice.isOverdue?"overdue":"current"} key={invoice.id}><label><input type="checkbox" checked={selectedInvoiceIds.includes(invoice.id)} onChange={()=>toggleInvoice(invoice.id)}/><span><b>{invoice.reference}</b><small>Échéance {dateFr(invoice.dueDate)} · {invoice.isOverdue?`${invoice.delayDays} j de retard`:"non échue"}</small></span><strong>{money(invoice.amount)}</strong></label><div className="invoice-documents">{invoice.documents.map(document=><span key={document.id}><a href={`/api/treasury/collections/documents?id=${document.id}`} target="_blank" rel="noreferrer">↗ {document.fileName}</a><button onClick={()=>void removeDocument(document)} aria-label={`Supprimer ${document.fileName}`}>×</button></span>)}<label className="attach-button">＋ Joindre la facture<input type="file" aria-label={`Joindre un document à la facture ${invoice.reference}`} accept="application/pdf,image/png,image/jpeg,image/webp" disabled={busy} onChange={event=>{void uploadDocument(invoice,event.target.files?.[0]);event.currentTarget.value=""}}/></label></div></article>)}</section>
              <form className={`reminder-composer ${selectedTemplate?.alertLevel||"low"}`} onSubmit={sendReminder}><div className="section-title"><div><h3>{editingReminderId?"Vérifier une relance existante":"Nouvelle relance par e-mail"}</h3><p>{editingReminderId?`Brouillon ou attente n°${editingReminderId} · la validation mettra à jour cette entrée sans doublon.`:"Vérifiez le destinataire, les factures et le message avant transmission."}</p></div><span className={`risk-badge ${selectedTemplate?.alertLevel||"low"}`}>{selectedTemplate?.name||"Modèle"}</span></div><label>Destinataire<input type="email" value={recipient} onChange={event=>setRecipient(event.target.value)} required placeholder="client@entreprise.ma"/></label><label>Modèle dynamique<select value={templateId} onChange={event=>applyTemplate(Number(event.target.value))} required>{data.templates.map(template=><option value={template.id} key={template.id}>{template.name}</option>)}</select></label><label>Objet<input value={subject} onChange={event=>setSubject(event.target.value)} required maxLength={300}/></label><label>Message<textarea value={content} onChange={event=>setContent(event.target.value)} rows={10} required/><small className="reminder-system-note">{COLLECTION_EMAIL_AUTOMATIC_NOTICE}</small></label><label className="attachment-choice"><input type="checkbox" aria-label="Joindre les factures disponibles" checked={includeAttachments} onChange={event=>setIncludeAttachments(event.target.checked)}/><span><b>Joindre les factures disponibles</b><small>{selectedInvoices.reduce((sum,invoice)=>sum+invoice.documents.length,0)} document(s) rattaché(s) à la sélection</small></span></label>{!data.emailDeliveryConfigured&&<div className="email-config-note"><b>Transport e-mail en attente de configuration</b><span>La relance sera journalisée et mise en file d’attente sans être perdue.</span></div>}<div className="send-confirmation"><span>✓ Après confirmation, la relance est transmise puis classée dans l’historique.</span><button className="primary" disabled={busy||!recipient||!selectedInvoiceIds.length}>{busy?"Traitement…":data.emailDeliveryConfigured?"Valider et transmettre la relance":"Valider et classer en attente"}</button></div></form>
            </div>:<div className="detail-scroll history-panel"><form onSubmit={logAction} className="log-action"><div><h3>Journaliser une action</h3><p>Appel téléphonique ou note interne.</p></div><select name="channel"><option value="phone">Appel téléphonique</option><option value="note">Note de suivi</option></select><textarea name="content" required placeholder="Compte rendu, engagement de paiement, prochaine action…" rows={3}/><button className="primary" disabled={busy}>Confirmer l’ajout au journal</button></form><div className="history-timeline">{selectedCustomer.reminders.length===0?<p className="collection-empty">Aucune action de recouvrement enregistrée.</p>:selectedCustomer.reminders.map(reminder=><article key={reminder.id}><span className={`timeline-dot ${reminder.alertLevel}`}/><div><header><b>{reminder.channel==="email"?(reminder.subject||"Relance e-mail"):reminder.channel==="phone"?"Compte rendu d’appel":"Note de suivi"}</b><em className={`reminder-status ${reminder.status}`}>{reminder.status==="sent"?"Envoyée":reminder.status==="queued"?"En attente":reminder.status==="draft"?"Brouillon":reminder.status==="failed"?"Échec":"Journalisée"}</em></header><p>{reminder.content}</p>{reminder.failureReason&&<small className="failure">{reminder.failureReason}</small>}<footer>{dateFr(reminder.sentAt||reminder.createdAt)} · {reminder.sentBy}{reminder.recipient?` · ${reminder.recipient}`:""}</footer>{reminder.channel==="email"&&(reminder.status==="draft"||reminder.status==="queued"||reminder.status==="failed")&&<nav className="reminder-history-actions"><button type="button" onClick={()=>reviewReminder(reminder)}>Vérifier / modifier</button><button type="button" className="transmit" disabled={busy} onClick={()=>void transmitExistingReminder(reminder)}>{reminder.status==="queued"?"Retenter la transmission":"Valider et transmettre"}</button><button type="button" className="delete" disabled={busy} onClick={()=>void deleteReminder(reminder)}>Supprimer</button></nav>}</div></article>)}</div></div>}
          </>}
        </aside>
      </section>}
    </main>
    <article className="collections-report-print" aria-hidden="true"><header><div><b>Moventra ERP</b><span>Reporting du recouvrement</span></div><div><strong>{companyFilter==="all"?"Vue consolidée":companyName(companyFilter)}</strong><small>Édité le {dateFr(new Date().toISOString())}</small></div></header><h1>Situation des créances clients</h1><section className="collections-report-kpis"><div><span>Recouvrable maintenant</span><b>{money(reporting.collectible)}</b></div><div><span>Encaissé sur 30 jours</span><b>{money(reporting.collected30d)}</b></div><div><span>Promesses actives</span><b>{money(reporting.promised)}</b></div><div><span>Montants en litige</span><b>{money(reporting.disputed)}</b></div></section><section className="collections-report-aging"><h2>Balance âgée</h2><table><thead><tr><th>Tranche</th><th>Montant</th><th>Part de l’encours</th></tr></thead><tbody>{agingBuckets.map(bucket=><tr key={bucket.key}><td>{agingLabels[bucket.key]}</td><td>{money(bucket.value)}</td><td>{reporting.total?`${(bucket.value/reporting.total*100).toFixed(1)} %`:"0 %"}</td></tr>)}</tbody></table></section><section><h2>Détail par client</h2><table><thead><tr><th>Client</th><th>Société</th><th>Échu brut</th><th>Recouvrable</th><th>Promis</th><th>Litiges</th><th>Retard</th><th>Risque</th></tr></thead><tbody>{scopedCustomers.map(customer=><tr key={`${customer.companyId}:${customer.customerKey}`}><td>{customer.customerName}</td><td>{companyName(customer.companyId)}</td><td>{money(customer.overdueAmount)}</td><td>{money(customer.collectibleAmount)}</td><td>{money(customer.promisedAmount)}</td><td>{money(customer.disputedAmount)}</td><td>{customer.maxDelay} j</td><td>{riskCopy[customer.riskLevel].label}</td></tr>)}</tbody></table></section><footer>Moventra ERP · Trésorerie & Finance · Recouvrement clients</footer></article>
    {statementCustomer&&<div className="statement-backdrop" role="dialog" aria-modal="true" aria-label="État de compte client"><article className="statement-print"><header><div><b>Moventra ERP</b><span>État de compte client</span></div><div><strong>{companyName(statementCustomer.companyId)}</strong><small>Édité le {dateFr(new Date().toISOString())}</small></div></header><section><p>CLIENT</p><h1>{statementCustomer.customerName}</h1><span>{statementCustomer.contact?.contactName||"Service comptabilité"} · {statementCustomer.contact?.email||"E-mail non renseigné"}</span></section><div className="statement-summary"><div><span>Encours total</span><b>{money(statementCustomer.totalDue)}</b></div><div><span>Dont échu</span><b>{money(statementCustomer.overdueAmount)}</b></div><div><span>Non échu</span><b>{money(statementCustomer.currentAmount)}</b></div><div><span>Factures ouvertes</span><b>{statementCustomer.invoiceCount}</b></div><div><span>Retard maximal</span><b>{statementCustomer.maxDelay} jours</b></div></div><table><colgroup><col/><col/><col/><col/><col/></colgroup><thead><tr><th>Facture</th><th>Date facture</th><th>Échéance</th><th>Situation</th><th>Montant restant dû</th></tr></thead><tbody>{statementCustomer.invoices.map(invoice=><tr key={invoice.id}><td>{invoice.reference}</td><td>{dateFr(invoice.invoiceDate)}</td><td>{dateFr(invoice.dueDate)}</td><td>{invoice.isOverdue?`Échue · ${invoice.delayDays} j`:"En cours"}</td><td>{money(invoice.amount)}</td></tr>)}</tbody><tfoot><tr><th colSpan={4}>Total restant dû</th><th>{money(statementCustomer.totalDue)}</th></tr></tfoot></table><footer>Document généré depuis Moventra ERP · Trésorerie & Finance · Recouvrement</footer></article><div className="statement-actions"><span className="statement-format-badge">A4 paysage · toutes les pages</span><button onClick={()=>setStatementCustomer(null)}>Fermer l’aperçu</button><button onClick={()=>exportStatementExcel(statementCustomer)}>Télécharger Excel</button><button className="primary" onClick={()=>printCollectionDocument("printing-collection-statement")}>Imprimer / Enregistrer en PDF</button></div></div>}
    {agingDetail&&<div className="aging-detail-backdrop" role="dialog" aria-modal="true" aria-label={`Détail ${agingLabels[agingDetail]}`}><article className="aging-detail-print"><header><div><b>Moventra ERP</b><span>Balance âgée · détail des créances</span></div><div><strong>{agingLabels[agingDetail]}</strong><small>Édité le {dateFr(new Date().toISOString())}</small></div></header><section className="aging-detail-summary"><div><span>Tranche sélectionnée</span><b>{agingLabels[agingDetail]}</b></div><div><span>Nombre de factures</span><b>{agingDetailRows.length}</b></div><div><span>Montant total</span><b>{money(agingDetailRows.reduce((sum,row)=>sum+Math.abs(row.amount),0))}</b></div></section><table><thead><tr><th>Société</th><th>Client</th><th>Facture</th><th>Date facture</th><th>Échéance</th><th>Retard</th><th>Montant restant dû</th></tr></thead><tbody>{agingDetailRows.map((row,index)=><tr key={`${row.reference}-${index}`}><td>{row.company}</td><td>{row.customer}</td><td>{row.reference}</td><td>{dateFr(row.invoiceDate)}</td><td>{dateFr(row.dueDate)}</td><td>{row.delayDays?`${row.delayDays} j`:"Non échue"}</td><td>{money(row.amount)}</td></tr>)}</tbody><tfoot><tr><th colSpan={6}>Total de la tranche</th><th>{money(agingDetailRows.reduce((sum,row)=>sum+Math.abs(row.amount),0))}</th></tr></tfoot></table><footer>Moventra ERP · Trésorerie & Finance · Reporting Recouvrement</footer></article><div className="aging-detail-actions"><span>A4 paysage · toutes les pages</span><button onClick={()=>setAgingDetail(null)}>Fermer</button><button onClick={exportAgingDetail}>Télécharger Excel</button><button className="primary" onClick={()=>printCollectionDocument("printing-aging-detail")}>Imprimer / Enregistrer en PDF</button></div></div>}
    {emailSettingsOpen&&<div className="email-settings-backdrop" role="presentation" onMouseDown={event=>event.target===event.currentTarget&&setEmailSettingsOpen(false)}><section className="email-settings-modal" role="dialog" aria-modal="true" aria-label="Configuration du compte d’envoi"><header><div><p>ADMINISTRATION DU RECOUVREMENT</p><h2>Configuration e-mail</h2><span>Compte sécurisé utilisé uniquement après validation explicite d’une relance.</span></div><button type="button" aria-label="Fermer" onClick={()=>setEmailSettingsOpen(false)}>×</button></header>{emailSettingsLoading||!emailSettings?<div className="email-settings-loading">Chargement de la configuration…</div>:<form onSubmit={saveEmailSettings}><div className={`email-settings-state ${emailSettings.configured?"ready":emailSettings.lastTestStatus==="failed"?"failed":"waiting"}`}><span>{emailSettings.configured?"✓":"!"}</span><div><b>{emailSettings.configured?"Compte d’envoi actif":"Compte d’envoi non opérationnel"}</b><small>{emailSettings.lastTestMessage||"Enregistrez les paramètres, puis envoyez un message de test."}</small></div></div><div className="email-settings-grid"><label><span>Service</span><select name="provider" defaultValue="resend" disabled><option value="resend">Resend</option></select></label><label className="email-settings-switch"><input name="enabled" type="checkbox" defaultChecked={emailSettings.enabled}/><span>Activer l’envoi des relances validées</span></label><label><span>Nom de l’expéditeur</span><input name="fromName" defaultValue={emailSettings.fromName||"Service juridique et financier"} required/></label><label><span>Adresse d’expédition</span><input name="fromEmail" type="email" defaultValue={emailSettings.fromEmail} placeholder="recouvrement@votre-domaine.ma" required/></label><label className="full"><span>Clé API sécurisée</span><input name="apiKey" type="password" autoComplete="new-password" placeholder={emailSettings.apiKeyConfigured?"Clé déjà enregistrée · laissez vide pour la conserver":"Saisir la clé API Resend"}/><small>La clé est chiffrée sur le serveur et n’est jamais réaffichée.</small></label><label className="full"><span>Adresse API</span><input name="apiUrl" type="url" defaultValue={emailSettings.apiUrl} required/><small>Valeur recommandée : https://api.resend.com/emails</small></label></div><footer><button type="button" onClick={()=>setEmailSettingsOpen(false)}>Fermer</button><button type="submit" className="primary" disabled={busy}>{busy?"Enregistrement…":"Enregistrer la configuration"}</button></footer><section className="email-test-panel"><div><b>Test avant mise en service</b><span>Envoie un message réel et enregistre le résultat du contrôle.</span></div><input type="email" value={testRecipient} onChange={event=>setTestRecipient(event.target.value)} placeholder="adresse-de-test@entreprise.ma"/><button type="button" disabled={busy||!emailSettings.configured||!testRecipient} onClick={()=>void testEmailDelivery()}>Envoyer le test</button></section></form>}</section></div>}
    {toast&&<div className="collections-toast" role="status">{toast}</div>}
  </div>;
}
