import { useEffect, useId, useRef, useState, type ChangeEvent } from 'react'
import {
  exportConfigJson,
  exportSynologyBackupJson,
  GANTT_SPRINT_METRIC_OPTIONS,
  mergeImportedConfig,
  normalizeFunctionalGapsForSave,
  parseGanttNonWorkingDatesFromText,
  sanitizeExcludedSprintIds,
  type DashboardConfig,
  type FunctionalGapBadge,
  type GanttSprintRowMetric,
  type LaneLabelsConfig,
  type Milestone,
  type MilestoneKind,
  type StatusBucketConfig,
} from '../lib/dashboardConfig'
import type { JiraSprintSnapshot } from '../lib/sprintExtract'
import { MILESTONE_KIND_OPTIONS } from '../lib/milestoneKinds'

function parseBucketLines(raw: string): string[] {
  return raw
    .split(/[\n,;]+/)
    .map((s) => s.trim())
    .filter(Boolean)
}

const BUCKET_FIELD_DEFS: {
  key: keyof StatusBucketConfig
  title: string
  hint: string
}[] = [
  { key: 'todo', title: 'À faire', hint: 'Ex. À faire, Open, Backlog' },
  { key: 'in_progress', title: 'En cours', hint: 'Ex. In Progress, Code Review, Recette' },
  { key: 'blocked', title: 'Bloqué', hint: 'Ex. Bloqué, Blocked' },
  { key: 'done', title: 'Terminé', hint: 'Ex. Done, Terminé, Closed, Livré' },
  { key: 'cancel', title: 'Annulé', hint: 'Ex. Annulé, Cancelled, Won\'t fix' },
]

const LANE_LABEL_FIELD_DEFS: {
  key: keyof LaneLabelsConfig
  title: string
  hint: string
}[] = [
  {
    key: 'analyse',
    title: 'Piste Analyse',
    hint: 'Étiquettes Jira comptées comme Analyse (ex. analyse). Une par ligne ou virgules.',
  },
  {
    key: 'design',
    title: 'Piste Design',
    hint: 'Ex. design, maquette (si vous utilisez ces étiquettes en Jira).',
  },
  {
    key: 'integration',
    title: 'Piste Intégration',
    hint: 'Ex. integration — ou tout libellé d’étiquette utilisé côté dev / recette.',
  },
  {
    key: 'blocked',
    title: 'Marquage bloqué (étiquettes)',
    hint: 'Ex. blocked : les tickets portant l’une de ces étiquettes sont signalés comme bloqués par étiquette (liste et tooltips).',
  },
]

type Props = {
  open: boolean
  config: DashboardConfig
  onClose: () => void
  onSave: (next: DashboardConfig) => void
  /** Sprints board (API) pour masquage sélectif ; optionnel si pas encore chargés. */
  boardSprints?: JiraSprintSnapshot[]
}

function newMilestone(): Milestone {
  return {
    id: typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : `m-${Date.now()}`,
    title: '',
    date: new Date().toISOString().slice(0, 10),
    linkedStoryKeys: [],
    critical: false,
    kind: 'generic',
    expectedActions: undefined,
  }
}

function newGapBadge(): FunctionalGapBadge {
  return {
    id:
      typeof crypto !== 'undefined' && crypto.randomUUID
        ? `gap-${crypto.randomUUID().slice(0, 10)}`
        : `gap-${Date.now()}`,
    label: '',
    terms: [''],
    criticalFlow: false,
  }
}

function parseGapTerms(raw: string): string[] {
  return raw
    .split(/[,;]+/)
    .map((s) => s.trim())
    .filter(Boolean)
}

