// Patient dataset — realistic vascular patients with full medical context.

const PATIENTS_FULL = [
  {
    id: 'p-2210',
    last: 'MARTIN', first: 'Jean', civ: 'M.', sex: 'M',
    age: 72, ddn: '03/11/1953',
    phone: '06 14 22 87 03', email: 'j.martin@gmail.com',
    address: '14 rue Lacordaire, 75015 Paris',
    secu: '1 53 11 75 105 124 33',
    mt: 'Dr. Vidal, Hervé',
    ipp: '2210·44871',
    status: 'hosp', // vivant, decede, rea, hosp, palliatif, ambu
    bed: 'CV2 · 412',
    summary: 'AOMI stade IIb G — pontage fémoro-poplité sus-géniculé J0',
    flags: ['AVK', 'HTA', 'Tabagisme actif'],
    crsCount: 14,
    nextRdv: { when: 'AUJ · 14:30', what: 'Visite post-op J0' },
    pinned: ['Allergie Pénicilline (urticaire 2018)', 'Refuse les transfusions'],
    blood: 'A+',
    allergies: ['Pénicilline', 'Iode (modéré)'],
    treatments: ['Warfarine 5mg', 'Atorvastatine 40mg', 'Aspirine 75mg'],
    atcdFam: ['Père : IDM 65 ans', 'Mère : AVC 78 ans'],
    atcdPerso: ['HTA depuis 2008', 'Tabagisme 40 PA, sevré 2020', 'Pontage coronarien 2015'],
    documents: [
      { d: '24/05/26', label: 'CRO — Pontage fémoro-poplité G',     type: 'CRO', tag: 'live' },
      { d: '22/05/26', label: 'CRC — Consultation pré-op',           type: 'CRC' },
      { d: '12/04/26', label: 'Echo-doppler artériel MI',            type: 'EXAM' },
      { d: '03/03/26', label: 'Angio-TDM aorto-iliaque + MI',        type: 'IMG'  },
      { d: '14/02/26', label: 'CRC — Suivi annuel',                  type: 'CRC'  },
      { d: '08/11/25', label: 'CRH — Sortie pontage coronarien',     type: 'CRH'  },
    ],
    rdvs: [
      { d: '24/05/26', t: '14:30', label: 'Visite post-op J0',          state: 'today' },
      { d: '26/05/26', t: '09:00', label: 'Doppler de contrôle J2',     state: 'planned' },
      { d: '24/06/26', t: '11:15', label: 'Consultation 1 mois',        state: 'planned' },
    ],
    notes: [
      { pinned: true,  d: '22/05/26', text: 'Allergie Pénicilline (urticaire 2018) — confirmée par le patient.' },
      { pinned: true,  d: '12/04/26', text: 'Refuse les transfusions sauf urgence vitale.' },
      { pinned: false, d: '03/03/26', text: 'Marche avec canne depuis 6 mois, claudication à 60m.' },
      { pinned: false, d: '14/02/26', text: 'Bon observant pour AVK. INR cible 2-3, dernier 2.4.' },
    ],
    stats: { crs: 14, rdvs: 28, lastVisit: '24/05/26', firstVisit: '11/05/2018' },
  },
  {
    id: 'p-1987',
    last: 'DURAND', first: 'Hélène', civ: 'Mme', sex: 'F',
    age: 68, ddn: '17/02/1958',
    phone: '06 88 41 09 22', email: 'h.durand58@orange.fr',
    address: '7 av. de la République, 92100 Boulogne',
    secu: '2 58 02 92 012 048 17',
    mt: 'Dr. Lemoine, Sophie',
    ipp: '1987·22014',
    status: 'hosp',
    bed: 'CV1 · 308',
    summary: 'Sténose carotide D 78% symptomatique — endartériectomie J1',
    flags: ['AAP', 'Diabète T2'],
    crsCount: 8,
    nextRdv: { when: 'AUJ · 16:00', what: 'Doppler de contrôle' },
    blood: 'O+',
    allergies: [],
    treatments: ['Clopidogrel 75mg', 'Metformine 1000mg ×2', 'Atorvastatine 20mg'],
    atcdFam: ['Frère : AVC 71 ans'],
    atcdPerso: ['Diabète T2 depuis 2014', 'AIT carrefour 02/05/26'],
  },
  {
    id: 'p-3041',
    last: 'BERNARD', first: 'Paul', civ: 'M.', sex: 'M',
    age: 79, ddn: '22/06/1946',
    phone: '06 02 55 17 80', email: '',
    address: '22 bd Voltaire, 75011 Paris',
    secu: '1 46 06 75 511 022 89',
    mt: 'Dr. Ferrand, Luc',
    ipp: '3041·90112',
    status: 'ambu',
    bed: '—',
    summary: 'AAA infra-rénal 54mm — discussion staff EVAR vs chirurgie',
    flags: ['AOD', 'BPCO'],
    crsCount: 5,
    nextRdv: { when: '28/05 · 11:00', what: 'Staff vasculaire' },
    blood: 'B+',
    allergies: ['Iode'],
    treatments: ['Apixaban 5mg ×2', 'Tiotropium inhalation'],
  },
  {
    id: 'p-4128',
    last: 'LEFÈVRE', first: 'Denise', civ: 'Mme', sex: 'F',
    age: 81, ddn: '04/09/1944',
    phone: '06 19 33 02 11', email: 'denise.lefevre@laposte.net',
    address: '5 rue des Lilas, 94300 Vincennes',
    secu: '2 44 09 94 037 011 22',
    mt: 'Dr. Vidal, Hervé',
    ipp: '4128·11890',
    status: 'ambu',
    bed: '—',
    summary: 'Pontage fémoro-jambier postérieur J+5 — sortie',
    flags: ['HBPM', 'Ostéoporose'],
    crsCount: 11,
    nextRdv: { when: '24/06 · 10:30', what: 'Consultation 1 mois' },
  },
  {
    id: 'p-5023',
    last: 'PETIT', first: 'Aline', civ: 'Mme', sex: 'F',
    age: 56, ddn: '12/03/1969',
    phone: '06 71 88 09 14', email: 'aline.petit@gmail.com',
    address: '11 rue Jean Moulin, 93100 Montreuil',
    secu: '2 69 03 93 044 091 04',
    mt: 'Dr. Roussel, Antoine',
    ipp: '5023·77231',
    status: 'ambu',
    bed: '—',
    summary: 'FAV radio-céphalique D — suivi J+30',
    flags: ['Insuffisance rénale stade 5'],
    crsCount: 22,
    nextRdv: { when: 'AUJ · 13:00', what: 'Suivi FAV' },
  },
  {
    id: 'p-5511',
    last: 'GARNIER', first: 'Robert', civ: 'M.', sex: 'M',
    age: 64, ddn: '08/07/1961',
    phone: '06 28 14 77 03', email: 'robert.garnier@free.fr',
    address: '3 place du Marché, 78000 Versailles',
    secu: '1 61 07 78 022 123 71',
    mt: 'Dr. Becker, Anne',
    ipp: '5511·30221',
    status: 'ambu',
    bed: '—',
    summary: 'AOMI stade IIa — bilan',
    flags: ['Tabagisme', 'Diabète T2'],
    crsCount: 3,
    nextRdv: { when: 'AUJ · 13:30', what: 'Consultation' },
  },
  {
    id: 'p-6172',
    last: 'ROBERT', first: 'Lucas', civ: 'M.', sex: 'M',
    age: 47, ddn: '19/11/1978',
    phone: '06 55 09 28 71', email: 'lucas.robert78@gmail.com',
    address: '88 rue de Charenton, 75012 Paris',
    secu: '1 78 11 75 122 089 14',
    mt: 'Dr. Tran, Vinh',
    ipp: '6172·44017',
    status: 'rea',
    bed: 'REA · 12',
    summary: 'Ischémie aiguë MI G — embolectomie en urgence',
    flags: ['ACFA', 'AVK arrêté'],
    crsCount: 1,
    nextRdv: { when: 'AUJ · 14:00', what: 'Bloc 5 — embolectomie' },
  },
  {
    id: 'p-6890',
    last: 'FAURE', first: 'Sylvie', civ: 'Mme', sex: 'F',
    age: 71, ddn: '02/05/1955',
    phone: '06 14 78 22 91', email: 'sylvie.faure@yahoo.fr',
    address: '17 rue du 11 Novembre, 95100 Argenteuil',
    secu: '2 55 05 95 122 014 88',
    mt: 'Dr. Lemoine, Sophie',
    ipp: '6890·22014',
    status: 'ambu',
    bed: '—',
    summary: 'Sténose iliaque commune G — pré-op stent',
    flags: ['HTA'],
    crsCount: 4,
    nextRdv: { when: 'AUJ · 16:00', what: 'Pré-op stent iliaque' },
  },
  {
    id: 'p-7701',
    last: 'LEMAIRE', first: 'Christian', civ: 'M.', sex: 'M',
    age: 58, ddn: '24/01/1967',
    phone: '06 09 22 81 47', email: 'c.lemaire67@orange.fr',
    address: '42 av. Foch, 92500 Rueil-Malmaison',
    secu: '1 67 01 92 064 042 77',
    mt: 'Dr. Roussel, Antoine',
    ipp: '7701·90142',
    status: 'ambu',
    bed: '—',
    summary: 'Création FAV radio-céphalique D — pré-dialyse',
    flags: ['IRC stade 5'],
    crsCount: 9,
    nextRdv: { when: 'AUJ · 17:00', what: 'Bloc 4 — FAV' },
  },
  {
    id: 'p-8044',
    last: 'MOREAU', first: 'Jacqueline', civ: 'Mme', sex: 'F',
    age: 88, ddn: '11/12/1937',
    phone: '06 71 03 14 22', email: '',
    address: 'EHPAD Les Tilleuls, 91300 Massy',
    secu: '2 37 12 91 011 047 53',
    mt: 'Dr. Bonnet, Patricia',
    ipp: '8044·14710',
    status: 'palliatif',
    bed: '—',
    summary: 'Ischémie chronique critique MI D — soins palliatifs',
    flags: ['DNR', 'Démence Alzheimer'],
    crsCount: 6,
    nextRdv: null,
  },
  {
    id: 'p-8902',
    last: 'GIRARD', first: 'Michel', civ: 'M.', sex: 'M',
    age: 75, ddn: '30/04/1950',
    phone: '06 12 89 71 03', email: '',
    address: '6 rue des Acacias, 78180 Montigny-le-Bretonneux',
    secu: '1 50 04 78 092 044 11',
    mt: 'Dr. Vidal, Hervé',
    ipp: '8902·71030',
    status: 'decede',
    bed: '—',
    summary: 'AAA rompu — décédé en peropératoire 14/03/26',
    flags: [],
    crsCount: 7,
    nextRdv: null,
  },
  {
    id: 'p-9211',
    last: 'NGUYEN', first: 'Linh', civ: 'Mme', sex: 'F',
    age: 52, ddn: '18/10/1973',
    phone: '06 88 14 02 27', email: 'linh.nguyen@gmail.com',
    address: '24 rue de Belleville, 75020 Paris',
    secu: '2 73 10 75 120 088 04',
    mt: 'Dr. Ferrand, Luc',
    ipp: '9211·02274',
    status: 'ambu',
    bed: '—',
    summary: 'Varices MI bilatérales — bilan veineux',
    flags: [],
    crsCount: 2,
    nextRdv: { when: '03/06 · 09:30', what: 'Consultation' },
  },
  { id: 'p-1001', last: 'ROUSSEAU', first: 'Marc', civ: 'M.', sex: 'M', age: 63, ddn: '15/08/1962', phone: '06 11 22 33 44', email: '', address: '8 rue Pasteur, 75005 Paris', secu: '1 62 08 75 005 011 22', mt: 'Dr. Vidal, Hervé', ipp: '1001·22110', status: 'ambu', bed: '—', summary: 'Claudication intermittente MI D', flags: ['HTA'], crsCount: 4, nextRdv: { when: '05/06 · 10:00', what: 'Consultation' } },
  { id: 'p-1002', last: 'LAMBERT', first: 'Catherine', civ: 'Mme', sex: 'F', age: 74, ddn: '22/01/1952', phone: '06 22 33 44 55', email: '', address: '3 av. Foch, 92000 Nanterre', secu: '2 52 01 92 011 044 11', mt: 'Dr. Lemoine, Sophie', ipp: '1002·33210', status: 'hosp', bed: 'CV1 · 205', summary: 'Sténose carotide G 82%', flags: ['AVK', 'Diabète T2'], crsCount: 9, nextRdv: { when: 'AUJ · 11:00', what: 'Doppler' } },
  { id: 'p-1003', last: 'MERCIER', first: 'François', civ: 'M.', sex: 'M', age: 69, ddn: '03/04/1957', phone: '06 33 44 55 66', email: '', address: '12 bd Haussmann, 75008 Paris', secu: '1 57 04 75 008 033 22', mt: 'Dr. Ferrand, Luc', ipp: '1003·11420', status: 'ambu', bed: '—', summary: 'Suivi pontage aorto-bifémoral', flags: ['AOD'], crsCount: 12, nextRdv: { when: '10/06 · 14:00', what: 'Suivi' } },
  { id: 'p-1004', last: 'BONNET', first: 'Marie', civ: 'Mme', sex: 'F', age: 58, ddn: '17/11/1967', phone: '06 44 55 66 77', email: '', address: '9 rue Victor Hugo, 93200 Saint-Denis', secu: '2 67 11 93 200 055 33', mt: 'Dr. Roussel, Antoine', ipp: '1004·55320', status: 'ambu', bed: '—', summary: 'Varices récidivantes MI bilatéral', flags: [], crsCount: 6, nextRdv: { when: '12/06 · 09:00', what: 'Echo-doppler' } },
  { id: 'p-1005', last: 'FOURNIER', first: 'Jacques', civ: 'M.', sex: 'M', age: 82, ddn: '28/06/1943', phone: '06 55 66 77 88', email: '', address: '15 rue Danton, 94200 Ivry', secu: '1 43 06 94 200 044 77', mt: 'Dr. Vidal, Hervé', ipp: '1005·77410', status: 'ambu', bed: '—', summary: 'AAA 48mm — surveillance', flags: ['BPCO', 'HTA'], crsCount: 8, nextRdv: { when: '15/06 · 11:30', what: 'Angio-TDM' } },
  { id: 'p-1006', last: 'MOREL', first: 'Isabelle', civ: 'Mme', sex: 'F', age: 66, ddn: '09/03/1960', phone: '06 66 77 88 99', email: '', address: '7 place de la Mairie, 78100 Saint-Germain', secu: '2 60 03 78 100 077 44', mt: 'Dr. Lemoine, Sophie', ipp: '1006·88520', status: 'ambu', bed: '—', summary: 'TVP MI G — anticoagulation', flags: ['AOD'], crsCount: 3, nextRdv: { when: '08/06 · 15:00', what: 'Contrôle INR' } },
  { id: 'p-1007', last: 'SIMON', first: 'André', civ: 'M.', sex: 'M', age: 77, ddn: '14/09/1948', phone: '06 77 88 99 00', email: '', address: '20 rue Molière, 75001 Paris', secu: '1 48 09 75 001 088 55', mt: 'Dr. Ferrand, Luc', ipp: '1007·99630', status: 'ambu', bed: '—', summary: 'Ischémie effort MI D — bilan', flags: ['Tabagisme'], crsCount: 5, nextRdv: { when: '20/06 · 10:00', what: 'Épreuve marche' } },
  { id: 'p-1008', last: 'HENRY', first: 'Françoise', civ: 'Mme', sex: 'F', age: 71, ddn: '05/12/1954', phone: '06 88 99 00 11', email: '', address: '4 rue des Roses, 92400 Courbevoie', secu: '2 54 12 92 400 099 66', mt: 'Dr. Roussel, Antoine', ipp: '1008·00740', status: 'hosp', bed: 'CV2 · 318', summary: 'Endartériectomie carotide D J0', flags: ['AAP'], crsCount: 7, nextRdv: { when: 'AUJ · 16:30', what: 'Post-op' } },
  { id: 'p-1009', last: 'ROUX', first: 'Pierre', civ: 'M.', sex: 'M', age: 60, ddn: '21/07/1965', phone: '06 99 00 11 22', email: '', address: '11 rue Gambetta, 93100 Montreuil', secu: '1 65 07 93 100 000 77', mt: 'Dr. Vidal, Hervé', ipp: '1009·11850', status: 'ambu', bed: '—', summary: 'FAV huméro-céphalique — suivi', flags: ['IRC stade 4'], crsCount: 15, nextRdv: { when: '06/06 · 13:00', what: 'Suivi FAV' } },
  { id: 'p-1010', last: 'DAVID', first: 'Monique', civ: 'Mme', sex: 'F', age: 85, ddn: '30/01/1941', phone: '06 00 11 22 33', email: '', address: '2 rue de la Paix, 75002 Paris', secu: '2 41 01 75 002 011 88', mt: 'Dr. Bonnet, Patricia', ipp: '1010·22960', status: 'ambu', bed: '—', summary: 'Artérite oblitérante stade III', flags: ['HTA', 'Diabète T2'], crsCount: 10, nextRdv: null },
  { id: 'p-1011', last: 'CHEVALIER', first: 'René', civ: 'M.', sex: 'M', age: 73, ddn: '12/05/1953', phone: '06 11 22 33 00', email: '', address: '18 rue Leclerc, 91000 Évry', secu: '1 53 05 91 000 022 99', mt: 'Dr. Ferrand, Luc', ipp: '1011·33070', status: 'ambu', bed: '—', summary: 'Anévrysme poplité D 32mm', flags: [], crsCount: 4, nextRdv: { when: '22/06 · 09:30', what: 'Consultation' } },
  { id: 'p-1012', last: 'MARTINEZ', first: 'Carmen', civ: 'Mme', sex: 'F', age: 55, ddn: '08/08/1970', phone: '06 22 33 00 11', email: '', address: '6 rue Voltaire, 75011 Paris', secu: '2 70 08 75 011 033 00', mt: 'Dr. Lemoine, Sophie', ipp: '1012·44180', status: 'ambu', bed: '—', summary: 'Insuffisance veineuse chronique', flags: [], crsCount: 3, nextRdv: { when: '25/06 · 14:30', what: 'Echo-doppler' } },
  { id: 'p-1013', last: 'THOMAS', first: 'Georges', civ: 'M.', sex: 'M', age: 80, ddn: '25/02/1946', phone: '06 33 00 11 22', email: '', address: '14 av. Jean Jaurès, 94800 Villejuif', secu: '1 46 02 94 800 000 11', mt: 'Dr. Vidal, Hervé', ipp: '1013·55290', status: 'rea', bed: 'REA · 08', summary: 'Rupture AAA — post-op EVAR', flags: ['AVK arrêté'], crsCount: 2, nextRdv: null },
  { id: 'p-1014', last: 'RICHARD', first: 'Nicole', civ: 'Mme', sex: 'F', age: 67, ddn: '19/06/1958', phone: '06 00 11 22 00', email: '', address: '9 rue Raspail, 92300 Levallois', secu: '2 58 06 92 300 011 22', mt: 'Dr. Roussel, Antoine', ipp: '1014·66300', status: 'ambu', bed: '—', summary: 'Sténose iliaque primitive D', flags: ['HTA'], crsCount: 5, nextRdv: { when: '18/06 · 10:00', what: 'Pré-op stent' } },
  { id: 'p-1015', last: 'DUBOIS', first: 'Henri', civ: 'M.', sex: 'M', age: 70, ddn: '01/10/1955', phone: '06 11 00 22 33', email: '', address: '5 bd Saint-Michel, 75005 Paris', secu: '1 55 10 75 005 000 33', mt: 'Dr. Ferrand, Luc', ipp: '1015·77410', status: 'ambu', bed: '—', summary: 'AOMI stade IIb bilatéral', flags: ['Tabagisme', 'HTA'], crsCount: 11, nextRdv: { when: '28/06 · 11:00', what: 'Bilan' } },
  { id: 'p-1016', last: 'LEROUX', first: 'Jeanne', civ: 'Mme', sex: 'F', age: 78, ddn: '07/04/1948', phone: '06 22 00 33 44', email: '', address: '1 rue du Château, 78000 Versailles', secu: '2 48 04 78 000 022 44', mt: 'Dr. Bonnet, Patricia', ipp: '1016·88520', status: 'ambu', bed: '—', summary: 'Ulcère veineux jambe G', flags: ['HBPM'], crsCount: 8, nextRdv: { when: '04/06 · 09:00', what: 'Pansement' } },
  { id: 'p-1017', last: 'GARCIA', first: 'Luis', civ: 'M.', sex: 'M', age: 62, ddn: '13/12/1963', phone: '06 33 00 44 55', email: '', address: '22 rue de la Liberté, 93300 Aubervilliers', secu: '1 63 12 93 300 033 55', mt: 'Dr. Vidal, Hervé', ipp: '1017·99630', status: 'ambu', bed: '—', summary: 'Pontage fémoro-poplité G — suivi 6 mois', flags: [], crsCount: 7, nextRdv: { when: '30/06 · 14:00', what: 'Suivi' } },
  { id: 'p-1018', last: 'PERRIN', first: 'Suzanne', civ: 'Mme', sex: 'F', age: 83, ddn: '26/03/1943', phone: '06 44 00 55 66', email: '', address: '10 rue Marbeuf, 75008 Paris', secu: '2 43 03 75 008 044 66', mt: 'Dr. Lemoine, Sophie', ipp: '1018·00740', status: 'palliatif', bed: '—', summary: 'Ischémie critique MI D — soins palliatifs', flags: ['DNR'], crsCount: 5, nextRdv: null },
];

