import { useEffect, useState } from "preact/hooks";

type Student = {
  numEtud: number;
  nom: string;
  prenom: string;
  idPromo: string;
};
type Promotion = { id: string; annee: string };
type Mobilite = {
  id: number;
  numEtud: number;
  duree: number;
  contratMob: string | null;
  ecole: string | null;
  pays: string | null;
  status: string;
  idStage: number | null;
};
type Stage = {
  id: number;
  numEtud: number;
  duree: number;
  nomEntreprise: string;
  mission: string | null;
};

const REQUIRED_WEEKS = 12;

const STATUS_ORDER = [
  "contracts_received",
  "under_revision",
  "done",
  "validated",
  "canceled",
] as const;

const STATUS_LABELS: Record<string, string> = {
  contracts_received: "Contrats reçus",
  under_revision: "En révision",
  done: "Signé",
  validated: "Validé",
  canceled: "Annulé",
};

const STATUS_COLORS: Record<string, string> = {
  contracts_received: "#f5a623",
  under_revision: "#dc2626",
  done: "#22c55e",
  validated: "light-dark(var(--light-accent-color), var(--dark-accent-color))",
  canceled:
    "light-dark(var(--light-foreground-dim), var(--dark-foreground-dim))",
};

function lowestStatus(mobs: Mobilite[]): string {
  let lowest = STATUS_ORDER.length - 1;
  for (const m of mobs) {
    const idx = STATUS_ORDER.indexOf(m.status as typeof STATUS_ORDER[number]);
    if (idx >= 0 && idx < lowest) lowest = idx;
  }
  return STATUS_ORDER[lowest];
}

function validatedWeeks(mobs: Mobilite[]): number {
  return mobs
    .filter((m) => m.status === "validated")
    .reduce((sum, m) => sum + m.duree, 0);
}

export default function MobilityOverview(
  { initialNumEtud }: { initialNumEtud?: number } = {},
) {
  const [students, setStudents] = useState<Student[]>([]);
  const [promos, setPromos] = useState<Promotion[]>([]);
  const [mobilites, setMobilites] = useState<Mobilite[]>([]);
  const [stagesMap, setStagesMap] = useState<Record<number, Stage>>({});
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  const [tab, setTab] = useState<"liste" | "kanban">("liste");
  const [filterPromo, setFilterPromo] = useState("");
  const [filterNom, setFilterNom] = useState("");

  // Detail view state
  const [detailStudent, setDetailStudent] = useState<Student | null>(null);
  const [editingMob, setEditingMob] = useState<Mobilite | null>(null);
  const [showAddForm, setShowAddForm] = useState(false);

  async function load() {
    try {
      const [sRes, pRes, mRes, stRes] = await Promise.all([
        fetch("/students/api/students"),
        fetch("/students/api/promotions"),
        fetch("/mobility/api/mobilites"),
        fetch("/stages/api/stages"),
      ]);
      if (!sRes.ok) throw new Error("Impossible de charger les données");
      const [sData, pData, mData, stData] = await Promise.all([
        sRes.json(),
        pRes.ok ? pRes.json() : [],
        mRes.ok ? mRes.json() : [],
        stRes.ok ? stRes.json() : [],
      ]);
      setStudents(sData);
      setPromos(pData);
      setMobilites(mData);
      setStagesMap(
        Object.fromEntries((stData as Stage[]).map((s) => [s.id, s])),
      );
      if (initialNumEtud) {
        const s = (sData as Student[]).find((s) =>
          s.numEtud === initialNumEtud
        );
        if (s) setDetailStudent(s);
      }
    } catch (e) {
      setError(e instanceof Error ? e.message : "Erreur");
    } finally {
      setLoading(false);
    }
  }

  useEffect(() => {
    load();
  }, []);

  function openStudent(s: Student) {
    setDetailStudent(s);
    history.pushState(null, "", `/mobility/overview/${s.numEtud}`);
  }

  function closeStudent() {
    setDetailStudent(null);
    setEditingMob(null);
    setShowAddForm(false);
    history.pushState(null, "", "/mobility/overview");
  }

  // If in detail view, render that
  if (detailStudent) {
    return (
      <DetailView
        student={detailStudent}
        mobilites={mobilites.filter((m) => m.numEtud === detailStudent.numEtud)}
        allMobilites={mobilites}
        stagesMap={stagesMap}
        editingMob={editingMob}
        setEditingMob={setEditingMob}
        showAddForm={showAddForm}
        setShowAddForm={setShowAddForm}
        onBack={closeStudent}
        onReload={load}
      />
    );
  }

  if (loading) {
    return (
      <div class="page-content">
        <p class="state-loading">Chargement...</p>
      </div>
    );
  }
  if (error) {
    return (
      <div class="page-content">
        <p class="state-error">{error}</p>
      </div>
    );
  }

  const filtered = students.filter((s) => {
    const matchPromo = !filterPromo || s.idPromo === filterPromo;
    const matchNom = !filterNom ||
      `${s.nom} ${s.prenom}`.toLowerCase().includes(filterNom.toLowerCase());
    return matchPromo && matchNom;
  });

  const mobsByStudent = (numEtud: number) =>
    mobilites.filter((m) => m.numEtud === numEtud);

  return (
    <div class="page-content">
      <h2 class="page-title">Suivi des mobilités</h2>

      <div class="tabs">
        <button
          type="button"
          class={`tab-btn${tab === "liste" ? " active" : ""}`}
          onClick={() => setTab("liste")}
        >
          Liste
        </button>
        <button
          type="button"
          class={`tab-btn${tab === "kanban" ? " active" : ""}`}
          onClick={() => setTab("kanban")}
        >
          Kanban
        </button>
      </div>

      <div class="filters">
        <select
          class="filter-select"
          value={filterPromo}
          onChange={(e) =>
            setFilterPromo((e.target as HTMLSelectElement).value)}
        >
          <option value="">Toutes les promos</option>
          {promos.map((p) => <option key={p.id} value={p.id}>{p.id}</option>)}
        </select>
        <input
          class="filter-input"
          placeholder="Rechercher..."
          value={filterNom}
          onInput={(e) => setFilterNom((e.target as HTMLInputElement).value)}
        />
      </div>

      {tab === "liste"
        ? (
          <ListView
            students={filtered}
            mobsByStudent={mobsByStudent}
            onConsult={(s) => openStudent(s)}
          />
        )
        : (
          <KanbanView
            students={filtered}
            mobsByStudent={mobsByStudent}
            onConsult={(s) => openStudent(s)}
          />
        )}
    </div>
  );
}

