// app/plan-table.jsx — Plan de table, gîtes, carte du domaine.
//
// L'app suppose que l'invité est déjà identifié (AuthGate). Donc :
//  - "Ma table" affiche directement la table de l'invité, ou un état
//    "pas encore attribué" si le backend n'a pas encore d'affectation.
//  - "Mon gîte" idem, avec la carte interactive uniquement si le gîte
//    est dans l'enceinte (gite.inChateau === true).
//  - "Tables" liste les 10 tables, chaque carte est cliquable pour
//    afficher la composition de la tablée.
// Le fuzzy match reste disponible en bouton "Chercher un autre invité"
// pour les cas où l'identité est en mode "saisie libre" (source: manual)
// ou pour aller voir où est placée une autre personne.

function normalize(s) {
  return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9\s]/g, "");
}
function fuzzyMatch(query, list) {
  const q = normalize(query).trim();
  if (!q || q.length < 2) return [];
  const qToks = q.split(/\s+/).filter(Boolean);
  return list.map((g) => {
    const n = normalize(g.name);
    const nToks = n.split(/\s+/);
    let score = 0;
    for (const qt of qToks) {
      for (const nt of nToks) {
        if (nt.startsWith(qt)) score += qt.length / nt.length + 0.5;
        else if (nt.includes(qt)) score += (qt.length / nt.length) * 0.5;
      }
    }
    return { ...g, score };
  }).filter(x => x.score > 0.3).sort((a, b) => b.score - a.score).slice(0, 6);
}

function PlanPage() {
  const [tab, setTab] = React.useState("table"); // table | all | sleep
  const backend = useAppBackend();
  const { tables, gites, allGuests, allSleepers, loading, source } = backend;

  return (
    <div style={{ position: "relative", overflow: "hidden", minHeight: "100vh" }}>
      <WashBg hue={60} opacity={0.16}/>
      <Page>
        <AppHeader eyebrow="Vous y êtes" title="Plan de table & gîtes" />
        <div style={{ textAlign: "center", fontFamily: A.italic, fontStyle: "italic", fontSize: 15, color: A.inkSoft, marginTop: 6 }}>
          Trouvez votre table ou votre gîte.
        </div>

        {source !== "backend" && (
          <div style={{
            marginTop: 14, padding: "8px 14px", textAlign: "center",
            background: source === "demo" ? A.rougePale : A.paper,
            border: `1px dashed ${source === "demo" ? A.rouge : A.ruleSoft}`,
            fontFamily: A.mono, fontSize: 9, letterSpacing: 1.5, color: source === "demo" ? A.rouge : A.inkMute,
            textTransform: "uppercase",
          }}>
            {loading ? "· · · synchronisation" : source === "cache" ? "⚡ caché — dernier sync" : "⚠ mode démo — affectations à valider"}
          </div>
        )}

        {/* Tabs (3) */}
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 4, background: A.paper, padding: 4, marginTop: 24, marginBottom: 18 }}>
          {[
            { id: "table", l: "♦ Ma table" },
            { id: "all",   l: "♠ Tables" },
            { id: "sleep", l: "♣ Mon gîte" },
          ].map((t) => (
            <button key={t.id} onClick={() => setTab(t.id)} style={{
              padding: "10px 4px", background: tab === t.id ? A.ink : "transparent",
              color: tab === t.id ? A.white : A.inkSoft, border: "none",
              fontFamily: A.mono, fontSize: 9, letterSpacing: 1, textTransform: "uppercase",
              cursor: "pointer", fontWeight: 600,
            }}>{t.l}</button>
          ))}
        </div>

        {tab === "table" && <MyTableView allGuests={allGuests} tables={tables} />}
        {tab === "all" && <AllTablesView tables={tables} />}
        {tab === "sleep" && <MyGiteView allSleepers={allSleepers} gites={gites} />}
      </Page>
    </div>
  );
}