// Status meta: label, color treatment
const STATUS_META = {
  vivant:    { label: 'Vivant',         dot: '#0a0a0a' },
  ambu:      { label: 'Ambulatoire',    dot: '#0a0a0a' },
  hosp:      { label: 'Hospitalisé',    dot: '#0a0a0a', invert: true },
  rea:       { label: 'Réa',            dot: '#0a0a0a', accent: true },
  palliatif: { label: 'Soins palliat.', dot: '#737373' },
  decede:    { label: 'Décédé',         dot: '#737373', strike: true },
};

// ─────────────────────────────────────────────────────────────
// Historiques documentaires — un seul patient (MARTIN) en avait un écrit à la
// main ; l'onglet Documents des 30 autres était vide, ce qui se voyait en démo.
//
// Plutôt qu'un pavé de 30 listes, on les dérive du dossier : la pathologie
// choisit la famille de documents, et l'étape du parcours décide de ce qui a
// pu être produit. Un patient au stade « bilan » n'a pas de compte rendu
// opératoire ; un patient à J+30 en a un, daté d'il y a 30 jours, avec du
// suivi par-dessus. Le nombre de pièces colle à son `crsCount`, affiché sur la
// page Patients — les deux ne peuvent donc pas diverger.
// ─────────────────────────────────────────────────────────────