// ─── Liste View ─────────────────────────────────────────────

function ListView(
  { students, mobsByStudent, onConsult }: {
    students: Student[];
    mobsByStudent: (n: number) => Mobilite[];
    onConsult: (s: Student) => void;
  },
) {
  return (
    <div class="data-table-wrap">
      <table class="data-table">
        <thead>
          <tr>
            <th>N° étud.</th>
            <th>Nom</th>
            <th>Prénom</th>
            <th>Semaines</th>
            <th>Actions</th>
          </tr>
        </thead>
        <tbody>
          {students.length === 0
            ? (
              <tr>
                <td colspan={5} class="state-empty">Aucun élève trouvé</td>
              </tr>
            )
            : students.map((s) => {
              const mobs = mobsByStudent(s.numEtud);
              const weeks = validatedWeeks(mobs);
              const ok = weeks >= REQUIRED_WEEKS;
              return (
                <tr key={s.numEtud}>
                  <td class="col-dim">{s.numEtud}</td>
                  <td>{s.nom}</td>
                  <td>{s.prenom}</td>
                  <td>
                    <span
                      style={{
                        color: ok ? "var(--ok-color,#22c55e)" : "#dc2626",
                        fontWeight: "var(--font-weight-bold)",
                        fontFamily: "monospace",
                      }}
                    >
                      {weeks}/{REQUIRED_WEEKS}
                    </span>
                  </td>
                  <td>
                    <button
                      type="button"
                      class="btn btn-sm btn-primary"
                      onClick={() => onConsult(s)}
                    >
                      Consulter
                    </button>
                  </td>
                </tr>
              );
            })}
        </tbody>
      </table>
    </div>
  );
}

// ─── Kanban View ────────────────────────────────────────────

