// New patient — 3-step wizard.

const WZ_VT = window.VT || {};
const WZ_DANGER = WZ_VT.danger || '#b91c1c';

// ─────────────────────────────────────────────────────────────
// Validation — au blur uniquement.
// Corriger quelqu'un pendant qu'il tape est hostile : on attend qu'il ait
// fini le champ. L'erreur s'efface dès la première frappe suivante.
// Les champs facultatifs ne sont contrôlés que s'ils sont remplis.
// ─────────────────────────────────────────────────────────────
function wzValidate(field, raw) {
  const v = String(raw || '').trim();

  if (field === 'ddn') {
    if (!v) return 'Date de naissance requise.';
    const m = /^(\d{2})\/(\d{2})\/(\d{4})$/.exec(v);
    if (!m) return 'Format attendu : JJ/MM/AAAA.';
    const [, dd, mm, yyyy] = m.map(Number);
    const d = new Date(yyyy, mm - 1, dd);
    // getMonth() rattrape les 31/02 : si le mois a glissé, la date n'existe pas.
    if (d.getFullYear() !== yyyy || d.getMonth() !== mm - 1 || d.getDate() !== dd) {
      return "Cette date n'existe pas.";
    }
    if (yyyy < 1900) return 'Année trop ancienne.';
    if (d > new Date()) return 'Date dans le futur.';
    return null;
  }

  if (field === 'email') {
    if (!v) return null;
    if (!/^[^\s@]+@[^\s@]+\.[a-zA-Z]{2,}$/.test(v)) return 'Adresse e-mail invalide.';
    return null;
  }

  if (field === 'secu') {
    if (!v) return null;
    const digits = v.replace(/\s/g, '');
    if (!/^\d{15}$/.test(digits)) return '15 chiffres attendus.';
    if (!/^[12]/.test(digits)) return 'Doit commencer par 1 ou 2.';
    return null;
  }

  if (field === 'phone') {
    if (!v) return null;
    const digits = v.replace(/[\s.\-]/g, '');
    if (!/^(0\d{9}|\+33\d{9})$/.test(digits)) return 'Numéro à 10 chiffres attendu.';
    return null;
  }

  return null;
}

function StepBar({ step }) {
  return (
    <div style={{ display: 'flex', gap: 6, padding: '0 24px', marginTop: 16 }}>
      {[0, 1, 2].map(i => (
        <div key={i} style={{
          flex: 1, height: 3, borderRadius: 2,
          background: i <= step ? P_INK : P_HAIR2,
        }} />
      ))}
    </div>
  );
}

function Field({ label, required, children, full }) {
  return (
    <div style={{ gridColumn: full ? '1 / -1' : 'auto' }}>
      <label style={{
        display: 'block', marginBottom: 6,
        fontSize: 11, fontWeight: 600, letterSpacing: '0.4px',
        color: P_INK, textTransform: 'uppercase',
        fontFamily: "'JetBrains Mono', ui-monospace, monospace",
      }}>{label}{required && <span style={{ color: '#b91c1c' }}> *</span>}</label>
      {children}
    </div>
  );
}

function Inp({ value, onChange, placeholder, type = 'text', onBlur, error }) {
  return (
    <>
      <input type={type} value={value} onChange={e => onChange(e.target.value)}
        onBlur={onBlur}
        placeholder={placeholder}
        aria-invalid={error ? 'true' : undefined}
        style={{
          width: '100%', height: 42,
          background: '#fff',
          border: `1px solid ${error ? WZ_DANGER : P_HAIR2}`, borderRadius: 16,
          outline: 'none', padding: '0 12px',
          fontFamily: 'inherit', fontSize: 14, color: P_INK,
          transition: 'border-color .15s ease',
        }}/>
      {error && (
        <div style={{
          fontSize: 11.5, fontWeight: 600, color: WZ_DANGER,
          marginTop: 5, lineHeight: 1.3,
        }}>{error}</div>
      )}
    </>
  );
}

function Sel({ value, onChange, options, placeholder }) {
  return (
    <select value={value} onChange={e => onChange(e.target.value)}
      style={{
        width: '100%', height: 42,
        background: '#fff',
        border: `1px solid ${P_HAIR2}`, borderRadius: 16,
        outline: 'none', padding: '0 12px',
        fontFamily: 'inherit', fontSize: 14, color: value ? P_INK : P_SUB,
        appearance: 'none',
        backgroundImage: `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%230a0a0a' stroke-width='2'><path d='M6 9l6 6 6-6'/></svg>")`,
        backgroundRepeat: 'no-repeat',
        backgroundPosition: 'right 12px center',
      }}>
      <option value="" disabled>{placeholder}</option>
      {options.map(o => <option key={o} value={o}>{o}</option>)}
    </select>
  );
}

