import React, { useEffect, useMemo, useState } from "react";
import { api } from "./api.js";
import { Spinner } from "./ui.jsx";

const NARRATOR = "narrateur";
let _seq = 0;
const nextId = () => ++_seq;

export default function AnalysisEditor({ slug, book, state }) {
  // Chapitres analysés (intersection ordre du livre x analyzed_chapters).
  const analyzed = useMemo(() => {
    const set = new Set(state.analyzed_chapters || []);
    return book.chapters.filter((c) => set.has(c.index));
  }, [book, state.analyzed_chapters]);

  const [index, setIndex] = useState(() => analyzed[0]?.index ?? null);
  const [analysis, setAnalysis] = useState(null); // { index, title, segments:[{_id,type,text,speaker}] }
  const [names, setNames] = useState([]); // noms de personnages pour la datalist
  const [loading, setLoading] = useState(false);
  const [saved, setSaved] = useState(false);
  // Derniere selection de texte dans une replique (pour "marquer comme incise").
  const [sel, setSel] = useState({ id: null, start: 0, end: 0 });

  // Filtres d'affichage (n'altèrent pas la sauvegarde).
  const [query, setQuery] = useState("");
  const [typeFilter, setTypeFilter] = useState("all");
  const [speakerFilter, setSpeakerFilter] = useState("all");

  // Si la liste des chapitres analysés change et que l'index courant disparaît.
  useEffect(() => {
    if (index == null || !analyzed.some((c) => c.index === index)) {
      setIndex(analyzed[0]?.index ?? null);
    }
  }, [analyzed]); // eslint-disable-line react-hooks/exhaustive-deps

  // Noms des personnages du casting (une fois).
  useEffect(() => {
    api.getCast(slug)
      .then((d) => setNames((d.cast?.characters || []).map((c) => c.name)))
      .catch(() => setNames([]));
  }, [slug]);

  // Chargement de l'analyse du chapitre sélectionné.
  useEffect(() => {
    if (index == null) { setAnalysis(null); return; }
    setLoading(true);
    setSaved(false);
    api.getChapter(slug, index).then((d) => {
      if (d.analysis) {
        setAnalysis({
          index: d.analysis.index,
          title: d.analysis.title,
          segments: (d.analysis.segments || []).map((s) => ({ ...s, _id: nextId() })),
        });
      } else {
        setAnalysis({ index, title: d.chapter?.title || "", segments: null });
      }
    }).finally(() => setLoading(false));
  }, [slug, index]);

  const speakerOptions = useMemo(() => {
    const set = new Set([NARRATOR, ...names]);
    (analysis?.segments || []).forEach((s) => s.speaker && set.add(s.speaker));
    return [...set];
  }, [names, analysis]);

  if (!analyzed.length)
    return <p className="text-ink-muted">Lancez d'abord l'<b>Analyse</b> sur un chapitre.</p>;

  const touch = (segments) => { setAnalysis((a) => ({ ...a, segments })); setSaved(false); };

  const setSeg = (id, patch) =>
    touch(analysis.segments.map((s) => {
      if (s._id !== id) return s;
      const next = { ...s, ...patch };
      if (next.type === "narration") { next.speaker = NARRATOR; next.incises = []; }
      // Edition du texte : on ecarte les incises devenues hors-bornes.
      if (patch.text !== undefined) {
        const len = next.text.length;
        next.incises = (next.incises || []).filter(
          (inc) => inc.start < inc.end && inc.end <= len);
      }
      return next;
    }));

  // Marque la portion [start,end) d'une replique comme incise (voix narrateur).
  const addIncise = (id, start, end) =>
    touch(analysis.segments.map((s) => {
      if (s._id !== id) return s;
      const incises = [...(s.incises || []), { start, end }]
        .sort((a, b) => a.start - b.start)
        .filter((inc, i, arr) => i === 0 || inc.start >= arr[i - 1].end);
      return { ...s, incises };
    }));

  const removeIncise = (id, i) =>
    touch(analysis.segments.map((s) =>
      s._id !== id ? s : { ...s, incises: (s.incises || []).filter((_, k) => k !== i) }));

  const removeSeg = (id) => touch(analysis.segments.filter((s) => s._id !== id));

  const insertAfter = (id) => {
    const segs = analysis.segments;
    const pos = id == null ? segs.length : segs.findIndex((s) => s._id === id) + 1;
    const next = [...segs];
    next.splice(pos, 0, { _id: nextId(), type: "narration", text: "", speaker: NARRATOR });
    touch(next);
  };

  const save = async () => {
    const payload = {
      index: analysis.index,
      title: analysis.title,
      segments: analysis.segments.map(({ _id, ...s }) => s),
    };
    await api.putAnalysis(slug, analysis.index, payload);
    setSaved(true);
  };

  const segments = analysis?.segments;
  const visible = (segments || []).filter((s) => {
    if (typeFilter !== "all" && s.type !== typeFilter) return false;
    if (speakerFilter !== "all" && s.speaker !== speakerFilter) return false;
    if (query && !s.text.toLowerCase().includes(query.toLowerCase())) return false;
    return true;
  });
  const dialogueCount = (segments || []).filter((s) => s.type === "dialogue").length;

  return (
    <div className="space-y-4">
      <datalist id="speaker-list">
        {speakerOptions.map((n) => <option key={n} value={n} />)}
      </datalist>

      {/* Barre de contrôle */}
      <div className="card flex flex-wrap items-center gap-3 p-3">
        <label className="text-sm text-ink-muted">Chapitre</label>
        <select className="input" value={index ?? ""}
          onChange={(e) => setIndex(Number(e.target.value))}>
          {analyzed.map((c) => (
            <option key={c.index} value={c.index}>{c.index} — {c.title}</option>
          ))}
        </select>
        {segments && (
          <span className="text-xs text-ink-muted">
            {segments.length} segments · {dialogueCount} dialogues
          </span>
        )}
        <button className="btn-primary ml-auto" disabled={!segments} onClick={save}>
          {saved ? "✓ enregistré" : "Enregistrer"}
        </button>
      </div>

      {loading && <p className="text-ink-muted"><Spinner /> chargement de l'analyse…</p>}

      {!loading && segments === null && (
        <p className="text-ink-muted">Ce chapitre n'a pas encore d'analyse. Lancez l'<b>Analyse</b>.</p>
      )}

      {!loading && segments && (
        <>
          {/* Filtres d'affichage */}
          <div className="card flex flex-wrap items-center gap-3 p-3">
            <input className="input flex-1 min-w-[12rem]" placeholder="Rechercher dans le texte…"
              value={query} onChange={(e) => setQuery(e.target.value)} />
            <select className="input" value={typeFilter} onChange={(e) => setTypeFilter(e.target.value)}>
              <option value="all">tous types</option>
              <option value="narration">narration</option>
              <option value="dialogue">dialogue</option>
            </select>
            <select className="input" value={speakerFilter} onChange={(e) => setSpeakerFilter(e.target.value)}>
              <option value="all">tous locuteurs</option>
              {speakerOptions.map((n) => <option key={n} value={n}>{n}</option>)}
            </select>
            {visible.length !== segments.length && (
              <span className="text-xs text-ink-muted">{visible.length} affichés</span>
            )}
          </div>

          <div className="card divide-y divide-ink-edge">
            {visible.map((s) => {
              const canMark = s.type === "dialogue"
                && sel.id === s._id && sel.end > sel.start;
              const incises = s.incises || [];
              return (
              <div key={s._id} className="px-4 py-2.5">
                <div className="flex items-start gap-3">
                  <select className="input w-28 shrink-0" value={s.type}
                    onChange={(e) => setSeg(s._id, { type: e.target.value })}>
                    <option value="narration">narration</option>
                    <option value="dialogue">dialogue</option>
                  </select>
                  <textarea className="input flex-1 min-h-[2.5rem] resize-y font-serif text-sm"
                    rows={Math.min(6, Math.ceil((s.text.length || 1) / 80))}
                    value={s.text}
                    onSelect={(e) => s.type === "dialogue" && setSel({
                      id: s._id, start: e.target.selectionStart, end: e.target.selectionEnd })}
                    onChange={(e) => setSeg(s._id, { text: e.target.value })} />
                  <input className="input w-40 shrink-0" list="speaker-list"
                    placeholder="locuteur"
                    value={s.speaker} disabled={s.type === "narration"}
                    onChange={(e) => setSeg(s._id, { speaker: e.target.value })} />
                  <div className="flex shrink-0 gap-1">
                    <button className="btn-ghost" title="Insérer après"
                      onClick={() => insertAfter(s._id)}>+</button>
                    <button className="btn-ghost" title="Supprimer"
                      onClick={() => removeSeg(s._id)}>✕</button>
                  </div>
                </div>

                {/* Incises : portions lues par le narrateur dans la réplique */}
                {s.type === "dialogue" && (incises.length > 0 || canMark) && (
                  <div className="mt-1.5 ml-[7.75rem] flex flex-wrap items-center gap-1.5">
                    <span className="text-[11px] uppercase tracking-wide text-ink-muted">incises</span>
                    {incises.map((inc, i) => (
                      <span key={i}
                        className="inline-flex items-center gap-1 rounded bg-ink-edge/40 px-1.5 py-0.5 text-xs"
                        title="Lu par la voix du narrateur">
                        <span className="text-ink-muted">🎙</span>
                        <span className="font-serif">{s.text.slice(inc.start, inc.end)}</span>
                        <button className="text-ink-muted hover:text-ink"
                          title="Retirer l'incise"
                          onClick={() => removeIncise(s._id, i)}>✕</button>
                      </span>
                    ))}
                    {canMark && (
                      <button className="btn-ghost text-xs"
                        onClick={() => { addIncise(s._id, sel.start, sel.end);
                          setSel({ id: null, start: 0, end: 0 }); }}>
                        + marquer la sélection
                      </button>
                    )}
                  </div>
                )}
              </div>
            ); })}
            <div className="px-4 py-2.5">
              <button className="btn-ghost" onClick={() => insertAfter(null)}>+ ajouter un segment</button>
            </div>
          </div>
        </>
      )}
    </div>
  );
}