function KanbanView(
  { students, mobsByStudent, onConsult }: {
    students: Student[];
    mobsByStudent: (n: number) => Mobilite[];
    onConsult: (s: Student) => void;
  },
) {
  // Students who have at least one mobility
  const studentsWithMobs = students.filter(
    (s) => mobsByStudent(s.numEtud).length > 0,
  );

  // Group students by their lowest status
  const columns: Record<string, Student[]> = {};
  for (const status of STATUS_ORDER) columns[status] = [];

  for (const s of studentsWithMobs) {
    const mobs = mobsByStudent(s.numEtud);
    // Filter out canceled for lowest-status calc (canceled is separate)
    const activeMobs = mobs.filter((m) => m.status !== "canceled");
    if (activeMobs.length === 0) {
      // All canceled
      columns["canceled"].push(s);
    } else {
      const lowest = lowestStatus(activeMobs);
      columns[lowest].push(s);
    }
  }

  return (
    <div
      style={{
        display: "flex",
        gap: "0.75rem",
        overflowX: "auto",
        paddingBottom: "0.5rem",
      }}
    >
      {STATUS_ORDER.map((status) => (
        <div
          key={status}
          style={{
            minWidth: "14rem",
            flex: "1",
            borderRadius: "4px",
            border:
              "1px solid light-dark(var(--light-foreground-dimmer), var(--dark-foreground-dimmer))",
            background: "light-dark(white, #141228)",
          }}
        >
          <div
            style={{
              padding: "0.6rem 0.75rem",
              borderBottom:
                "1px solid light-dark(var(--light-foreground-dimmer), var(--dark-foreground-dimmer))",
              display: "flex",
              alignItems: "center",
              gap: "0.5rem",
            }}
          >
            <span
              style={{
                width: "0.6rem",
                height: "0.6rem",
                borderRadius: "50%",
                background: STATUS_COLORS[status],
                display: "inline-block",
              }}
            />
            <span
              style={{
                fontWeight: "var(--font-weight-bold)",
                fontSize: "0.82rem",
              }}
            >
              {STATUS_LABELS[status]}
            </span>
            <span
              style={{
                fontSize: "0.7rem",
                opacity: "0.7",
                marginLeft: "auto",
              }}
            >
              {columns[status].length}
            </span>
          </div>
          <div style={{ padding: "0.5rem" }}>
            {columns[status].length === 0
              ? (
                <p
                  style={{
                    fontSize: "0.75rem",
                    opacity: "0.5",
                    textAlign: "center",
                    margin: "1rem 0",
                  }}
                >
                  Aucun
                </p>
              )
              : columns[status].map((s) => {
                const mobs = mobsByStudent(s.numEtud);
                const weeks = validatedWeeks(mobs);
                return (
                  <div
                    key={s.numEtud}
                    style={{
                      padding: "0.5rem 0.6rem",
                      marginBottom: "0.4rem",
                      borderRadius: "3px",
                      border:
                        "1px solid light-dark(var(--light-foreground-dimmer), var(--dark-foreground-dimmer))",
                      cursor: "pointer",
                      fontSize: "0.8rem",
                    }}
                    onClick={() => onConsult(s)}
                  >
                    <div
                      style={{
                        fontWeight: "var(--font-weight-bold)",
                        marginBottom: "0.15rem",
                      }}
                    >
                      {s.nom} {s.prenom}
                    </div>
                    <div
                      style={{
                        fontSize: "0.72rem",
                        display: "flex",
                        justifyContent: "space-between",
                      }}
                    >
                      <span style={{ opacity: "0.7" }}>{s.numEtud}</span>
                      <span
                        style={{
                          color: weeks >= REQUIRED_WEEKS
                            ? "#22c55e"
                            : "#dc2626",
                          fontWeight: "var(--font-weight-bold)",
                          fontFamily: "monospace",
                        }}
                      >
                        {weeks}/{REQUIRED_WEEKS} sem.
                      </span>
                    </div>
                  </div>
                );
              })}
          </div>
        </div>
      ))}
    </div>
  );
}

// ─── Detail View ────────────────────────────────────────────