// Trois temps par famille : `pre` (mise au point), `op` (le geste et la
// sortie), `post` (le suivi). Le premier motif qui matche gagne.
const PD_DOC_FAMILIES = [
  { test: /carotid|TSA|AIT/i,
    pre: [
      { t: 'CRC',  l: 'CRC — Consultation pré-opératoire' },
      { t: 'EXAM', l: 'Écho-doppler des troncs supra-aortiques' },
      { t: 'IMG',  l: 'Angio-TDM des TSA' },
      { t: 'EXAM', l: 'Bilan biologique pré-opératoire' },
      { t: 'IMG',  l: 'IRM cérébrale de diffusion' },
      { t: 'CRC',  l: 'CRC — Consultation neurovasculaire' },
    ],
    op: [
      { t: 'CRO',  l: 'CRO — Endartériectomie carotidienne' },
      { t: 'CRH',  l: "CRH — Sortie d'hospitalisation" },
    ],
    post: [
      { t: 'EXAM', l: 'Écho-doppler de contrôle post-opératoire' },
      { t: 'CRC',  l: 'CRC — Suivi neurovasculaire' },
    ] },

  { test: /AAA|aorte|anévrysme aortique|EVAR/i,
    pre: [
      { t: 'IMG',  l: 'Angio-TDM aortique avec mesures' },
      { t: 'CRC',  l: 'CRC — Discussion staff EVAR / chirurgie ouverte' },
      { t: 'EXAM', l: 'Écho-doppler aortique de surveillance' },
      { t: 'CRC',  l: "CRC — Consultation d'annonce" },
      { t: 'EXAM', l: 'Épreuve fonctionnelle respiratoire' },
      { t: 'EXAM', l: 'Bilan cardiologique pré-opératoire' },
    ],
    op: [
      { t: 'CRO',  l: 'CRO — Endoprothèse aortique (EVAR)' },
      { t: 'CRH',  l: 'CRH — Sortie de chirurgie aortique' },
    ],
    post: [
      { t: 'IMG',  l: 'Angio-TDM de contrôle post-EVAR' },
      { t: 'CRC',  l: "CRC — Suivi d'endoprothèse" },
    ] },

  { test: /FAV|dialyse|abord vasculaire/i,
    pre: [
      { t: 'CRC',  l: 'CRC — Consultation pré-dialyse' },
      { t: 'EXAM', l: 'Cartographie veineuse du membre supérieur' },
      { t: 'EXAM', l: 'Bilan néphrologique conjoint' },
      { t: 'CRC',  l: "CRC — Information sur les modes d'épuration" },
      { t: 'EXAM', l: 'Écho-doppler artériel du membre supérieur' },
      { t: 'CRC',  l: 'CRC — Consultation d’anesthésie' },
    ],
    op: [
      { t: 'CRO',  l: 'CRO — Création de FAV radio-céphalique' },
      { t: 'CRH',  l: 'CRH — Sortie après création de FAV' },
    ],
    post: [
      { t: 'EXAM', l: 'Écho-doppler de maturation de FAV' },
      { t: 'CRC',  l: 'CRC — Suivi du débit de FAV' },
    ] },

  { test: /varice|veineu|TVP|phlébit/i,
    pre: [
      { t: 'EXAM', l: 'Écho-doppler veineux des membres inférieurs' },
      { t: 'CRC',  l: 'CRC — Consultation phlébologique' },
      { t: 'EXAM', l: 'Cartographie veineuse pré-opératoire' },
      { t: 'CRC',  l: 'CRC — Consultation de suivi veineux' },
      { t: 'IMG',  l: 'Phlébographie des membres inférieurs' },
      { t: 'EXAM', l: 'Bilan de thrombophilie' },
    ],
    op: [
      { t: 'CRO',  l: 'CRO — Stripping de la grande saphène' },
      { t: 'CRH',  l: 'CRH — Sortie de chirurgie veineuse' },
    ],
    post: [
      { t: 'CRC',  l: 'CRC — Suivi post-opératoire veineux' },
      { t: 'EXAM', l: 'Écho-doppler veineux de contrôle' },
    ] },

  { test: /iliaque|stent/i,
    pre: [
      { t: 'IMG',  l: 'Angio-TDM aorto-iliaque' },
      { t: 'CRC',  l: 'CRC — Consultation pré-interventionnelle' },
      { t: 'EXAM', l: 'Index de pression systolique (IPS)' },
      { t: 'IMG',  l: 'Artériographie diagnostique' },
      { t: 'EXAM', l: 'Écho-doppler artériel des membres inférieurs' },
      { t: 'CRC',  l: 'CRC — Consultation de claudication' },
    ],
    op: [
      { t: 'CRO',  l: 'CRO — Angioplastie iliaque avec stent' },
      { t: 'CRH',  l: "CRH — Sortie d'angioplastie" },
    ],
    post: [
      { t: 'EXAM', l: 'Écho-doppler de contrôle du stent' },
      { t: 'CRC',  l: 'CRC — Suivi endovasculaire' },
    ] },

  { test: /ischémie aiguë|embolectom|Fogarty/i,
    pre: [
      { t: 'IMG',  l: 'Angio-TDM des membres inférieurs en urgence' },
      { t: 'EXAM', l: 'Bilan biologique en urgence' },
      { t: 'CRC',  l: 'CRC — Consultation de cardiologie (ACFA)' },
      { t: 'EXAM', l: 'ECG et bilan rythmologique' },
      { t: 'EXAM', l: 'Écho-doppler artériel diagnostique' },
      { t: 'CRC',  l: "CRC — Suivi d'anticoagulation" },
    ],
    op: [
      { t: 'CRO',  l: 'CRO — Embolectomie au Fogarty' },
      { t: 'CRH',  l: 'CRH — Passage en réanimation' },
    ],
    post: [
      { t: 'EXAM', l: 'Bilan de reperfusion (CPK, lactates)' },
      { t: 'EXAM', l: 'Écho-doppler artériel post-embolectomie' },
    ] },

  { test: /palliatif|amputation/i,
    pre: [
      { t: 'CRC',  l: 'CRC — Réunion de concertation pluridisciplinaire' },
      { t: 'EXAM', l: 'Écho-doppler artériel — évaluation de sauvetage' },
      { t: 'IMG',  l: 'Artériographie des membres inférieurs' },
      { t: 'EXAM', l: 'Bilan nutritionnel et biologique' },
      { t: 'CRC',  l: 'CRC — Entretien avec la famille' },
      { t: 'CRC',  l: 'CRC — Consultation antalgique' },
    ],
    op: [
      { t: 'CRH',  l: 'CRH — Prise en charge palliative' },
    ],
    post: [
      { t: 'CRC',  l: 'CRC — Suivi des soins de plaie' },
      { t: 'CRC',  l: 'CRC — Réévaluation antalgique' },
    ] },

  // AOMI, pontages et le reste de la revascularisation des membres.
  { test: /.*/,
    pre: [
      { t: 'EXAM', l: 'Écho-doppler artériel des membres inférieurs' },
      { t: 'CRC',  l: 'CRC — Consultation pré-opératoire' },
      { t: 'IMG',  l: 'Angio-TDM aorto-iliaque et membres inférieurs' },
      { t: 'EXAM', l: 'Épreuve de marche sur tapis' },
      { t: 'EXAM', l: 'Index de pression systolique (IPS)' },
      { t: 'CRC',  l: 'CRC — Suivi de claudication' },
    ],
    op: [
      { t: 'CRO',  l: 'CRO — Pontage fémoro-poplité' },
      { t: 'CRH',  l: "CRH — Sortie d'hospitalisation" },
    ],
    post: [
      { t: 'EXAM', l: 'Écho-doppler de perméabilité du pontage' },
      { t: 'CRC',  l: 'CRC — Consultation post-opératoire' },
    ] },
];