export function DashboardSettingsModal({ open, config, onClose, onSave, boardSprints }: Props) {
  const dialogRef = useRef<HTMLDialogElement>(null)
  const fileRef = useRef<HTMLInputElement>(null)
  const titleId = useId()
  const [draft, setDraft] = useState<DashboardConfig>(config)
  const [ganttNonWorkInput, setGanttNonWorkInput] = useState('')

  useEffect(() => {
    if (open) setDraft(config)
  }, [open, config])

  const configNonWorkKey = config.ganttNonWorkingDates.join('|')
  useEffect(() => {
    if (!open) return
    setGanttNonWorkInput(config.ganttNonWorkingDates.join('\n'))
  }, [open, configNonWorkKey])

  useEffect(() => {
    const el = dialogRef.current
    if (!el) return
    if (open) {
      if (!el.open) el.showModal()
    } else if (el.open) {
      el.close()
    }
  }, [open])

  const updateMilestone = (id: string, patch: Partial<Milestone>) => {
    setDraft((d) => ({
      ...d,
      milestones: d.milestones.map((m) => (m.id === id ? { ...m, ...patch } : m)),
    }))
  }

  const removeMilestone = (id: string) => {
    setDraft((d) => ({ ...d, milestones: d.milestones.filter((m) => m.id !== id) }))
  }

  const addMilestone = () => {
    setDraft((d) => ({ ...d, milestones: [...d.milestones, newMilestone()] }))
  }

  const onImportFile = (e: ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0]
    if (!file) return
    const reader = new FileReader()
    reader.onload = () => {
      try {
        const parsed = JSON.parse(String(reader.result)) as unknown
        const merged = mergeImportedConfig(draft, parsed)
        if (merged) {
          setDraft(merged)
          setGanttNonWorkInput(merged.ganttNonWorkingDates.join('\n'))
        } else alert('Fichier JSON invalide (configuration v1 ou bundle Synology v1).')
      } catch {
        alert('Impossible de lire ce fichier JSON.')
      }
      e.target.value = ''
    }
    reader.readAsText(file)
  }

  return (
    <dialog
      ref={dialogRef}
      aria-labelledby={titleId}
      className="max-h-[90vh] w-[min(100%,520px)] overflow-y-auto rounded-2xl border border-white/10 bg-slate-950/95 p-0 text-slate-200 shadow-2xl backdrop:bg-black/70"
      onClose={onClose}
      onCancel={(e) => {
        e.preventDefault()
        onClose()
      }}
    >
      <div className="border-b border-white/10 px-5 py-4">
        <h2 id={titleId} className="text-lg font-semibold text-white">
          Configuration dashboard
        </h2>
        <p className="mt-1 text-xs text-slate-500">
          Sauvegarde locale (navigateur). Exportez le JSON pour le versionner sur votre Synology.
        </p>
      </div>

      <div className="space-y-5 px-5 py-4">
        <div className="rounded-xl border border-amber-500/25 bg-amber-500/5 p-3">
          <p className="text-xs font-semibold uppercase tracking-wide text-amber-200/90">
            Capacité & reporting
          </p>
          <div className="mt-3 grid gap-3 sm:grid-cols-3">
            <div>
              <label className="text-[10px] uppercase text-slate-500">Effectif actif</label>
              <input
                type="number"
                min={0.25}
                step={0.25}
                className="mt-1 w-full rounded-lg border border-white/10 bg-black/30 px-2 py-1.5 text-sm outline-none"
                value={draft.teamCapacity}
                onChange={(e) =>
                  setDraft((d) => ({
                    ...d,
                    teamCapacity: Math.max(0.25, Number(e.target.value) || 0.25),
                  }))
                }
              />
            </div>
            <div>
              <label className="text-[10px] uppercase text-slate-500">Baseline vélocité</label>
              <input
                type="number"
                min={0.25}
                step={0.25}
                className="mt-1 w-full rounded-lg border border-white/10 bg-black/30 px-2 py-1.5 text-sm outline-none"
                value={draft.baselineCapacity}
                onChange={(e) =>
                  setDraft((d) => ({
                    ...d,
                    baselineCapacity: Math.max(0.25, Number(e.target.value) || 0.25),
                  }))
                }
              />
            </div>
            <div>
              <label className="text-[10px] uppercase text-slate-500">WIP / pers.</label>
              <input
                type="number"
                min={1}
                step={1}
                className="mt-1 w-full rounded-lg border border-white/10 bg-black/30 px-2 py-1.5 text-sm outline-none"
                value={draft.wipSlotsPerDev}
                onChange={(e) =>
                  setDraft((d) => ({
                    ...d,
                    wipSlotsPerDev: Math.max(1, Math.floor(Number(e.target.value) || 1)),
                  }))
                }
              />
            </div>
          </div>
          <div className="mt-3">
            <label className="text-[10px] uppercase text-slate-500">
              Champ Sprint Jira (ID customfield)
            </label>
            <input
              className="mt-1 w-full rounded-lg border border-white/10 bg-black/30 px-2 py-1.5 font-mono text-xs outline-none"
              value={draft.sprintFieldId ?? ''}
              onChange={(e) =>
                setDraft((d) => ({
                  ...d,
                  sprintFieldId: e.target.value.trim() || undefined,
                }))
              }
              placeholder="ex. customfield_10020 (vide = utiliser VITE_JIRA_SPRINT_FIELD ou désactiver)"
            />
            <p className="mt-1 text-[10px] text-slate-600">
              Laissez vide pour n’utiliser que la variable d’environnement, ou saisissez l’ID exact du
              champ Sprint de votre projet Scrum.
            </p>
          </div>
          <p className="mt-2 text-[10px] text-slate-500">
            La date d’atterrissage utilise : vélocité × (effectif / baseline). WIP = créneaux
            nominaux pour la jauge « Ressources ».
          </p>
        </div>

        <div>
          <label className="block text-xs font-medium uppercase tracking-wide text-slate-500">
            Mon accountId Jira (recommandé pour « Ma vue »)
          </label>
          <input
            className="mt-1 w-full rounded-lg border border-white/10 bg-black/30 px-3 py-2 text-sm outline-none ring-cyan-500/30 focus:ring-2"
            value={draft.myJiraAccountId ?? ''}
            onChange={(e) => setDraft((d) => ({ ...d, myJiraAccountId: e.target.value || undefined }))}
            placeholder="ex. 5b10a2844c20165700ede21g"
          />
        </div>
        <div>
          <label className="block text-xs font-medium uppercase tracking-wide text-slate-500">
            Mon e-mail Jira (alternative si visible dans l’API)
          </label>
          <input
            type="email"
            className="mt-1 w-full rounded-lg border border-white/10 bg-black/30 px-3 py-2 text-sm outline-none ring-cyan-500/30 focus:ring-2"
            value={draft.myJiraEmail ?? ''}
            onChange={(e) => setDraft((d) => ({ ...d, myJiraEmail: e.target.value || undefined }))}
            placeholder="vous@entreprise.com"
          />
        </div>

        <div className="rounded-xl border border-violet-500/25 bg-violet-500/5 p-3">
          <p className="text-xs font-semibold uppercase tracking-wide text-violet-200/90">
            Cartographie des statuts Jira
          </p>
          <p className="mt-1 text-[10px] leading-relaxed text-slate-500">
            Libellés exacts des statuts dans Jira (un par ligne ou séparés par des virgules). La
            comparaison ignore casse et accents. Si un statut n’est dans aucune liste, la catégorie
            Jira (nouveau, en cours, terminé) sert de secours. Les listes vides au prochain
            chargement reprennent les valeurs par défaut.
          </p>
          <div className="mt-3 space-y-3">
            {BUCKET_FIELD_DEFS.map(({ key, title, hint }) => (
              <div key={key}>
                <label className="text-[10px] font-medium uppercase tracking-wide text-slate-400">
                  {title}
                </label>
                <p className="text-[10px] text-slate-600">{hint}</p>
                <textarea
                  rows={3}
                  spellCheck={false}
                  className="mt-1 w-full resize-y rounded-lg border border-white/10 bg-black/30 px-2 py-1.5 font-mono text-[11px] leading-relaxed text-slate-200 outline-none ring-cyan-500/20 focus:ring-1"
                  value={draft.statusBuckets[key].join('\n')}
                  onChange={(e) =>
                    setDraft((d) => ({
                      ...d,
                      statusBuckets: { ...d.statusBuckets, [key]: parseBucketLines(e.target.value) },
                    }))
                  }
                />
              </div>
            ))}
          </div>
        </div>

        <div className="rounded-xl border border-cyan-500/25 bg-cyan-500/5 p-3">
          <p className="text-xs font-semibold uppercase tracking-wide text-cyan-200/90">
            Étiquettes Jira (pistes & bloqué)
          </p>
          <p className="mt-1 text-[10px] leading-relaxed text-slate-500">
            Les sous-tâches sont classées en Analyse / Design / Intégration si elles portent au moins
            une des étiquettes listées (ordre de priorité : Analyse, puis Design, puis Intégration).
            Sinon, l’ancienne détection par mots dans le résumé et le statut s’applique. Les listes
            vides au prochain chargement reprennent les valeurs par défaut (analyse, design,
            integration, blocked).
          </p>
          <div className="mt-3 space-y-3">
            {LANE_LABEL_FIELD_DEFS.map(({ key, title, hint }) => (
              <div key={key}>
                <label className="text-[10px] font-medium uppercase tracking-wide text-slate-400">
                  {title}
                </label>
                <p className="text-[10px] text-slate-600">{hint}</p>
                <textarea
                  rows={2}
                  spellCheck={false}
                  className="mt-1 w-full resize-y rounded-lg border border-white/10 bg-black/30 px-2 py-1.5 font-mono text-[11px] leading-relaxed text-slate-200 outline-none ring-cyan-500/20 focus:ring-1"
                  value={draft.laneLabels[key].join('\n')}
                  onChange={(e) =>
                    setDraft((d) => ({
                      ...d,
                      laneLabels: { ...d.laneLabels, [key]: parseBucketLines(e.target.value) },
                    }))
                  }
                />
              </div>
            ))}
          </div>
        </div>

        <div className="rounded-xl border border-indigo-500/25 bg-indigo-500/5 p-3">
          <p className="text-xs font-semibold uppercase tracking-wide text-indigo-200/90">
            Gantt & vue Sprint
          </p>
          <label className="mt-2 block text-[10px] font-semibold uppercase tracking-wide text-slate-500">
            Infos sous les barres (Gantt)
          </label>
          <select
            className="mt-1 w-full rounded-lg border border-white/10 bg-black/30 px-2 py-2 text-sm text-slate-200 outline-none"
            value={draft.ganttSprintRowMetric}
            onChange={(e) =>
              setDraft((d) => ({
                ...d,
                ganttSprintRowMetric: e.target.value as GanttSprintRowMetric,
              }))
            }
          >
            {GANTT_SPRINT_METRIC_OPTIONS.map((o) => (
              <option key={o.value} value={o.value}>
                {o.label}
              </option>
            ))}
          </select>
          <label className="mt-3 block text-[10px] font-semibold uppercase tracking-wide text-indigo-200/80">
            Jours non travaillés (Gantt)
          </label>
          <p className="mt-1 text-[10px] leading-relaxed text-slate-500">
            Une date par ligne au format <span className="font-mono text-slate-400">AAAA-MM-JJ</span> (fuseau
            local du navigateur). Même style que sam. / dim. : fériés, ponts, fermeture.
          </p>
          <textarea
            rows={4}
            spellCheck={false}
            placeholder={'2026-05-01\n2026-05-08'}
            className="mt-1.5 w-full resize-y rounded-lg border border-white/10 bg-black/30 px-2 py-1.5 font-mono text-[11px] leading-relaxed text-slate-200 outline-none ring-indigo-500/20 focus:ring-1"
            value={ganttNonWorkInput}
            onChange={(e) => setGanttNonWorkInput(e.target.value)}
          />
          <p className="mt-3 text-[10px] font-semibold uppercase tracking-wide text-indigo-200/80">
            Sprints à masquer
          </p>
          <p className="mt-1 text-[10px] leading-relaxed text-slate-500">
            Sprints cochés : retirés du Gantt et du menu de la vue Sprint. Rechargez les données si la
            liste est vide.
          </p>
          {boardSprints && boardSprints.length > 0 ? (
            <ul className="mt-2 max-h-44 space-y-2 overflow-y-auto pr-1">
              {boardSprints.map((sp) => (
                <li key={sp.id} className="flex items-start gap-2 text-xs text-slate-200">
                  <input
                    type="checkbox"
                    id={`ex-sp-${sp.id}`}
                    checked={draft.excludedSprintIds.includes(sp.id)}
                    onChange={(e) =>
                      setDraft((d) => {
                        const next = new Set(d.excludedSprintIds)
                        if (e.target.checked) next.add(sp.id)
                        else next.delete(sp.id)
                        return { ...d, excludedSprintIds: [...next] }
                      })
                    }
                    className="mt-0.5 rounded border-indigo-400/50"
                  />
                  <label htmlFor={`ex-sp-${sp.id}`} className="cursor-pointer leading-snug">
                    <span className="font-medium">{sp.name}</span>
                    <span className="ml-2 font-mono text-[10px] text-slate-500">#{sp.id}</span>
                    {sp.state ? (
                      <span className="ml-2 text-[10px] uppercase text-slate-500">{sp.state}</span>
                    ) : null}
                  </label>
                </li>
              ))}
            </ul>
          ) : (
            <p className="mt-2 text-[11px] text-slate-500">
              Aucun sprint en mémoire : actualisez le cockpit puis rouvrez les réglages.
            </p>
          )}
        </div>

        <div className="rounded-xl border border-rose-500/20 bg-rose-500/[0.06] p-3">
          <div className="mb-2 flex items-center justify-between">
            <span className="text-xs font-semibold uppercase tracking-wide text-rose-100/90">
              Badges « gaps » (PO)
            </span>
            <button
              type="button"
              onClick={() =>
                setDraft((d) => ({ ...d, functionalGaps: [...d.functionalGaps, newGapBadge()] }))
              }
              className="text-xs font-medium text-rose-200/90 hover:text-rose-100"
            >
              + Ajouter
            </button>
          </div>
          <p className="text-[10px] leading-relaxed text-slate-500">
            Termes cherchés dans clés, résumés et étiquettes (insensible casse / accents). Cochez
            « flux critique » pour renforcer le feu rouge macro (Panier, Checkout…).
          </p>
          <ul className="mt-3 max-h-48 space-y-3 overflow-y-auto pr-1">
            {draft.functionalGaps.map((g) => (
              <li
                key={g.id}
                className="rounded-lg border border-white/10 bg-black/25 p-2 text-xs text-slate-200"
              >
                <div className="flex flex-wrap gap-2">
                  <input
                    className="min-w-[100px] flex-1 rounded border border-white/10 bg-transparent px-2 py-1"
                    value={g.label}
                    onChange={(e) =>
                      setDraft((d) => ({
                        ...d,
                        functionalGaps: d.functionalGaps.map((x) =>
                          x.id === g.id ? { ...x, label: e.target.value } : x,
                        ),
                      }))
                    }
                    placeholder="Libellé (ex. Panier)"
                  />
                  <label className="flex cursor-pointer items-center gap-1.5 text-[11px] text-rose-100/90">
                    <input
                      type="checkbox"
                      checked={Boolean(g.criticalFlow)}
                      onChange={(e) =>
                        setDraft((d) => ({
                          ...d,
                          functionalGaps: d.functionalGaps.map((x) =>
                            x.id === g.id ? { ...x, criticalFlow: e.target.checked } : x,
                          ),
                        }))
                      }
                      className="rounded border-rose-400/50"
                    />
                    Flux critique
                  </label>
                  <button
                    type="button"
                    onClick={() =>
                      setDraft((d) => ({
                        ...d,
                        functionalGaps: d.functionalGaps.filter((x) => x.id !== g.id),
                      }))
                    }
                    className="text-[11px] text-rose-400 hover:text-rose-300"
                  >
                    Supprimer
                  </button>
                </div>
                <textarea
                  rows={2}
                  spellCheck={false}
                  className="mt-2 w-full resize-y rounded border border-white/10 bg-black/30 px-2 py-1 font-mono text-[11px] outline-none"
                  value={g.terms.join(', ')}
                  onChange={(e) =>
                    setDraft((d) => ({
                      ...d,
                      functionalGaps: d.functionalGaps.map((x) =>
                        x.id === g.id ? { ...x, terms: parseGapTerms(e.target.value) } : x,
                      ),
                    }))
                  }
                  placeholder="panier, cart, basket…"
                />
              </li>
            ))}
          </ul>
        </div>

        <div>
          <div className="mb-2 flex items-center justify-between">
            <span className="text-xs font-medium uppercase tracking-wide text-slate-500">
              Jalons
            </span>
            <button
              type="button"
              onClick={addMilestone}
              className="text-xs font-medium text-cyan-400 hover:text-cyan-300"
            >
              + Ajouter
            </button>
          </div>
          <ul className="max-h-52 space-y-3 overflow-y-auto pr-1">
            {draft.milestones.map((m) => (
              <li
                key={m.id}
                className="rounded-xl border border-white/10 bg-black/25 p-3 text-sm"
              >
                <input
                  className="mb-2 w-full rounded border border-white/10 bg-transparent px-2 py-1 text-sm text-white"
                  value={m.title}
                  onChange={(e) => updateMilestone(m.id, { title: e.target.value })}
                  placeholder="ex. Fin design"
                />
                <div className="mb-2">
                  <label className="text-[10px] uppercase text-slate-500">Type de jalon</label>
                  <select
                    className="mt-1 w-full rounded border border-white/10 bg-black/30 px-2 py-1 text-xs text-slate-200 outline-none"
                    value={m.kind ?? 'generic'}
                    onChange={(e) =>
                      updateMilestone(m.id, { kind: e.target.value as MilestoneKind })
                    }
                  >
                    {MILESTONE_KIND_OPTIONS.map((opt) => (
                      <option key={opt.value} value={opt.value}>
                        {opt.label} — {opt.hint}
                      </option>
                    ))}
                  </select>
                </div>
                <div className="mb-2">
                  <label className="text-[10px] uppercase text-slate-500">Actions attendues à cette date</label>
                  <textarea
                    rows={2}
                    spellCheck={false}
                    className="mt-1 w-full resize-y rounded border border-white/10 bg-black/30 px-2 py-1 text-xs text-slate-200 outline-none"
                    value={m.expectedActions ?? ''}
                    onChange={(e) =>
                      updateMilestone(m.id, {
                        expectedActions: e.target.value.trim() ? e.target.value : undefined,
                      })
                    }
                    placeholder="Ex. Recette signée, doc runbook, passage en prod…"
                  />
                </div>
                <label className="mt-2 flex cursor-pointer items-center gap-2 text-[11px] text-amber-100/90">
                  <input
                    type="checkbox"
                    checked={Boolean(m.critical)}
                    onChange={(e) => updateMilestone(m.id, { critical: e.target.checked })}
                    className="rounded border-amber-400/50"
                  />
                  Jalon critique (alerte d’impact si retard)
                </label>
                <div className="mt-2 flex flex-wrap gap-2">
                  <input
                    type="date"
                    className="rounded border border-white/10 bg-transparent px-2 py-1 text-xs"
                    value={m.date}
                    onChange={(e) => updateMilestone(m.id, { date: e.target.value })}
                  />
                  <input
                    className="min-w-[120px] flex-1 rounded border border-white/10 bg-transparent px-2 py-1 text-xs"
                    value={(m.linkedStoryKeys ?? []).join(', ')}
                    onChange={(e) =>
                      updateMilestone(m.id, {
                        linkedStoryKeys: e.target.value
                          .split(/[,\s]+/)
                          .map((s) => s.trim())
                          .filter(Boolean),
                      })
                    }
                    placeholder="Stories (DCC-1, DCC-2) — vide = toutes les stories chargées"
                  />
                  <button
                    type="button"
                    onClick={() => removeMilestone(m.id)}
                    className="text-xs text-rose-400 hover:text-rose-300"
                  >
                    Supprimer
                  </button>
                </div>
              </li>
            ))}
          </ul>
        </div>

        <div className="flex flex-wrap gap-2 border-t border-white/10 pt-4">
          <button
            type="button"
            onClick={() => exportConfigJson(draft)}
            className="rounded-lg border border-emerald-500/40 bg-emerald-500/10 px-3 py-2 text-xs font-semibold text-emerald-100"
          >
            Exporter configuration (JSON)
          </button>
          <button
            type="button"
            onClick={() => exportSynologyBackupJson(draft)}
            className="rounded-lg border border-emerald-400/35 bg-emerald-950/40 px-3 py-2 text-xs font-semibold text-emerald-50"
            title="Enveloppe bundleVersion + exportedAt pour sauvegarde NAS / Docker Synology."
          >
            Bundle Synology (JSON)
          </button>
          <button
            type="button"
            onClick={() => fileRef.current?.click()}
            className="rounded-lg border border-white/15 bg-white/5 px-3 py-2 text-xs font-semibold text-slate-200"
          >
            Importer JSON…
          </button>
          <input
            ref={fileRef}
            type="file"
            accept="application/json,.json"
            className="hidden"
            onChange={onImportFile}
          />
        </div>
      </div>

      <div className="flex justify-end gap-2 border-t border-white/10 px-5 py-4">
        <button
          type="button"
          onClick={onClose}
          className="rounded-lg px-4 py-2 text-sm text-slate-400 hover:text-white"
        >
          Annuler
        </button>
        <button
          type="button"
          onClick={() => {
            onSave({
              ...draft,
              functionalGaps: normalizeFunctionalGapsForSave(draft.functionalGaps),
              excludedSprintIds: sanitizeExcludedSprintIds(draft.excludedSprintIds),
              ganttNonWorkingDates: parseGanttNonWorkingDatesFromText(ganttNonWorkInput),
            })
            onClose()
          }}
          className="rounded-lg bg-cyan-500 px-4 py-2 text-sm font-semibold text-slate-950 hover:bg-cyan-400"
        >
          Enregistrer
        </button>
      </div>
    </dialog>
  )
}