function DetailView(
  {
    student,
    mobilites,
    allMobilites,
    stagesMap,
    editingMob,
    setEditingMob,
    showAddForm,
    setShowAddForm,
    onBack,
    onReload,
  }: {
    student: Student;
    mobilites: Mobilite[];
    allMobilites: Mobilite[];
    stagesMap: Record<number, Stage>;
    editingMob: Mobilite | null;
    setEditingMob: (m: Mobilite | null) => void;
    showAddForm: boolean;
    setShowAddForm: (v: boolean) => void;
    onBack: () => void;
    onReload: () => Promise<void>;
  },
) {
  const weeks = validatedWeeks(mobilites);
  const ecoles = [...new Set(allMobilites.map((m) => m.ecole).filter(Boolean))];
  const paysList = [
    ...new Set(allMobilites.map((m) => m.pays).filter(Boolean)),
  ];

  async function deleteMob(id: number) {
    if (!confirm("Supprimer cette mobilité ?")) return;
    await fetch(`/mobility/api/mobilites/${id}`, { method: "DELETE" });
    await onReload();
  }

  return (
    <div class="page-content">
      <button
        type="button"
        class="btn btn-sm btn-secondary"
        onClick={onBack}
        style={{ marginBottom: "0.75rem" }}
      >
        Retour
      </button>
      <h2 class="page-title">
        Consulter : {student.prenom} {student.nom}
        <span
          style={{
            fontSize: "0.8rem",
            fontWeight: "normal",
            marginLeft: "0.75rem",
            color: weeks >= REQUIRED_WEEKS ? "#22c55e" : "#dc2626",
            fontFamily: "monospace",
          }}
        >
          {weeks}/{REQUIRED_WEEKS} semaines validées
        </span>
      </h2>

      {mobilites.length === 0 && (
        <p class="state-empty">Aucune mobilité enregistrée.</p>
      )}

      {mobilites.map((mob, i) => {
        const stage = mob.idStage ? stagesMap[mob.idStage] : null;
        const isEditing = editingMob?.id === mob.id;

        if (isEditing) {
          return (
            <MobEditForm
              key={mob.id}
              mob={mob}
              ecoles={ecoles}
              paysList={paysList}
              onCancel={() => setEditingMob(null)}
              onSave={async () => {
                setEditingMob(null);
                await onReload();
              }}
            />
          );
        }

        return (
          <div key={mob.id} class="ue-card" style={{ marginBottom: "1rem" }}>
            <div class="ue-card-header">
              <p class="ue-card-title">
                Mobilité {i + 1}
                {stage ? " : Stage" : " : Étude"}
              </p>
              <p
                class="ue-card-avg"
                style={{
                  display: "flex",
                  gap: "0.75rem",
                  alignItems: "center",
                }}
              >
                <span
                  class={`note-chip ${
                    mob.status === "validated"
                      ? "note-chip--ok"
                      : mob.status === "canceled"
                      ? "note-chip--fail"
                      : "note-chip--none"
                  }`}
                >
                  {STATUS_LABELS[mob.status] ?? mob.status}
                </span>
                <span>Durée : {mob.duree} semaine(s)</span>
              </p>
            </div>
            <div style={{ padding: "0.6rem 1.1rem" }}>
              {stage
                ? (
                  <p style={{ fontSize: "0.82rem", margin: "0 0 0.4rem" }}>
                    Entreprise : <strong>{stage.nomEntreprise}</strong>
                    {stage.mission && <span>— {stage.mission}</span>}
                  </p>
                )
                : (
                  <p style={{ fontSize: "0.82rem", margin: "0 0 0.4rem" }}>
                    {mob.ecole && (
                      <>
                        École : <strong>{mob.ecole}</strong>
                      </>
                    )}
                    {mob.ecole && mob.pays && <span>,</span>}
                    {mob.pays && (
                      <>
                        Pays : <strong>{mob.pays}</strong>
                      </>
                    )}
                  </p>
                )}
              <div
                style={{
                  display: "flex",
                  gap: "0.5rem",
                  flexWrap: "wrap",
                  marginTop: "0.5rem",
                }}
              >
                {mob.contratMob && (
                  <a
                    class="btn btn-sm btn-primary"
                    href={`/mobility/api/mobilites/${mob.id}/contrat`}
                    target="_blank"
                  >
                    Télécharger contrat
                  </a>
                )}
                {!mob.idStage && (
                  <UploadContratBtn
                    mobId={mob.id}
                    hasContrat={!!mob.contratMob}
                    onDone={onReload}
                  />
                )}
                <button
                  type="button"
                  class="btn btn-sm btn-secondary"
                  onClick={() =>
                    setEditingMob(mob)}
                >
                  Modifier
                </button>
                <button
                  type="button"
                  class="btn btn-sm btn-danger"
                  onClick={() =>
                    deleteMob(mob.id)}
                >
                  Supprimer
                </button>
              </div>
            </div>
          </div>
        );
      })}

      {showAddForm
        ? (
          <MobAddForm
            numEtud={student.numEtud}
            ecoles={ecoles}
            paysList={paysList}
            availableStages={Object.values(stagesMap)
              .filter((s) => s.numEtud === student.numEtud)
              .filter((s) => !mobilites.some((m) => m.idStage === s.id))}
            onCancel={() => setShowAddForm(false)}
            onSave={async () => {
              setShowAddForm(false);
              await onReload();
            }}
          />
        )
        : (
          <button
            type="button"
            class="btn btn-primary"
            onClick={() => setShowAddForm(true)}
          >
            + Nouvelle mobilité
          </button>
        )}
    </div>
  );
}