function pdDocFamily(p) {
  const hay = `${p.summary || ''} ${(p.flags || []).join(' ')}`;
  return PD_DOC_FAMILIES.find((f) => f.test.test(hay)) || PD_DOC_FAMILIES[PD_DOC_FAMILIES.length - 1];
}

// Où en est le patient ? `opDaysAgo` vaut null s'il n'a jamais été opéré.
function pdStage(p) {
  const s = p.summary || '';
  const jplus = /J\+(\d+)/.exec(s);
  if (jplus) return { opDaysAgo: Number(jplus[1]) };
  if (/\bJ0\b/.test(s)) return { opDaysAgo: 0 };
  if (/\bJ1\b/.test(s)) return { opDaysAgo: 1 };
  if (/décédé/i.test(s)) return { opDaysAgo: 0 };
  // Mise au point en cours : rien n'a encore été fait au bloc.
  if (/bilan|pré-op|pre-op|discussion|surveillance|pré-dialyse|palliatif/i.test(s)) {
    return { opDaysAgo: null };
  }
  // Formulé comme un suivi sans jour précisé : l'intervention date d'un moment.
  if (/suivi|sortie|post-op|contrôle/i.test(s)) return { opDaysAgo: 45 };
  return { opDaysAgo: null };
}

function pdFmtDate(d) {
  const p2 = (n) => String(n).padStart(2, '0');
  return `${p2(d.getDate())}/${p2(d.getMonth() + 1)}/${String(d.getFullYear()).slice(2)}`;
}