function TA({ value, onChange, placeholder, rows = 3 }) {
  return (
    <textarea value={value} onChange={e => onChange(e.target.value)}
      rows={rows} placeholder={placeholder}
      style={{
        width: '100%', resize: 'vertical',
        background: '#fff',
        border: `1px solid ${P_HAIR2}`, borderRadius: 16,
        outline: 'none', padding: '10px 12px',
        fontFamily: 'inherit', fontSize: 14, lineHeight: 1.5, color: P_INK,
      }}/>
  );
}

// ─────────────────────────────────────────────────────────────
// Step 1 — Identité
// ─────────────────────────────────────────────────────────────
function Step1({ data, set, err, blur }) {
  return (
    <div style={{ padding: '20px 24px 24px' }}>
      <PCaption>Étape 1 / 3</PCaption>
      <h2 style={{
        fontSize: 26, fontWeight: 800, color: P_INK,
        margin: '6px 0 18px', letterSpacing: '-0.7px', lineHeight: 1,
      }}>Identité.</h2>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
        <Field label="Nom" required><Inp value={data.last} onChange={v => set('last', v.toUpperCase())} placeholder="MARTIN" /></Field>
        <Field label="Prénom" required><Inp value={data.first} onChange={v => set('first', v)} placeholder="Jean" /></Field>
        <Field label="Date de naissance" required><Inp value={data.ddn} onChange={v => set('ddn', v)} onBlur={() => blur('ddn')} error={err('ddn')} placeholder="JJ/MM/AAAA" /></Field>
        <Field label="Sexe" required><Sel value={data.sex} onChange={v => set('sex', v)} options={['M', 'F', 'Autre']} placeholder="À renseigner" /></Field>
        <Field label="Téléphone"><Inp value={data.phone} onChange={v => set('phone', v)} onBlur={() => blur('phone')} error={err('phone')} placeholder="06 XX XX XX XX" /></Field>
        <Field label="Email"><Inp value={data.email} onChange={v => set('email', v)} onBlur={() => blur('email')} error={err('email')} type="email" placeholder="patient@email.fr" /></Field>
        <Field label="Adresse" full><Inp value={data.address} onChange={v => set('address', v)} placeholder="Adresse complète" /></Field>
        <Field label="N° Sécurité sociale" full><Inp value={data.secu} onChange={v => set('secu', v)} onBlur={() => blur('secu')} error={err('secu')} placeholder="X XX XX XX XXX XXX XX" /></Field>
        <Field label="Médecin traitant" full><Inp value={data.mt} onChange={v => set('mt', v)} placeholder="Dr. ..." /></Field>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Step 2 — Médical
// ─────────────────────────────────────────────────────────────
function Step2({ data, set }) {
  return (
    <div style={{ padding: '20px 24px 24px' }}>
      <PCaption>Étape 2 / 3</PCaption>
      <h2 style={{
        fontSize: 26, fontWeight: 800, color: P_INK,
        margin: '6px 0 18px', letterSpacing: '-0.7px', lineHeight: 1,
      }}>Données médicales.</h2>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
        <Field label="Groupe sanguin"><Sel value={data.blood} onChange={v => set('blood', v)} options={['A+', 'A-', 'B+', 'B-', 'AB+', 'AB-', 'O+', 'O-']} placeholder="Si connu" /></Field>
        <Field label="Statut clinique"><Sel value={data.status} onChange={v => set('status', v)} options={['Ambulatoire', 'Hospitalisé', 'Réa', 'Soins palliatifs']} placeholder="Ambulatoire" /></Field>
        <Field label="Allergies" full><TA value={data.allergies} onChange={v => set('allergies', v)} placeholder="Une allergie par ligne. Ex : Pénicilline (urticaire)" /></Field>
        <Field label="Antécédents personnels" full><TA value={data.atcdPerso} onChange={v => set('atcdPerso', v)} placeholder="HTA, diabète, chirurgies antérieures…" /></Field>
        <Field label="Traitements en cours" full><TA value={data.treatments} onChange={v => set('treatments', v)} placeholder="Un traitement par ligne avec posologie" /></Field>
        <Field label="Antécédents familiaux" full><TA value={data.atcdFam} onChange={v => set('atcdFam', v)} placeholder="ex : père IDM 65 ans, mère AVC 78 ans" rows={2} /></Field>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Step 3 — Notes & finalisation
// ─────────────────────────────────────────────────────────────
function Step3({ data, set }) {
  return (
    <div style={{ padding: '20px 24px 24px' }}>
      <PCaption>Étape 3 / 3</PCaption>
      <h2 style={{
        fontSize: 26, fontWeight: 800, color: P_INK,
        margin: '6px 0 18px', letterSpacing: '-0.7px', lineHeight: 1,
      }}>Finalisation.</h2>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr', gap: 12 }}>
        <Field label="Motif de la 1ère consultation" required><TA value={data.motif} onChange={v => set('motif', v)} placeholder="Ex : Bilan AOMI stade IIb gauche, claudication 60m" /></Field>
        <Field label="Adressé par"><Inp value={data.adresseur} onChange={v => set('adresseur', v)} placeholder="Dr. ... — service de…" /></Field>
        <Field label="Notes libres"><TA value={data.notes} onChange={v => set('notes', v)} placeholder="Toute observation utile à connaître" rows={4} /></Field>
      </div>

      {/* Recap */}
      <div style={{ marginTop: 24, border: `1px solid ${P_HAIR2}`, borderRadius: 16, padding: 14 }}>
        <PCaption>Récapitulatif</PCaption>
        <div style={{ marginTop: 10 }}>
          {[
            ['Identité',   `${data.last || '—'} ${data.first || ''}`.trim() || '—'],
            ['Né(e) le',   data.ddn || '—'],
            ['Sexe',       data.sex || '—'],
            ['Téléphone',  data.phone || '—'],
            ['Statut',     data.status || 'Ambulatoire'],
            ['Allergies',  (data.allergies || '').trim() || 'Aucune renseignée'],
          ].map(([k, v], i, arr) => (
            <div key={k} style={{
              display: 'flex', justifyContent: 'space-between', gap: 12,
              padding: '8px 0',
              borderBottom: i === arr.length - 1 ? 'none' : `1px solid ${P_HAIR}`,
            }}>
              <PCaption>{k}</PCaption>
              <div style={{
                fontSize: 13, fontWeight: 500, color: P_INK,
                textAlign: 'right', maxWidth: '60%',
                whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
              }}>{v}</div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Wizard host
// ─────────────────────────────────────────────────────────────
const WZ_EMPTY = {
  last: '', first: '', ddn: '', sex: '', phone: '', email: '',
  address: '', secu: '', mt: '',
  blood: '', status: 'Ambulatoire', allergies: '',
  atcdPerso: '', treatments: '', atcdFam: '',
  motif: '', adresseur: '', notes: '',
};
// Champs contrôlés au blur (les autres sont libres).
const WZ_CHECKED = ['ddn', 'phone', 'email', 'secu'];

// Traduction du formulaire vers le contrat serveur. Les zones de texte
// multilignes deviennent des listes : le serveur attend des éléments, pas un
// bloc de texte.
const WZ_SEX = { M: 'male', F: 'female', Autre: 'other' };
const WZ_STATUS = {
  Ambulatoire: 'ambulatory', 'Hospitalisé': 'hospitalized',
  'Réa': 'icu', 'Soins palliatifs': 'palliative',
};

function wzLines(value) {
  return String(value || '')
    .split('\n')
    .map((line) => line.trim())
    .filter(Boolean);
}

function wzBirthDate(value) {
  const match = /^(\d{2})\/(\d{2})\/(\d{4})$/.exec(String(value || '').trim());
  return match ? `${match[3]}-${match[2]}-${match[1]}` : null;
}

function wzPayload(data) {
  const birthDate = wzBirthDate(data.ddn);
  if (!birthDate) return null;
  const optional = (value) => (String(value || '').trim() || null);
  return {
    family_name: data.last.trim(),
    given_name: data.first.trim(),
    birth_date: birthDate,
    sex: WZ_SEX[data.sex] || 'other',
    phone: optional(data.phone),
    email: optional(data.email),
    address: optional(data.address),
    national_health_id: optional(data.secu),
    primary_physician: optional(data.mt),
    blood_type: data.blood || null,
    clinical_status: WZ_STATUS[data.status] || 'ambulatory',
    allergies: wzLines(data.allergies),
    personal_history: wzLines(data.atcdPerso),
    treatments: wzLines(data.treatments),
    family_history: wzLines(data.atcdFam),
    initial_consultation_reason: data.motif.trim(),
    referred_by: optional(data.adresseur),
    general_notes: optional(data.notes),
  };
}

function NewPatientWizard({ accent, onClose, onComplete, runtime = null }) {
  const [step, setStep] = React.useState(0);
  const [data, setData] = React.useState(WZ_EMPTY);
  // Champs déjà quittés une fois : on n'affiche une erreur que sur ceux-là.
  const [touched, setTouched] = React.useState({});
  const [confirmDiscard, setConfirmDiscard] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  const [error, setError] = React.useState(null);

  const set = (k, v) => {
    setData(d => ({ ...d, [k]: v }));
    // Retaper efface l'erreur : on ne laisse pas du rouge sous les doigts.
    if (touched[k]) setTouched(t => ({ ...t, [k]: false }));
  };
  const blur = (k) => setTouched(t => ({ ...t, [k]: true }));
  const err = (k) => (touched[k] ? wzValidate(k, data[k]) : null);

  // « Sale » = au moins un champ s'écarte du formulaire vierge.
  const dirty = React.useMemo(
    () => Object.keys(WZ_EMPTY).some((k) => data[k] !== WZ_EMPTY[k]),
    [data]);

  const askClose = () => {
    if (dirty) { setConfirmDiscard(true); return; }
    onClose();
  };

  const canNext = () => {
    if (step === 0) {
      if (!(data.last && data.first && data.ddn && data.sex)) return false;
      // Un champ mal rempli bloque, qu'il ait été quitté ou non : sinon on
      // pourrait passer l'étape sans jamais sortir du champ fautif.
      return WZ_CHECKED.every((k) => !wzValidate(k, data[k]));
    }
    if (step === 2) return data.motif.trim().length > 0;
    return true;
  };

  // Passer à l'étape suivante marque les champs contrôlés comme quittés :
  // si le bouton reste inerte, on veut voir pourquoi.
  // Le dossier n'était jamais créé : la maquette annonçait « Dossier créé »
  // puis fermait le formulaire. Rien n'existait ensuite dans la liste.
  const submit = async () => {
    if (!runtime) { onComplete(data); return; }
    const payload = wzPayload(data);
    if (!payload) { setError('La date de naissance doit être au format JJ/MM/AAAA'); return; }
    setBusy(true);
    setError(null);
    try {
      const patient = await runtime.create(payload);
      // L'assistant se ferme sur le répertoire rechargé, où le dossier créé
      // figure : c'est la preuve, et elle reste à l'écran.
      onComplete(patient);
    } catch (failure) {
      setError(failure?.message || 'Création du dossier impossible');
    } finally {
      setBusy(false);
    }
  };

  const tryNext = () => {
    if (step === 0 && !canNext()) {
      setTouched((t) => {
        const n = { ...t };
        WZ_CHECKED.forEach((k) => { n[k] = true; });
        return n;
      });
      return;
    }
    if (!canNext()) return;
    if (step < 2) { setStep(step + 1); return; }
    void submit();
  };

  return (
    <div style={{
      // 7 et pas 200 : la barre d'état du châssis est à 10, l'île dynamique à
      // 50. Au-dessus, le wizard les effaçait — alors qu'il réserve justement
      // 62px de padding pour elles. Il crée son propre contexte d'empilement,
      // donc sa modale interne (210) reste bien au-dessus de lui.
      position: 'absolute', inset: 0, zIndex: 7,
      background: P_PAPER, color: P_INK,
      display: 'flex', flexDirection: 'column',
      fontFamily: "'Inter Tight', -apple-system, system-ui, sans-serif",
      animation: 'slideUp .25s cubic-bezier(.2,.7,.2,1)',
    }}>
      <div style={{ flex: 1, overflowY: 'auto', paddingTop: 62 }} className="vasco-scroll">
        {/* Top bar */}
        <div style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          padding: '8px 16px 4px',
        }}>
          <button onClick={() => step > 0 ? setStep(step - 1) : askClose()} className="press"
            style={{
              background: 'none', border: 'none', cursor: 'pointer',
              padding: 6, marginLeft: -6,
            }} aria-label="Retour">
            <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke={P_INK} strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round">
              <path d="M15 6l-6 6 6 6" />
            </svg>
          </button>
          <PCaption>NOUVEAU PATIENT</PCaption>
          <button onClick={askClose} className="press"
            style={{
              background: 'none', border: 'none', cursor: 'pointer',
              padding: 6, marginRight: -6,
            }} aria-label="Fermer">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke={P_INK} strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round">
              <path d="M18 6L6 18M6 6l12 12" />
            </svg>
          </button>
        </div>

        <StepBar step={step} />

        {step === 0 && <Step1 data={data} set={set} err={err} blur={blur} />}
        {step === 1 && <Step2 data={data} set={set} />}
        {step === 2 && <Step3 data={data} set={set} />}

        {error && (
          <div role="alert" style={{
            margin: '0 24px', padding: '10px 12px', borderRadius: 12,
            background: 'rgba(185,28,28,0.06)', fontSize: 12.5, fontWeight: 600, color: '#b91c1c',
          }}>{error}</div>
        )}

        <div style={{ height: 110 }} />
      </div>

      {/* Footer */}
      <div style={{
        borderTop: `1px solid ${P_HAIR}`,
        padding: '14px 24px 28px',
        background: P_PAPER,
        display: 'flex', alignItems: 'center', gap: 10,
      }}>
        <button onClick={askClose} className="press"
          style={{
            background: 'none', border: 'none', cursor: 'pointer',
            padding: '12px 4px',
            fontFamily: 'inherit', fontSize: 13, fontWeight: 600, color: P_SUB,
            letterSpacing: '0.2px',
          }}>ANNULER</button>
        <div style={{ flex: 1 }} />
        {step > 0 && (
          <button onClick={() => setStep(step - 1)} className="press"
            style={{
              height: VT.hLg, padding: '0 18px',
              background: 'transparent', color: P_INK,
              border: `1px solid ${P_HAIR2}`, borderRadius: 16, cursor: 'pointer',
              fontFamily: 'inherit', fontSize: 13, fontWeight: 700, letterSpacing: '-0.1px',
            }}>Précédent</button>
        )}
        <button onClick={tryNext} disabled={busy} className="press"
          style={{
            height: VT.hLg, padding: '0 22px', borderRadius: VT.rLg,
            background: canNext() && !busy ? P_INK : '#e5e5e5',
            color: canNext() && !busy ? '#fff' : P_SUB,
            border: 'none', cursor: canNext() && !busy ? 'pointer' : 'default',
            fontFamily: 'inherit', fontSize: 13.5, fontWeight: 700, letterSpacing: '-0.1px',
            display: 'flex', alignItems: 'center', gap: 8,
          }}>
          {step < 2 ? 'Suivant' : busy ? 'Envoi…' : 'Créer le dossier'}
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round">
            <path d="M9 6l6 6-6 6" />
          </svg>
        </button>
      </div>

      {/* Abandon — seulement si quelque chose a été saisi */}
      {confirmDiscard && (
        <>
          <div onClick={() => setConfirmDiscard(false)} style={{
            position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.35)', zIndex: 210,
          }} />
          <div style={{
            position: 'absolute', left: 20, right: 20, top: '50%', transform: 'translateY(-50%)',
            zIndex: 211, background: '#fff', borderRadius: VT.rXl2 || 22,
            padding: 20, boxShadow: VT.shadowLg,
          }}>
            <div style={{ fontSize: 16, fontWeight: 700, color: P_INK, letterSpacing: '-0.2px' }}>
              Abandonner ce dossier ?
            </div>
            <div style={{ fontSize: 13, fontWeight: 500, color: P_SUB, marginTop: 8, lineHeight: 1.45 }}>
              Les informations saisies pour {data.last || data.first
                ? `${data.last} ${data.first}`.trim()
                : 'ce patient'} seront perdues.
            </div>
            <div style={{ display: 'flex', gap: 8, marginTop: 20 }}>
              <button onClick={() => setConfirmDiscard(false)} className="press" style={{
                flex: 1, height: VT.hLg, borderRadius: VT.rLg,
                background: 'none', border: `1px solid ${P_HAIR2}`,
                fontFamily: 'inherit', fontSize: 14, fontWeight: 700, color: P_INK, cursor: 'pointer',
              }}>Continuer la saisie</button>
              <button onClick={() => { setConfirmDiscard(false); onClose(); }} className="press" style={{
                flex: 1, height: VT.hLg, borderRadius: VT.rLg,
                background: WZ_DANGER, border: 'none',
                fontFamily: 'inherit', fontSize: 14, fontWeight: 700, color: '#fff', cursor: 'pointer',
              }}>Abandonner</button>
            </div>
          </div>
        </>
      )}
    </div>
  );
}

window.NewPatientWizard = NewPatientWizard;