// ─── Inline forms ───────────────────────────────────────────

function MobEditForm(
  { mob, ecoles, paysList, onCancel, onSave }: {
    mob: Mobilite;
    ecoles: string[];
    paysList: string[];
    onCancel: () => void;
    onSave: () => Promise<void>;
  },
) {
  const [duree, setDuree] = useState(String(mob.duree));
  const [ecole, setEcole] = useState(mob.ecole ?? "");
  const [pays, setPays] = useState(mob.pays ?? "");
  const [status, setStatus] = useState(mob.status);
  const [busy, setBusy] = useState(false);

  async function submit() {
    setBusy(true);
    try {
      const res = await fetch(`/mobility/api/mobilites/${mob.id}`, {
        method: "PUT",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          duree: parseInt(duree),
          ecole: ecole || null,
          pays: pays || null,
          status,
        }),
      });
      if (!res.ok) throw new Error("Erreur");
      await onSave();
    } catch {
      alert("Erreur lors de la modification");
    } finally {
      setBusy(false);
    }
  }

  return (
    <div class="edit-section" style={{ marginBottom: "1rem" }}>
      <p class="edit-section-title">Modifier la mobilité #{mob.id}</p>
      <div class="form-grid">
        <div class="form-field">
          <label>Durée (semaines)</label>
          <input
            class="form-input"
            type="number"
            min="1"
            value={duree}
            onInput={(e) => setDuree((e.target as HTMLInputElement).value)}
          />
        </div>
        {!mob.idStage && (
          <>
            <div class="form-field">
              <label>École</label>
              <input
                class="form-input"
                list="edit-ecoles"
                value={ecole}
                onInput={(e) => setEcole((e.target as HTMLInputElement).value)}
              />
              <datalist id="edit-ecoles">
                {ecoles.map((e) => <option key={e} value={e} />)}
              </datalist>
            </div>
            <div class="form-field">
              <label>Pays</label>
              <input
                class="form-input"
                list="edit-pays"
                value={pays}
                onInput={(e) => setPays((e.target as HTMLInputElement).value)}
              />
              <datalist id="edit-pays">
                {paysList.map((p) => <option key={p} value={p} />)}
              </datalist>
            </div>
            <div class="form-field">
              <label>Status</label>
              <select
                class="filter-select"
                value={status}
                onChange={(e) =>
                  setStatus((e.target as HTMLSelectElement).value)}
              >
                {STATUS_ORDER.map((s) => (
                  <option key={s} value={s}>{STATUS_LABELS[s]}</option>
                ))}
              </select>
            </div>
          </>
        )}
      </div>
      <div style={{ display: "flex", gap: "0.5rem", marginTop: "0.5rem" }}>
        <button
          type="button"
          class="btn btn-sm btn-primary"
          disabled={busy}
          onClick={submit}
        >
          Enregistrer
        </button>
        <button
          type="button"
          class="btn btn-sm btn-secondary"
          onClick={onCancel}
        >
          Annuler
        </button>
      </div>
    </div>
  );
}