function pdBuildDocuments(p, seed) {
  const fam = pdDocFamily(p);
  const { opDaysAgo } = pdStage(p);
  // Au-delà d'une dizaine de lignes la liste n'apprend plus rien.
  const n = Math.max(1, Math.min(p.crsCount || 3, 10));

  // Un dossier clos s'arrête au décès ; les autres courent jusqu'à
  // aujourd'hui dans la fiction de l'app (24/05/26).
  const dead = /décédé/i.test(p.summary || '');
  const anchor = dead ? new Date(2026, 2, 14) : new Date(2026, 4, 24);
  const dayMs = 86400000;

  // Séquence du plus récent au plus ancien, selon l'étape du parcours.
  // Le compte rendu de sortie vient APRÈS le geste dans le temps, donc avant
  // lui dans une liste antichronologique — et il n'existe pas si le patient
  // est encore au lit, ou s'il est mort au bloc.
  const stillIn = p.status === 'hosp' || p.status === 'rea';
  const seq = [];
  if (opDaysAgo === null) {
    seq.push(...fam.pre);
  } else {
    const followUps = opDaysAgo >= 25 ? 2 : opDaysAgo >= 5 ? 1 : 0;
    seq.push(...fam.post.slice(0, followUps));
    const cro = fam.op[0];
    const sortie = fam.op[1];
    if (sortie && !stillIn && !dead) seq.push(sortie);
    seq.push(cro);
    seq.push(...fam.pre);
  }

  const out = [];
  let cursor = anchor.getTime();
  for (let i = 0; i < n; i++) {
    const src = seq[i % seq.length];
    // Ancrage sur la PREMIÈRE occurrence seulement : au-delà de `seq.length`
    // la liste reboucle, et re-caler le curseur sur la date du bloc le
    // ferait repartir en avant — les dates cessaient d'être décroissantes.
    const firstPass = i < seq.length;
    const isOp = firstPass && opDaysAgo !== null && src === fam.op[0];
    const isSortie = firstPass && opDaysAgo !== null && fam.op[1] && src === fam.op[1];

    if (isOp) {
      // Le compte rendu opératoire est daté par le J+N du dossier, pas par
      // la marche arrière générique : c'est ce que le médecin va vérifier.
      cursor = anchor.getTime() - opDaysAgo * dayMs;
    } else if (isSortie) {
      // Sortie 2 à 5 jours après le geste, jamais dans le futur.
      const stay = 2 + (seed % 4);
      cursor = Math.min(anchor.getTime(), anchor.getTime() - (opDaysAgo - stay) * dayMs);
    } else if (i > 0) {
      const gap = i < 3 ? 2 + ((seed + i * 7) % 9) : 24 + ((seed + i * 13) % 70);
      cursor -= gap * dayMs;
    }

    out.push({
      d: pdFmtDate(new Date(cursor)),
      label: i < seq.length ? src.l : `${src.l} (${Math.floor(i / seq.length) + 1})`,
      type: src.t,
      // « En cours » ne vaut que pour un patient encore au lit, sur la pièce
      // la plus récente, et seulement si elle vient du bloc.
      ...(i === 0 && src.t === 'CRO' && (p.status === 'hosp' || p.status === 'rea')
        ? { tag: 'live' } : {}),
    });
  }
  return out;
}

// Les patients écrits à la main gardent leur historique : on ne remplit que les vides.
PATIENTS_FULL.forEach((p, i) => {
  if (!p.documents || !p.documents.length) p.documents = pdBuildDocuments(p, i * 5 + 3);
});

Object.assign(window, { PATIENTS_FULL, STATUS_META });