// ─────────────────────────────────────────────────────────
// MA TABLE — auto-affichage selon identité, fallback fuzzy match
// ─────────────────────────────────────────────────────────
function MyTableView({ allGuests, tables }) {
  const { identity } = useGuest();
  const [searchMode, setSearchMode] = React.useState(false);

  // L'identité a-t-elle un tableId fiable ?
  const myAssignment = React.useMemo(() => {
    if (!identity?.name) return null;
    // 1) Si l'identité a un tableId stocké, on l'utilise
    if (identity.tableId) {
      const t = tables.find(x => x.id === identity.tableId);
      if (t) return { table: t, name: identity.name };
    }
    // 2) Sinon, on cherche dans allGuests par nom exact (normalisé)
    const me = allGuests.find(g => normalize(g.name) === normalize(identity.name));
    if (me) {
      const t = tables.find(x => x.id === me.tableId);
      if (t) return { table: t, name: me.name };
    }
    return null;
  }, [identity, allGuests, tables]);

  const empty = !allGuests.length && !tables.some(t => (t.guests || []).length);
  const notAssignedYet = !empty && !myAssignment;

  // Mode recherche libre (pour voir où est quelqu'un d'autre)
  if (searchMode) {
    return <TableSearchFreeMode allGuests={allGuests} tables={tables} onClose={() => setSearchMode(false)} />;
  }

  // Cas 1 : on a trouvé la table → afficher directement
  if (myAssignment) {
    return (
      <div className="anim-up">
        <TableDetail
          tableId={myAssignment.table.id}
          highlight={myAssignment.name}
          tables={tables}
          onBack={null}
          mineLabel={`Bonjour ${identity.firstName || identity.name.split(" ")[0]}`}
        />
        <div style={{ marginTop: 18, textAlign: "center" }}>
          <button onClick={() => setSearchMode(true)} style={ghostLinkStyle()}>
            ✦ Chercher un autre invité →
          </button>
        </div>
      </div>
    );
  }

  // Cas 2 : pas encore attribué (identité OK mais pas de table)
  if (notAssignedYet) {
    return (
      <div className="anim-up">
        <div style={{ background: A.card, padding: "26px 24px", border: `1px dashed ${A.rule}`, textAlign: "center" }}>
          <div style={{ fontFamily: A.display, fontStyle: "italic", fontSize: 48, color: A.or, lineHeight: 1 }}>♦</div>
          <div className="display-bold" style={{ fontSize: 26, color: A.ink, marginTop: 10, letterSpacing: -0.5 }}>
            Pas encore attribué
          </div>
          <div style={{ fontFamily: A.italic, fontStyle: "italic", fontSize: 14.5, color: A.inkSoft, marginTop: 8, lineHeight: 1.55, maxWidth: 360, margin: "8px auto 0" }}>
            {identity?.firstName ? `${identity.firstName}, votre` : "Votre"} place à table sera attribuée quelques semaines avant le mariage. Revenez bientôt ici pour la découvrir.
          </div>
          <div style={{ marginTop: 18 }}>
            <ButtonA onClick={() => setSearchMode(true)}>✦ Chercher un autre invité</ButtonA>
          </div>
        </div>
      </div>
    );
  }

  // Cas 3 : le plan complet est vide (placeholders pas chargés)
  return (
    <div className="anim-up">
      <div style={{ padding: "20px 18px", background: A.paper, border: `1px dashed ${A.ruleSoft}`, fontFamily: A.italic, fontStyle: "italic", fontSize: 14, color: A.inkSoft, textAlign: "center", lineHeight: 1.5 }}>
        Le plan de table n'est pas encore finalisé. Revenez quelques semaines avant le mariage.
      </div>
      <div style={{ marginTop: 14, textAlign: "center" }}>
        <button onClick={() => setSearchMode(true)} style={ghostLinkStyle()}>
          ✦ Chercher un invité quand même
        </button>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────
// Recherche libre — fuzzy match pour aller voir où est quelqu'un
// ─────────────────────────────────────────────────────────
function TableSearchFreeMode({ allGuests, tables, onClose }) {
  const [query, setQuery] = React.useState("");
  const [selected, setSelected] = React.useState(null);
  const matches = React.useMemo(() => fuzzyMatch(query, allGuests), [query, allGuests]);
  const empty = !allGuests.length;
  return (
    <div className="anim-up">
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 12 }}>
        <div className="eyebrow" style={{ color: A.or }}>★ Chercher un invité</div>
        <button onClick={onClose} style={ghostLinkStyle()}>← Retour à ma table</button>
      </div>
      <input type="text" value={query} onChange={(e) => { setQuery(e.target.value); setSelected(null); }}
        placeholder={empty ? "Affectations en cours…" : "Tapez un prénom et nom…"} autoFocus
        disabled={empty}
        style={inputStyle()}/>
      {empty && (
        <div style={{ marginTop: 14, padding: "14px 16px", background: A.paper, border: `1px dashed ${A.ruleSoft}`, fontFamily: A.italic, fontStyle: "italic", fontSize: 13, color: A.inkSoft, textAlign: "center" }}>
          Le plan de table n'est pas encore finalisé.
        </div>
      )}
      {matches.length > 0 && !selected && (
        <div style={{ marginTop: 10, display: "flex", flexDirection: "column", gap: 4 }}>
          {matches.map((m) => (
            <button key={m.name} onClick={() => setSelected(m)} style={resultRowStyle()}>
              <span style={{ color: A.ink, fontWeight: 500 }}>{m.name}</span>
              <span style={{ fontFamily: A.mono, fontSize: 10, color: A.or, letterSpacing: 1 }}>{m.tableName} →</span>
            </button>
          ))}
        </div>
      )}
      {selected && (
        <TableDetail tableId={selected.tableId} highlight={selected.name}
          tables={tables}
          onBack={() => { setSelected(null); setQuery(""); }}
          mineLabel={null}
        />
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────
// TABLES — grille de toutes les tables, cliquable pour voir composition
// ─────────────────────────────────────────────────────────
function AllTablesView({ tables }) {
  const [openId, setOpenId] = React.useState(null);

  if (!tables.length) {
    return (
      <div style={{ padding: 24, background: A.paper, border: `1px dashed ${A.ruleSoft}`, textAlign: "center", fontFamily: A.italic, fontStyle: "italic", color: A.inkSoft }}>
        Les tables seront publiées ici quand le plan sera finalisé.
      </div>
    );
  }

  return (
    <div>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
        {tables.map((t) => (
          <InfoCard
            key={t.id}
            accent={t.color}
            onClick={() => setOpenId(t.id)}
            style={{ padding: "14px 16px", borderLeft: `4px solid ${t.color}` }}
          >
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 8 }}>
              <div style={{ minWidth: 0, flex: 1 }}>
                <div style={{ fontFamily: A.display, fontWeight: 700, fontSize: 22, color: A.ink, lineHeight: 1 }}>{t.name}</div>
                {t.note && <div style={{ fontFamily: A.italic, fontStyle: "italic", fontSize: 12, color: A.inkSoft, marginTop: 4, overflow: "hidden", textOverflow: "ellipsis" }}>{t.note}</div>}
              </div>
              <div style={{ color: t.color, fontFamily: A.display, fontSize: 18, flexShrink: 0 }}>→</div>
            </div>
            <div style={{ fontFamily: A.mono, fontSize: 9, letterSpacing: 1.5, color: t.color, marginTop: 8, textTransform: "uppercase" }}>
              {t.guests.length} / {t.seats} couverts
            </div>
          </InfoCard>
        ))}
      </div>

      {/* Modal détail */}
      {openId && (
        <TableDetailModal
          tableId={openId}
          tables={tables}
          onClose={() => setOpenId(null)}
        />
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────
// MON GÎTE — auto-affichage selon identité, map si inChateau,
// autres gîtes du domaine restent cliquables pour exploration.
// ─────────────────────────────────────────────────────────
function MyGiteView({ allSleepers, gites }) {
  const { identity } = useGuest();
  const [searchMode, setSearchMode] = React.useState(false);
  // Quand l'utilisateur clique sur un autre gîte sur la map, on l'explore.
  // null = on est sur son propre gîte.
  const [exploringId, setExploringId] = React.useState(null);

  const myGite = React.useMemo(() => {
    if (!identity?.name) return null;
    if (identity.giteId) {
      const g = gites.find(x => x.id === identity.giteId);
      if (g) return { gite: g, name: identity.name };
    }
    const me = allSleepers.find(s => normalize(s.name) === normalize(identity.name));
    if (me) {
      const g = gites.find(x => x.id === me.giteId);
      if (g) return { gite: g, name: me.name };
    }
    return null;
  }, [identity, allSleepers, gites]);

  const empty = !allSleepers.length && !gites.some(g => (g.occupants || []).length);
  const notAssignedYet = !empty && !myGite && identity?.name;

  if (searchMode) {
    return <GiteSearchFreeMode allSleepers={allSleepers} gites={gites} onClose={() => setSearchMode(false)} />;
  }

  // Cas 1 : on a trouvé le gîte
  if (myGite) {
    const isInChateau = myGite.gite.inChateau !== false;
    return (
      <div className="anim-up">
        {isInChateau ? (
          // Variante avec map interactive : on peut explorer les autres gîtes
          <GiteInChateauExplorer
            myGite={myGite.gite}
            myName={myGite.name}
            gites={gites}
            exploringId={exploringId}
            onExplore={setExploringId}
          />
        ) : (
          // Variante sans map (hors château)
          <GiteOutsideChateau gite={myGite.gite} highlightName={myGite.name} />
        )}
        <div style={{ marginTop: 18, textAlign: "center" }}>
          <button onClick={() => setSearchMode(true)} style={ghostLinkStyle()}>
            ✦ Chercher où dort un autre invité →
          </button>
        </div>
      </div>
    );
  }

  // Cas 2 : pas encore attribué
  if (notAssignedYet) {
    return (
      <div className="anim-up">
        <div style={{ background: A.card, padding: "26px 24px", border: `1px dashed ${A.rule}`, textAlign: "center" }}>
          <div style={{ fontFamily: A.display, fontStyle: "italic", fontSize: 48, color: A.sage, lineHeight: 1 }}>♣</div>
          <div className="display-bold" style={{ fontSize: 26, color: A.ink, marginTop: 10, letterSpacing: -0.5 }}>
            Pas encore attribué
          </div>
          <div style={{ fontFamily: A.italic, fontStyle: "italic", fontSize: 14.5, color: A.inkSoft, marginTop: 8, lineHeight: 1.55, maxWidth: 380, margin: "8px auto 0" }}>
            {identity?.firstName ? `${identity.firstName}, votre` : "Votre"} couchage sera attribué quelques semaines avant le mariage.
            <br/><br/>
            <span style={{ color: A.inkMute, fontSize: 12.5 }}>
              Vous logez peut-être hors du domaine — on confirmera tout ça par email.
            </span>
          </div>
          <div style={{ marginTop: 18 }}>
            <ButtonA onClick={() => setSearchMode(true)}>✦ Chercher où dort un invité</ButtonA>
          </div>
        </div>
      </div>
    );
  }

  // Cas 3 : data globale vide
  return (
    <div className="anim-up">
      <div style={{ padding: "20px 18px", background: A.paper, border: `1px dashed ${A.ruleSoft}`, fontFamily: A.italic, fontStyle: "italic", fontSize: 14, color: A.inkSoft, textAlign: "center", lineHeight: 1.5 }}>
        Affectations des gîtes en cours de finalisation par Enora &amp; Antoine.
      </div>
    </div>
  );
}

// ─── Explorateur de gîtes (quand on est dans le château) ───────────────
// Affiche d'abord le gîte de l'invité, mais la map reste cliquable pour
// explorer les autres gîtes du domaine. Quand l'utilisateur clique sur
// un autre gîte, on bascule l'affichage de détail vers celui-là et on
// montre un bouton "← Retour à mon gîte".
function GiteInChateauExplorer({ myGite, myName, gites, exploringId, onExplore }) {
  const showing = exploringId
    ? (gites.find(g => g.id === exploringId) || myGite)
    : myGite;
  const isMine = !exploringId || showing.id === myGite.id;
  const accent = isMine ? A.sage : A.or;
  const occupants = showing.occupants || [];

  return (
    <div>
      {/* En-tête */}
      <div style={{ background: A.card, padding: 22, border: `1px solid ${A.ruleSoft}`, position: "relative", overflow: "hidden", marginBottom: 14 }}>
        <div style={{ position: "absolute", top: 0, left: 0, right: 0, height: 5, background: accent }}/>
        <div className="eyebrow" style={{ color: accent, marginTop: 6 }}>
          {isMine ? "Votre gîte · sur le domaine" : "Vous explorez · sur le domaine"}
        </div>
        <div style={{ fontFamily: A.display, fontWeight: 700, fontSize: 44, color: A.ink, marginTop: 4, lineHeight: 1, letterSpacing: -1 }}>
          {showing.name}
        </div>
        <div style={{ fontFamily: A.italic, fontStyle: "italic", fontSize: 15, color: A.inkSoft, marginTop: 4 }}>
          {showing.beds} couchage{showing.beds > 1 ? "s" : ""}
        </div>
        {!isMine && (
          <button onClick={() => onExplore(null)} style={{
            marginTop: 14, background: "transparent", border: `1.5px solid ${A.ink}`,
            padding: "8px 14px", color: A.ink, cursor: "pointer",
            fontFamily: A.mono, fontSize: 10, letterSpacing: 2, textTransform: "uppercase", fontWeight: 600,
          }}>← Retour à mon gîte</button>
        )}
      </div>

      {/* Map interactive — le gîte affiché est en surbrillance ; tap sur un autre = on bascule */}
      <GiteMap
        activeId={showing.id}
        onSelectGite={(id) => {
          if (!id || id === myGite.id) onExplore(null);
          else if (id === showing.id) onExplore(null);
          else onExplore(id);
        }}
        gites={gites}
      />
      <div style={{ marginTop: 8, fontFamily: A.mono, fontSize: 9, letterSpacing: 1.5, color: A.inkMute, textTransform: "uppercase", textAlign: "center" }}>
        → Touchez un autre gîte pour voir qui y dort
      </div>

      {/* Occupants du gîte affiché */}
      <div style={{ marginTop: 18, background: A.card, padding: 22, border: `1px solid ${A.ruleSoft}` }}>
        <div className="eyebrow" style={{ marginBottom: 10 }}>
          {isMine ? "Avec vous au gîte" : `Au gîte ${showing.name}`}
        </div>
        {occupants.length === 0 ? (
          <div style={{ fontFamily: A.italic, fontStyle: "italic", fontSize: 13, color: A.inkMute }}>
            Aucun occupant assigné pour l'instant.
          </div>
        ) : (
          <ul style={{ margin: 0, padding: 0, listStyle: "none", display: "flex", flexDirection: "column", gap: 6 }}>
            {occupants.map((o) => (
              <li key={o} style={listItemStyle(isMine && o === myName, accent)}>
                <span>{o}</span>
                {isMine && o === myName && <span style={{ fontFamily: A.mono, fontSize: 9, color: accent, letterSpacing: 1.5 }}>VOUS</span>}
              </li>
            ))}
          </ul>
        )}
      </div>
    </div>
  );
}

// ─── Variante "dans le château" : avec map interactive ──────────────────
function GiteInChateau({ gite, highlightName, gites }) {
  return (
    <div>
      <div style={{ background: A.card, padding: 22, border: `1px solid ${A.ruleSoft}`, position: "relative", overflow: "hidden", marginBottom: 14 }}>
        <div style={{ position: "absolute", top: 0, left: 0, right: 0, height: 5, background: A.sage }}/>
        <div className="eyebrow" style={{ color: A.sage, marginTop: 6 }}>Votre gîte · sur le domaine</div>
        <div style={{ fontFamily: A.display, fontWeight: 700, fontSize: 44, color: A.ink, marginTop: 4, lineHeight: 1, letterSpacing: -1 }}>{gite.name}</div>
        <div style={{ fontFamily: A.italic, fontStyle: "italic", fontSize: 15, color: A.inkSoft, marginTop: 4 }}>
          {gite.beds} couchage{gite.beds > 1 ? "s" : ""}
        </div>
      </div>

      {/* Map avec gîte actif en surbrillance */}
      <GiteMap activeId={gite.id} onSelectGite={() => {}} gites={gites} readonly={true} />

      {/* Occupants */}
      <div style={{ marginTop: 18, background: A.card, padding: 22, border: `1px solid ${A.ruleSoft}` }}>
        <div className="eyebrow" style={{ marginBottom: 10 }}>Avec vous au gîte</div>
        {gite.occupants.length === 0 ? (
          <div style={{ fontFamily: A.italic, fontStyle: "italic", fontSize: 13, color: A.inkMute }}>
            Aucun autre occupant assigné pour l'instant.
          </div>
        ) : (
          <ul style={{ margin: 0, padding: 0, listStyle: "none", display: "flex", flexDirection: "column", gap: 6 }}>
            {gite.occupants.map((o) => (
              <li key={o} style={listItemStyle(o === highlightName, A.sage)}>
                <span>{o}</span>
                {o === highlightName && <span style={{ fontFamily: A.mono, fontSize: 9, color: A.sage, letterSpacing: 1.5 }}>VOUS</span>}
              </li>
            ))}
          </ul>
        )}
      </div>
    </div>
  );
}

// ─── Variante "hors château" : juste texte ─────────────────────────────
function GiteOutsideChateau({ gite, highlightName }) {
  return (
    <div>
      <div style={{ background: A.card, padding: "24px 22px", border: `1px solid ${A.ruleSoft}`, position: "relative", overflow: "hidden" }}>
        <div style={{ position: "absolute", top: 0, left: 0, right: 0, height: 5, background: A.or }}/>
        <div className="eyebrow" style={{ color: A.or, marginTop: 6 }}>Votre gîte · hors du domaine</div>
        <div style={{ fontFamily: A.display, fontWeight: 700, fontSize: 40, color: A.ink, marginTop: 4, lineHeight: 1, letterSpacing: -1 }}>{gite.name}</div>
        <div style={{ fontFamily: A.italic, fontStyle: "italic", fontSize: 15, color: A.inkSoft, marginTop: 6 }}>
          {gite.beds} couchage{gite.beds > 1 ? "s" : ""}
        </div>
        {gite.note && (
          <div style={{ marginTop: 14, padding: "12px 14px", background: A.paper, borderLeft: `3px solid ${A.or}`, fontFamily: A.italic, fontStyle: "italic", fontSize: 14, color: A.ink, lineHeight: 1.5 }}>
            {gite.note}
          </div>
        )}
        <div style={{ marginTop: 16, fontFamily: A.italic, fontStyle: "italic", fontSize: 13, color: A.inkMute, lineHeight: 1.5 }}>
          Ce gîte n'est pas sur la carte du domaine du Château Sentout. Les détails (adresse, horaires d'arrivée) vous seront envoyés par email.
        </div>
      </div>

      {/* Occupants */}
      {gite.occupants.length > 0 && (
        <div style={{ marginTop: 14, background: A.card, padding: 22, border: `1px solid ${A.ruleSoft}` }}>
          <div className="eyebrow" style={{ marginBottom: 10 }}>Avec vous au gîte</div>
          <ul style={{ margin: 0, padding: 0, listStyle: "none", display: "flex", flexDirection: "column", gap: 6 }}>
            {gite.occupants.map((o) => (
              <li key={o} style={listItemStyle(o === highlightName, A.or)}>
                <span>{o}</span>
                {o === highlightName && <span style={{ fontFamily: A.mono, fontSize: 9, color: A.or, letterSpacing: 1.5 }}>VOUS</span>}
              </li>
            ))}
          </ul>
        </div>
      )}
    </div>
  );
}

// ─── Mode recherche libre pour les gîtes ───────────────────────────────
function GiteSearchFreeMode({ allSleepers, gites, onClose }) {
  const [query, setQuery] = React.useState("");
  const [selected, setSelected] = React.useState(null);
  const matches = React.useMemo(() => fuzzyMatch(query, allSleepers), [query, allSleepers]);
  return (
    <div className="anim-up">
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 12 }}>
        <div className="eyebrow" style={{ color: A.or }}>★ Chercher un invité</div>
        <button onClick={onClose} style={ghostLinkStyle()}>← Retour à mon gîte</button>
      </div>
      <input type="text" value={query} onChange={(e) => { setQuery(e.target.value); setSelected(null); }}
        placeholder="Tapez un prénom et nom…" autoFocus style={inputStyle()}/>
      {matches.length > 0 && !selected && (
        <div style={{ marginTop: 10, display: "flex", flexDirection: "column", gap: 4 }}>
          {matches.map((m) => (
            <button key={m.name} onClick={() => setSelected(m)} style={resultRowStyle()}>
              <span style={{ color: A.ink, fontWeight: 500 }}>{m.name}</span>
              <span style={{ fontFamily: A.mono, fontSize: 10, color: A.or, letterSpacing: 1 }}>Gîte {m.giteName} →</span>
            </button>
          ))}
        </div>
      )}
      {selected && (() => {
        const gite = gites.find(g => g.id === selected.giteId);
        if (!gite) return null;
        return gite.inChateau !== false
          ? <div style={{ marginTop: 16 }}><GiteInChateau gite={gite} highlightName={selected.name} gites={gites}/></div>
          : <div style={{ marginTop: 16 }}><GiteOutsideChateau gite={gite} highlightName={selected.name}/></div>;
      })()}
    </div>
  );
}

// ─── Modal détail table (utilisé depuis AllTablesView) ──────────────────
function TableDetailModal({ tableId, tables, onClose }) {
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);

  const t = tables.find(x => x.id === tableId);
  if (!t) return null;

  return (
    <div onClick={onClose} style={{
      position: "fixed", inset: 0, background: "rgba(26,22,18,0.62)",
      display: "flex", alignItems: "flex-end", justifyContent: "center",
      zIndex: 90, animation: "a-fade .25s ease both",
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        background: A.white, width: "100%", maxWidth: 720, maxHeight: "92vh",
        overflowY: "auto", animation: "modal-slide-up .35s cubic-bezier(.2,.7,.3,1) both",
        position: "relative",
      }}>
        <div style={{ position: "absolute", top: 0, left: 0, right: 0, height: 6, background: t.color }}/>
        <button onClick={onClose} style={{
          position: "absolute", top: 14, right: 14, zIndex: 2,
          background: "transparent", border: `1px solid ${A.ruleSoft}`,
          width: 32, height: 32, cursor: "pointer", color: A.inkSoft,
          fontFamily: A.mono, fontSize: 14, lineHeight: 1,
        }} aria-label="Fermer">×</button>
        <div style={{ padding: "30px 26px 32px" }}>
          <div className="eyebrow" style={{ color: t.color }}>★ Composition</div>
          <div style={{ fontFamily: A.display, fontWeight: 700, fontSize: 44, color: A.ink, marginTop: 4, lineHeight: 1, letterSpacing: -1 }}>{t.name}</div>
          {t.note && <div style={{ fontFamily: A.italic, fontStyle: "italic", fontSize: 15, color: A.inkSoft, marginTop: 4 }}>{t.note}</div>}
          <div style={{ marginTop: 8, fontFamily: A.mono, fontSize: 9, letterSpacing: 1.5, color: t.color, textTransform: "uppercase" }}>
            {t.guests.length} / {t.seats} couverts
          </div>

          <div style={{ marginTop: 22, paddingTop: 18, borderTop: `1px solid ${A.ruleSoft}` }}>
            <div className="eyebrow" style={{ marginBottom: 10 }}>Les invité·e·s à cette table</div>
            {t.guests.length === 0 ? (
              <div style={{ fontFamily: A.italic, fontStyle: "italic", fontSize: 14, color: A.inkMute, padding: "12px 0" }}>
                Aucun nom enregistré pour l'instant.
              </div>
            ) : (
              <ul style={{ margin: 0, padding: 0, listStyle: "none", display: "flex", flexDirection: "column", gap: 6 }}>
                {t.guests.map((g) => (
                  <li key={g} style={listItemStyle(false, t.color)}>
                    <span>{g}</span>
                  </li>
                ))}
              </ul>
            )}
          </div>
        </div>
      </div>
      <style>{`
        @keyframes modal-slide-up {
          from { transform: translateY(20px); opacity: 0.6; }
          to   { transform: translateY(0);    opacity: 1; }
        }
        @media (min-width: 720px) {
          /* sur grand écran : centré */
          [data-modal-center="1"] { align-items: center !important; }
        }
      `}</style>
    </div>
  );
}

// ─── GiteMap : image + SVG overlay polygones + callout ───────────────────
function GiteMap({ activeId, onSelectGite, gites, readonly = false }) {
  const [hoverId, setHoverId] = React.useState(null);
  const pointsStr = (pts) => pts.map(([x, y]) => `${x},${y}`).join(" ");

  return (
    <div style={{ position: "relative", display: "block", background: A.ink, border: `1px solid ${A.ruleSoft}` }}>
      <img src="assets/plan-gites.png" alt="Plan des gîtes du domaine"
        style={{ width: "100%", height: "auto", display: "block" }} />

      <svg viewBox="0 0 100 100" preserveAspectRatio="none"
        style={{ position: "absolute", inset: 0, width: "100%", height: "100%", overflow: "visible" }}>
        <defs>
          <mask id="gite-dim-mask">
            <rect x="0" y="0" width="100" height="100" fill="white" />
            {activeId && GITE_SHAPES[activeId] && (
              <polygon points={pointsStr(GITE_SHAPES[activeId])} fill="black" />
            )}
          </mask>
        </defs>

        {activeId && (
          <rect x="0" y="0" width="100" height="100" fill="#1A1612" opacity="0.55" mask="url(#gite-dim-mask)" style={{ animation: "gite-dim-in .35s ease both" }} />
        )}

        {Object.entries(GITE_SHAPES).map(([id, pts]) => {
          const active = id === activeId;
          const hovered = id === hoverId && !active && !readonly;
          return (
            <polygon
              key={id}
              points={pointsStr(pts)}
              fill={hovered ? "rgba(184,90,78,0.18)" : "transparent"}
              stroke={active ? "#FAFAF7" : (hovered ? "#B85A4E" : "transparent")}
              strokeWidth={active ? 0.6 : 0.4}
              vectorEffect="non-scaling-stroke"
              onClick={readonly ? undefined : (e) => { e.stopPropagation(); onSelectGite(active ? null : id); }}
              onMouseEnter={readonly ? undefined : () => setHoverId(id)}
              onMouseLeave={readonly ? undefined : () => setHoverId(null)}
              style={{ cursor: readonly ? "default" : "pointer", pointerEvents: readonly ? "none" : "all", transition: "fill .15s, stroke .15s" }}
            >
              <title>{gites.find(g => g.id === id)?.name}</title>
            </polygon>
          );
        })}

        {activeId && GITE_CENTERS[activeId] && (() => {
          const [cx, cy] = GITE_CENTERS[activeId];
          return (
            <g style={{ pointerEvents: "none" }}>
              <circle cx={cx} cy={cy} r="1.5" fill="#FAFAF7" />
              <circle cx={cx} cy={cy} r="1.5" fill="none" stroke="#FAFAF7" strokeWidth="0.5" vectorEffect="non-scaling-stroke" style={{ animation: "gite-pulse-ring 1.8s ease-out infinite", transformOrigin: `${cx}px ${cy}px` }} />
              <circle cx={cx} cy={cy} r="1.5" fill="none" stroke="#FAFAF7" strokeWidth="0.5" vectorEffect="non-scaling-stroke" style={{ animation: "gite-pulse-ring 1.8s ease-out .9s infinite", transformOrigin: `${cx}px ${cy}px` }} />
            </g>
          );
        })()}
      </svg>

      {activeId && GITE_CENTERS[activeId] && (() => {
        const [cx, cy] = GITE_CENTERS[activeId];
        const gite = gites.find(g => g.id === activeId);
        const above = cy > 25;
        return (
          <div style={{
            position: "absolute",
            left: `${cx}%`, top: `${cy}%`,
            transform: above ? "translate(-50%, calc(-100% - 26px))" : "translate(-50%, 26px)",
            background: A.white, color: A.ink,
            padding: "7px 14px", fontFamily: A.mono, fontSize: 10,
            letterSpacing: 2, textTransform: "uppercase", whiteSpace: "nowrap",
            boxShadow: "0 4px 14px rgba(0,0,0,0.4)",
            border: `1.5px solid ${A.rouge}`, fontWeight: 600,
            pointerEvents: "none", zIndex: 2,
            animation: "gite-callout .4s cubic-bezier(.34,1.56,.64,1) both",
          }}>
            <span style={{ color: A.rouge, marginRight: 6 }}>★</span>{gite?.name || activeId}
            <span style={{
              position: "absolute", left: "50%",
              [above ? "bottom" : "top"]: -7,
              transform: "translateX(-50%)",
              width: 0, height: 0,
              borderLeft: "7px solid transparent", borderRight: "7px solid transparent",
              [above ? "borderTop" : "borderBottom"]: `7px solid ${A.rouge}`,
            }} />
          </div>
        );
      })()}

      <style>{`
        @keyframes gite-pulse-ring {
          0%   { transform: scale(1);  opacity: 0.85; }
          100% { transform: scale(4);  opacity: 0; }
        }
        @keyframes gite-dim-in { from { opacity: 0; } to { opacity: 0.55; } }
        @keyframes gite-callout {
          0%   { opacity: 0; transform: translate(-50%, calc(-100% - 38px)) scale(.92); }
          100% { opacity: 1; }
        }
      `}</style>
    </div>
  );
}

// ─── TableDetail : utilisé quand on a une table à mettre en avant ───────
function TableDetail({ tableId, highlight, tables, onBack, mineLabel = null }) {
  const t = tables.find(x => x.id === tableId);
  if (!t) return null;
  return (
    <div style={{ animation: "a-up .5s ease both" }}>
      {onBack && (
        <button onClick={onBack} style={backButtonStyle()}>← Nouvelle recherche</button>
      )}
      <div style={{ background: A.card, padding: 24, border: `1px solid ${A.ruleSoft}`, position: "relative", overflow: "hidden" }}>
        <div style={{ position: "absolute", top: 0, left: 0, right: 0, height: 6, background: t.color }}/>
        <div className="eyebrow" style={{ color: t.color, marginTop: 6 }}>
          {mineLabel || "Votre table"}
        </div>
        <div style={{ fontFamily: A.display, fontWeight: 700, fontSize: 44, color: A.ink, marginTop: 4, lineHeight: 1, letterSpacing: -1 }}>{t.name}</div>
        {t.note && <div style={{ fontFamily: A.italic, fontStyle: "italic", fontSize: 15, color: A.inkSoft, marginTop: 4 }}>{t.note}</div>}
        <div style={{ marginTop: 22, paddingTop: 18, borderTop: `1px solid ${A.ruleSoft}` }}>
          <div className="eyebrow" style={{ marginBottom: 10 }}>Avec vous à table</div>
          <ul style={{ margin: 0, padding: 0, listStyle: "none", display: "flex", flexDirection: "column", gap: 6 }}>
            {t.guests.map((g) => (
              <li key={g} style={listItemStyle(g === highlight, t.color)}>
                <span>{g}</span>
                {g === highlight && <span style={{ fontFamily: A.mono, fontSize: 9, color: t.color, letterSpacing: 1.5 }}>VOUS</span>}
              </li>
            ))}
          </ul>
        </div>
      </div>
    </div>
  );
}

// ─── Helpers de style ──────────────────────────────────────────────────
function inputStyle() {
  return {
    width: "100%", padding: "14px 16px", background: A.card,
    border: `1.5px solid ${A.ruleSoft}`, fontFamily: A.sans, fontSize: 15, color: A.ink, outline: "none",
  };
}
function resultRowStyle() {
  return {
    textAlign: "left", padding: "12px 14px",
    background: A.paper, border: `1px solid ${A.ruleSoft}`,
    fontFamily: A.sans, fontSize: 14, cursor: "pointer",
    display: "flex", justifyContent: "space-between", alignItems: "center",
  };
}
function backButtonStyle() {
  return { background: "transparent", border: "none", color: A.inkSoft, cursor: "pointer", fontFamily: A.mono, fontSize: 10, letterSpacing: 2, padding: 0, marginBottom: 14 };
}
function ghostLinkStyle() {
  return {
    background: "transparent", border: "none", color: A.inkSoft,
    fontFamily: A.italic, fontStyle: "italic", fontSize: 14,
    cursor: "pointer", textDecoration: "underline", textUnderlineOffset: 4,
    padding: 0,
  };
}
function listItemStyle(highlighted, color) {
  return {
    padding: "10px 12px", background: highlighted ? color + "22" : A.paper,
    border: `1px solid ${highlighted ? color : "transparent"}`,
    fontFamily: A.sans, fontSize: 14, color: A.ink, fontWeight: highlighted ? 600 : 400,
    display: "flex", justifyContent: "space-between", alignItems: "center",
  };
}

Object.assign(window, { PlanPage });