function MobAddForm(
  { numEtud, ecoles, paysList, availableStages, onCancel, onSave }: {
    numEtud: number;
    ecoles: string[];
    paysList: string[];
    availableStages: Stage[];
    onCancel: () => void;
    onSave: () => Promise<void>;
  },
) {
  const [duree, setDuree] = useState("4");
  const [ecole, setEcole] = useState("");
  const [pays, setPays] = useState("");
  const [status, setStatus] = useState("contracts_received");
  const [selectedStageId, setSelectedStageId] = useState("");
  const [busy, setBusy] = useState(false);

  const isStageLinked = selectedStageId !== "";

  function onStageChange(value: string) {
    setSelectedStageId(value);
    if (value) {
      const stage = availableStages.find((s) => s.id === Number(value));
      if (stage) setDuree(String(stage.duree));
    }
  }

  async function submit() {
    setBusy(true);
    try {
      const res = await fetch("/mobility/api/mobilites", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          numEtud,
          duree: parseInt(duree),
          ecole: isStageLinked ? null : (ecole || null),
          pays: isStageLinked ? null : (pays || null),
          status: isStageLinked ? "validated" : status,
          idStage: isStageLinked ? Number(selectedStageId) : null,
        }),
      });
      if (!res.ok) throw new Error("Erreur");
      await onSave();
    } catch {
      alert("Erreur lors de la création");
    } finally {
      setBusy(false);
    }
  }

  return (
    <div class="edit-section" style={{ marginBottom: "1rem" }}>
      <p class="edit-section-title">Nouvelle mobilité</p>
      <div class="form-grid">
        {availableStages.length > 0 && (
          <div class="form-field">
            <label>Lier à un stage</label>
            <select
              class="filter-select"
              value={selectedStageId}
              onChange={(e) =>
                onStageChange((e.target as HTMLSelectElement).value)}
            >
              <option value="">— Mobilité d'étude —</option>
              {availableStages.map((s) => (
                <option key={s.id} value={String(s.id)}>
                  {s.nomEntreprise} ({s.duree} sem.)
                </option>
              ))}
            </select>
          </div>
        )}
        <div class="form-field">
          <label>Durée (semaines)</label>
          <input
            class="form-input"
            type="number"
            min="1"
            value={duree}
            onInput={(e) => setDuree((e.target as HTMLInputElement).value)}
          />
        </div>
        {!isStageLinked && (
          <>
            <div class="form-field">
              <label>École</label>
              <input
                class="form-input"
                list="add-ecoles"
                value={ecole}
                onInput={(e) => setEcole((e.target as HTMLInputElement).value)}
              />
              <datalist id="add-ecoles">
                {ecoles.map((e) => <option key={e} value={e} />)}
              </datalist>
            </div>
            <div class="form-field">
              <label>Pays</label>
              <input
                class="form-input"
                list="add-pays"
                value={pays}
                onInput={(e) => setPays((e.target as HTMLInputElement).value)}
              />
              <datalist id="add-pays">
                {paysList.map((p) => <option key={p} value={p} />)}
              </datalist>
            </div>
            <div class="form-field">
              <label>Status</label>
              <select
                class="filter-select"
                value={status}
                onChange={(e) =>
                  setStatus((e.target as HTMLSelectElement).value)}
              >
                {STATUS_ORDER.map((s) => (
                  <option key={s} value={s}>{STATUS_LABELS[s]}</option>
                ))}
              </select>
            </div>
          </>
        )}
      </div>
      {isStageLinked && (
        <p
          style={{
            fontSize: "0.8rem",
            opacity: 0.7,
            margin: "0.4rem 0",
          }}
        >
          Mobilité liée à un stage — status automatiquement « Validé »
        </p>
      )}
      <div style={{ display: "flex", gap: "0.5rem", marginTop: "0.5rem" }}>
        <button
          type="button"
          class="btn btn-sm btn-primary"
          disabled={busy || !duree}
          onClick={submit}
        >
          Créer
        </button>
        <button
          type="button"
          class="btn btn-sm btn-secondary"
          onClick={onCancel}
        >
          Annuler
        </button>
      </div>
    </div>
  );
}

function UploadContratBtn(
  { mobId, hasContrat, onDone }: {
    mobId: number;
    hasContrat: boolean;
    onDone: () => Promise<void>;
  },
) {
  const [busy, setBusy] = useState(false);

  function upload() {
    const input = document.createElement("input");
    input.type = "file";
    input.accept = "application/pdf";
    input.onchange = async () => {
      const file = input.files?.[0];
      if (!file) return;
      setBusy(true);
      try {
        const fd = new FormData();
        fd.append("contrat", file);
        const res = await fetch(`/mobility/api/mobilites/${mobId}/contrat`, {
          method: "POST",
          body: fd,
        });
        if (!res.ok) throw new Error("Erreur upload");
        await onDone();
      } catch {
        alert("Erreur lors de l'upload du contrat");
      } finally {
        setBusy(false);
      }
    };
    input.click();
  }

  return (
    <button
      type="button"
      class="btn btn-sm btn-secondary"
      disabled={busy}
      onClick={upload}
    >
      {busy ? "..." : hasContrat ? "Remplacer contrat" : "Ajouter contrat"}
    </button>
  );
}
