/* global React, Icon, useI18n */
// Dentertain — Chatbot (Liv, der Wedding Concierge)
// Emotion-driven, mixes scripted multiple-choice with optional AI follow-ups.

// ===== Conversation script =====
// Each step has: id, type (bot | options | input | offer | end), bot lines, options/input config
// The flow tries to evoke emotion ("was ist euch wichtig"), gather info, end with a personal offer.

const buildScript = (lang) => {
  if (lang === "en") return buildScriptEN();
  return buildScriptDE();
};

const buildScriptDE = () => ([
  {
    id: "greet",
    bot: [
      "Hi, ich bin <em>Liv</em> — der digitale Begleiter hier im Studio. ✦",
      "Lust, in zwei Minuten eure perfekte Hochzeit für die Kamera zu skizzieren? Dennis bekommt das hinterher — nicht als Vorlage, sondern als ersten Eindruck."
    ],
    options: [
      { v: "yes",  l: "Ja, los geht's" },
      { v: "skip", l: "Lieber direkt schreiben" },
    ],
    next: (v) => v === "skip" ? "skip" : "feel",
  },
  {
    id: "skip",
    bot: ["Alles gut. Schreibt direkt an <em>info@dentertain-production.de</em> oder über das Formular auf der Kontakt-Seite. Bis bald 🎬"],
    type: "end",
  },
  {
    id: "feel",
    bot: [
      "Bevor wir über Datum und Stunden reden — wie soll sich euer Tag anfühlen, wenn ihr in zehn Jahren den Film noch einmal schaut?",
    ],
    options: [
      { v: "warm",     l: "Warm & familiär" },
      { v: "epic",     l: "Cinematic & episch" },
      { v: "intimate", l: "Klein & sehr intim" },
      { v: "wild",     l: "Wild & gefeiert bis 5 Uhr" },
      { v: "elegant",  l: "Zeitlos elegant" },
    ],
    save: "feeling",
    next: () => "feel_reply",
  },
  {
    id: "feel_reply",
    bot: (s) => ({
      warm:     ["Genau die, die man Jahre später noch öffnet. ✦ Die Tränen kommen meistens beim ersten Tanz — und ich bin dann da."],
      epic:     ["Mein Lieblings-Setup. Cinema-Lenses, Drohne, langsame Bewegung. Dafür haben wir die Canon R5 Mark II — sie ist gebaut für genau diesen Look."],
      intimate: ["Die intimsten Hochzeiten sind oft die emotionalsten Filme. Wir sind dann oft nur zu zweit, ganz leise — wie Gäste, nur mit Kamera."],
      wild:     ["Die langen Nächte. Genau für die nehme ich Kameras mit Stille-Modus mit. Niemand soll uns hören — gehört werden soll der Beat."],
      elegant:  ["Editorial-Stil, ruhige Schnitte, weniger ist mehr. Wenn ihr das mögt, werdet ihr meine Filme lieben."],
    }[s.feeling] || ["Notiert. Genau die Stimmung halten wir fest."]),
    next: () => "important",
  },
  {
    id: "important",
    bot: [
      "Was ist euch <em>besonders wichtig</em> an dem Tag? — bitte ohne Filter. Es muss kein Foto-Wunsch sein.",
      'Ein paar Beispiele: \u201Edass meine Oma im Bild ist\u201C · \u201Eder Moment, wenn er sie zum ersten Mal sieht\u201C · \u201Edass mein Vater nicht weint\u201C · \u201Edie Reden\u201C · \u201Eder Walzer mit meiner Mutter\u201C.'
    ],
    type: "input",
    placeholder: "Was zählt für euch?",
    save: "important",
    next: () => "important_reply",
  },
  {
    id: "important_reply",
    bot: (s) => [
      `Das hab ich notiert. Genau für solche Momente arbeite ich mit zwei Kameras gleichzeitig — eine immer auf den Reaktionen, eine auf dem Geschehen.`,
      `<em>„${escapeHtml(s.important || "")}"</em> — danke, dass ihr das geteilt habt. Sowas geht nicht verloren, wenn ich da bin.`,
    ],
    next: () => "date",
  },
  {
    id: "date",
    bot: ["Habt ihr schon ein Datum oder zumindest eine Saison im Auge?"],
    type: "input",
    placeholder: "z.B. 14.06.2026 oder Sommer 2026",
    save: "date",
    next: () => "location",
  },
  {
    id: "location",
    bot: ["Schon eine Location? Auch ein Stadtteil oder \u201EPfalz\u201C reichen."],
    type: "input",
    placeholder: "z.B. Schloss Eberstein, Pfalz, oder \u201Enoch offen\u201C",
    save: "location",
    next: () => "guests",
  },
  {
    id: "guests",
    bot: ["Wie viele Gäste werdet ihr ungefähr sein?"],
    options: [
      { v: "<30", l: "Bis 30 (intim)" },
      { v: "30-80", l: "30 – 80" },
      { v: "80-150", l: "80 – 150" },
      { v: "150+", l: "150+" },
      { v: "?", l: "Noch unklar" },
    ],
    save: "guests",
    next: () => "style",
  },
  {
    id: "style",
    bot: ["Wie würdet ihr euren Hochzeits-Stil beschreiben? Mehrfach ist kein Problem."],
    options: [
      { v: "klassisch", l: "Klassisch" },
      { v: "boho",      l: "Boho" },
      { v: "modern",    l: "Modern · Minimal" },
      { v: "festlich",  l: "Festlich · Glam" },
      { v: "naturlich", l: "Natürlich · Outdoor" },
      { v: "vintage",   l: "Vintage" },
    ],
    save: "style",
    next: () => "coverage",
  },
  {
    id: "coverage",
    bot: ["Was möchtet ihr abgedeckt haben?"],
    options: [
      { v: "photo",  l: "Nur Foto" },
      { v: "video",  l: "Nur Film" },
      { v: "both",   l: "Foto + Film (Kombi)" },
      { v: "doc",    l: "Doku-Film" },
      { v: "stand",  l: "Standesamt-only" },
      { v: "after",  l: "After-Wedding-Shoot" },
    ],
    save: "coverage",
    next: () => "hours",
  },
  {
    id: "hours",
    bot: ["Wie viele Stunden Begleitung wünscht ihr — von Getting-Ready bis Tanzfläche?"],
    options: [
      { v: "4",  l: "Bis 4h (Standesamt)" },
      { v: "6",  l: "6h (Trauung + Empfang)" },
      { v: "10", l: "10h (Halbtag → Party)" },
      { v: "14", l: "Ganztag (14h+)" },
      { v: "?",  l: "Noch unklar" },
    ],
    save: "hours",
    next: () => "budget",
  },
  {
    id: "budget",
    bot: ["Habt ihr einen groben Budget-Rahmen? — Hilft mir, das richtige Paket zu bauen."],
    options: [
      { v: "<2k",  l: "Bis 2.000 €" },
      { v: "2-4",  l: "2.000 – 4.000 €" },
      { v: "4-6",  l: "4.000 – 6.000 €" },
      { v: "6-9",  l: "6.000 – 9.000 €" },
      { v: "9+",   l: "9.000 €+" },
      { v: "?",    l: "Erstmal sehen" },
    ],
    save: "budget",
    next: () => "source",
  },
  {
    id: "source",
    bot: ["Letzte Frage: Wie habt ihr mich gefunden?"],
    options: [
      { v: "ig",     l: "Instagram" },
      { v: "google", l: "Google" },
      { v: "rec",    l: "Empfehlung" },
      { v: "venue",  l: "Über die Location" },
      { v: "blog",   l: "Hochzeitsblog" },
      { v: "other",  l: "Anders" },
    ],
    save: "source",
    next: () => "email",
  },
  {
    id: "email",
    bot: ["Eure E-Mail — ich schick euch direkt eine erste Skizze nach diesem Gespräch."],
    type: "input",
    placeholder: "ihr@beispiel.de",
    inputType: "email",
    save: "email",
    next: () => "names",
  },
  {
    id: "names",
    bot: ["Und eure Namen?"],
    type: "input",
    placeholder: "z.B. Marie & Jonas",
    save: "names",
    next: () => "thinking",
  },
  {
    id: "thinking",
    bot: ["Einen Moment, ich skizziere euer Paket … ✦"],
    type: "thinking",
    next: () => "offer",
  },
  {
    id: "offer",
    type: "offer",
    bot: () => [],
  },
]);

const buildScriptEN = () => ([
  {
    id: "greet",
    bot: [
      "Hi, I'm <em>Liv</em> — the digital concierge here at the studio. ✦",
      "Up for sketching out your perfect wedding in two minutes? Dennis gets it after — not as a template, just a first impression."
    ],
    options: [
      { v: "yes",  l: "Yes, let's go" },
      { v: "skip", l: "I'll just write directly" },
    ],
    next: (v) => v === "skip" ? "skip" : "feel",
  },
  {
    id: "skip",
    bot: ["No worries. Write directly to <em>info@dentertain-production.de</em> or via the contact page. See you 🎬"],
    type: "end",
  },
  {
    id: "feel",
    bot: ["Before we talk dates and hours — how should the day feel when you watch the film again in ten years?"],
    options: [
      { v: "warm",     l: "Warm & familial" },
      { v: "epic",     l: "Cinematic & epic" },
      { v: "intimate", l: "Tiny & very intimate" },
      { v: "wild",     l: "Wild · dancing till 5am" },
      { v: "elegant",  l: "Timeless · elegant" },
    ],
    save: "feeling",
    next: () => "feel_reply",
  },
  {
    id: "feel_reply",
    bot: (s) => ({
      warm:     ["The kind people still open years later. ✦ Tears usually come during the first dance — I'll be ready."],
      epic:     ["My favorite setup. Cinema lenses, drone, slow motion. The Canon R5 Mark II is built exactly for this look."],
      intimate: ["The most intimate weddings often make the most emotional films. We tend to work as just two, very quietly — almost like guests with cameras."],
      wild:     ["The long nights — that's exactly why I bring silent cameras. You shouldn't hear us, you should hear the beat."],
      elegant:  ["Editorial style, calm cuts, less is more. If that's your taste, you'll love my films."],
    }[s.feeling] || ["Noted. We'll hold exactly that mood."]),
    next: () => "important",
  },
  {
    id: "important",
    bot: [
      "What's <em>truly important</em> about that day for you? — no filter please. It doesn't have to be a photo wish.",
      "(Some examples: \"that my grandma is in the film\", \"the moment he sees her for the first time\", \"that my dad doesn't cry\", \"the speeches\", \"the waltz with my mum\")"
    ],
    type: "input",
    placeholder: "What matters to you?",
    save: "important",
    next: () => "important_reply",
  },
  {
    id: "important_reply",
    bot: (s) => [
      `Got it. That's why I work with two cameras at once — one always on the reactions, one on the action.`,
      `<em>"${escapeHtml(s.important || "")}"</em> — thank you for sharing that. It won't be missed when I'm there.`,
    ],
    next: () => "date",
  },
  {
    id: "date", bot: ["Do you have a date in mind, or at least a season?"],
    type: "input", placeholder: "e.g. June 14, 2026 or Summer 2026", save: "date", next: () => "location",
  },
  {
    id: "location", bot: ["A venue already? A town or region works too."],
    type: "input", placeholder: "e.g. Schloss Eberstein, Pfalz, or \"open\"", save: "location", next: () => "guests",
  },
  {
    id: "guests", bot: ["How many guests, roughly?"],
    options: [{v:"<30",l:"Up to 30"},{v:"30-80",l:"30 – 80"},{v:"80-150",l:"80 – 150"},{v:"150+",l:"150+"},{v:"?",l:"Not sure"}],
    save: "guests", next: () => "style",
  },
  {
    id: "style", bot: ["How would you describe your style?"],
    options: [{v:"classic",l:"Classic"},{v:"boho",l:"Boho"},{v:"modern",l:"Modern · Minimal"},{v:"glam",l:"Glam · Festive"},{v:"outdoor",l:"Outdoor · Natural"},{v:"vintage",l:"Vintage"}],
    save: "style", next: () => "coverage",
  },
  {
    id: "coverage", bot: ["What do you want covered?"],
    options: [{v:"photo",l:"Photo only"},{v:"video",l:"Film only"},{v:"both",l:"Photo + Film"},{v:"doc",l:"Documentary film"},{v:"stand",l:"Civil-ceremony only"},{v:"after",l:"After-wedding shoot"}],
    save: "coverage", next: () => "hours",
  },
  {
    id: "hours", bot: ["How many hours of coverage — from getting ready to the dance floor?"],
    options: [{v:"4",l:"Up to 4h"},{v:"6",l:"6h"},{v:"10",l:"10h"},{v:"14",l:"Full day (14h+)"},{v:"?",l:"Not sure"}],
    save: "hours", next: () => "budget",
  },
  {
    id: "budget", bot: ["Rough budget range? — helps me build the right package."],
    options: [{v:"<2k",l:"Up to € 2,000"},{v:"2-4",l:"€ 2 – 4 k"},{v:"4-6",l:"€ 4 – 6 k"},{v:"6-9",l:"€ 6 – 9 k"},{v:"9+",l:"€ 9 k+"},{v:"?",l:"Let's see"}],
    save: "budget", next: () => "source",
  },
  {
    id: "source", bot: ["Last one: how did you find me?"],
    options: [{v:"ig",l:"Instagram"},{v:"google",l:"Google"},{v:"rec",l:"Recommendation"},{v:"venue",l:"Through venue"},{v:"blog",l:"Wedding blog"},{v:"other",l:"Other"}],
    save: "source", next: () => "email",
  },
  {
    id: "email", bot: ["Your email — I'll send you a first sketch after this chat."],
    type: "input", placeholder: "you@example.com", inputType: "email", save: "email", next: () => "names",
  },
  {
    id: "names", bot: ["And your names?"],
    type: "input", placeholder: "e.g. Marie & Jonas", save: "names", next: () => "thinking",
  },
  {
    id: "thinking", bot: ["One moment, sketching your package … ✦"], type: "thinking", next: () => "offer",
  },
  { id: "offer", type: "offer", bot: () => [] },
]);

const escapeHtml = (s) => String(s || "").replace(/[&<>"']/g, c => ({ "&":"&amp;","<":"&lt;",">":"&gt;","\"":"&quot;","'":"&#39;" }[c]));

// ===== Wissensbasis: Liv beantwortet Fragen — und leitet IMMER aufs Sinnvolle =====
// Kosten-/Preisfragen werden bewusst abgewiesen (persönliches Gespräch).
const KB = {
  de: [
    { m: /(preis|kost|budget|teuer|€|euro|honorar|rabatt)/i, a: "Ehrliche Antwort: <em>Preise besprechen wir persönlich.</em> Keine Hochzeit gleicht der anderen — nach einem kurzen Gespräch bekommt ihr ein klares, auf euren Tag zugeschnittenes Angebot. Ohne versteckte Posten. Genau dafür ist diese Anfrage der erste Schritt. ✦" },
    { m: /(wann.*(film|foto|galerie|bilder)|liefer|fertig|bekommen wir|dauert)/i, a: "Ein Highlight-Reel gibt's voraussichtlich nach etwa zwei Wochen, den vollständigen cinematischen Film in der Regel nach ungefähr drei Monaten. Fotos: Vorschau meist nach etwa einer Woche, die volle Galerie nach rund sechs Wochen." },
    { m: /(reis|ausland|toskana|mallorca|italien|spanien|destination)/i, a: "Ja, Dennis reist jederzeit — er war z.B. schon in der Toskana und auf Mallorca im Einsatz. Reisekosten fließen ins Angebot ein, ohne extra Zuschläge." },
    { m: /(krank|ausfall|ersatz|notfall)/i, a: "Erstmal: Dennis ist da — solange es kein absoluter Notfall ist, steht er an eurem Tag hinter der Kamera. Und für den Fall der Fälle gibt es ein Netz aus Foto- und Videografen mit Top-Qualität, die einspringen würden. Euer Tag ist so oder so abgesichert." },
    { m: /(frei|verf(ü|u)gbar|termin|buch|datum noch)/i, a: "So früh wie möglich anfragen — Sommer-Wochenenden gehen zuerst weg. Schickt die Anfrage mit eurem Datum ab, dann prüft Dennis die Verfügbarkeit und meldet sich persönlich." },
    { m: /(drohne|drone|luftaufnahme)/i, a: "Ja — Drohnen-Aufnahmen sind optional möglich (wenn Location und Wetter es erlauben), genauso wie dezente VFX-Akzente. Immer nur, wenn es die Geschichte trägt." },
    { m: /(vfx|effekt|kino|cinematic)/i, a: "VFX setzt Dennis nur ein, wenn die Story es trägt — ein leiser Übergang, ein Lichteffekt, ein Time-Lapse. Das Können kommt aus echter Kinofilm-Postproduktion." },
    { m: /(paket|leistung|angebot|was.*(bietet|macht|gibt))/i, a: "Sechs Wege: <em>Hochzeitsfilm</em> · <em>Hochzeitsfotografie</em> · <em>Foto+Film-Kombi</em> (meistgebucht) · <em>Standesamt-only</em> · <em>After-Wedding-Shoot</em> · <em>Doku-Film</em>. Was davon passt, finden wir hier gemeinsam raus." },
    { m: /(ablauf|prozess|kennenlernen|wie l(ä|a)uft)/i, a: "Ganz einfach: 1) Kennenlernen (Video-Call oder Kaffee) · 2) Planung ein paar Wochen vor dem Tag · 3) Der Tag selbst — begleitet, nie inszeniert · 4) Lieferung: Vorschau nach etwa zwei Wochen." },
    { m: /(mensch|echt|dennis|pers(ö|o)nlich sprechen|anrufen|telefon)/i, a: "Klar — Dennis übernimmt ab hier gern persönlich. Schickt die Anfrage ab oder schreibt direkt an <em>info@dentertain-production.de</em>." },
  ],
  en: [
    { m: /(price|cost|budget|expensive|€|euro|fee|discount)/i, a: "Honest answer: <em>pricing is discussed personally.</em> No two weddings are alike — after a short chat you'll get one clear quote tailored to your day. That's exactly what this inquiry is for. ✦" },
    { m: /(when.*(film|photo|gallery)|deliver|ready|receive|how long)/i, a: "A highlight reel usually after about two weeks, the full cinematic film typically after around three months. Photos: preview after roughly a week, full gallery after about six weeks." },
    { m: /(travel|abroad|tuscany|mallorca|destination)/i, a: "Yes — Dennis travels anytime; he has shot in Tuscany and on Mallorca. Travel cost is built into the quote, no surcharges." },
    { m: /(sick|ill|backup|emergency)/i, a: "First things first: Dennis will be there — unless it's a genuine emergency. And just in case, there's a network of top photo and video professionals who would step in. Your day is covered either way." },
    { m: /(available|free|date|book)/i, a: "Ask as early as you can — summer weekends go first. Send the inquiry with your date and Dennis will check availability personally." },
    { m: /(drone|aerial)/i, a: "Yes — drone footage is optional (when venue and weather allow), as are subtle VFX accents. Only ever when the story carries it." },
    { m: /(package|service|offer|what.*do)/i, a: "Six ways: <em>wedding film</em> · <em>photography</em> · <em>photo+film combo</em> (most booked) · <em>civil-ceremony only</em> · <em>after-wedding shoot</em> · <em>documentary</em>. We'll find your fit right here." },
    { m: /(process|how.*work|meet)/i, a: "Simple: 1) meet (video call or coffee) · 2) planning a few weeks ahead · 3) the day itself — accompanied, never staged · 4) delivery: preview after about two weeks." },
    { m: /(human|real|dennis|call|phone)/i, a: "Of course — Dennis is happy to take over personally. Send the inquiry, or write to <em>info@dentertain-production.de</em>." },
  ],
};
// Antwort auf eine freie Frage; fallback=true ⇒ Frage wird in die Anfrage übernommen.
const answerQuestion = (text, lang) => {
  const kb = KB[lang === "en" ? "en" : "de"];
  for (const e of kb) if (e.m.test(text)) return { a: e.a, fallback: false };
  return {
    a: lang === "en"
      ? "Good question — Dennis prefers to answer that one personally. I'll attach it to your inquiry, so you'll get the answer together with his reply. ✦"
      : "Gute Frage — die beantwortet euch Dennis am liebsten persönlich. Ich nehme sie mit in die Anfrage, dann bekommt ihr die Antwort direkt mit seiner Rückmeldung. ✦",
    fallback: true,
  };
};

// ===== Build offer card from state =====
const buildOffer = (s, lang) => {
  const labels = lang === "en" ? {
    title: "Your sketch",
    tag: "Draft · no price",
    pkg: "Package",
    date: "Date",
    place: "Location",
    guests: "Guests",
    hours: "Coverage",
    style: "Style",
    feeling: "Mood",
    note: "Dennis will turn this into a real quote within two working days. No commitment, no spam.",
    cta: "Send to Dennis & view contact",
  } : {
    title: "Eure Skizze",
    tag: "Entwurf · noch kein Preis",
    pkg: "Paket",
    date: "Datum",
    place: "Location",
    guests: "Gäste",
    hours: "Stunden",
    style: "Stil",
    feeling: "Stimmung",
    note: "Dennis macht daraus innerhalb von zwei Werktagen ein echtes Angebot. Unverbindlich, kein Spam.",
    cta: "An Dennis senden · zum Kontakt",
  };
  const cov = {
    photo: lang === "en" ? "Photo only" : "Nur Foto",
    video: lang === "en" ? "Film only" : "Nur Film",
    both:  lang === "en" ? "Photo + Film" : "Foto + Film",
    doc:   lang === "en" ? "Documentary film" : "Doku-Film",
    stand: lang === "en" ? "Civil ceremony" : "Standesamt",
    after: lang === "en" ? "After-wedding shoot" : "After-Wedding-Shoot",
  };
  const hrs = { "4":"4h","6":"6h","10":"10h","14":"14h+","?":"—" };
  return {
    labels,
    rows: [
      { k: labels.pkg, v: cov[s.coverage] || "—" },
      { k: labels.date, v: s.date || "—" },
      { k: labels.place, v: s.location || "—" },
      { k: labels.guests, v: s.guests || "—" },
      { k: labels.hours, v: hrs[s.hours] || s.hours || "—" },
      { k: labels.style, v: s.style ? s.style.charAt(0).toUpperCase() + s.style.slice(1) : "—" },
      { k: labels.feeling, v: s.feeling ? s.feeling.charAt(0).toUpperCase() + s.feeling.slice(1) : "—" },
    ],
  };
};

// ===== Chatbot UI =====
const Chatbot = ({ enabled = true, onNavigate }) => {
  const { lang } = useI18n();
  const script = React.useMemo(() => buildScript(lang), [lang]);
  const stepMap = React.useMemo(() => Object.fromEntries(script.map(s => [s.id, s])), [script]);

  const [bubbleShown, setBubbleShown] = React.useState(false);
  const [open, setOpen] = React.useState(false);
  const [stepId, setStepId] = React.useState("greet");
  const [messages, setMessages] = React.useState([]); // {who: "bot"|"user", html}
  const [typing, setTyping] = React.useState(false);
  const [collected, setCollected] = React.useState({});
  const [showOpts, setShowOpts] = React.useState(false);
  const [showInput, setShowInput] = React.useState(false);
  const [inputValue, setInputValue] = React.useState("");
  const [offer, setOffer] = React.useState(null);
  const [askMode, setAskMode] = React.useState(false);
  const [sending, setSending] = React.useState(false);
  const [sent, setSent] = React.useState(null);      // { mailed } nach Erfolg
  const [sendError, setSendError] = React.useState("");
  const bodyRef = React.useRef(null);
  const everOpened = React.useRef(false);
  const askResumeRef = React.useRef(null);           // UI-Zustand, zu dem wir nach einer Frage zurückkehren

  // delayed bubble entry
  React.useEffect(() => {
    if (!enabled) { setBubbleShown(false); return; }
    const t = setTimeout(() => setBubbleShown(true), 5500);
    return () => clearTimeout(t);
  }, [enabled]);

  // when language switches, reset to greet if not in middle
  React.useEffect(() => {
    if (!everOpened.current) {
      setStepId("greet");
      setMessages([]);
      setCollected({});
    }
  }, [lang]);

  // auto-scroll
  React.useEffect(() => {
    const el = bodyRef.current;
    if (el) el.scrollTop = el.scrollHeight;
  }, [messages, typing, showOpts, offer]);

  const linesFor = (step, state) => {
    if (typeof step.bot === "function") return step.bot(state);
    return step.bot || [];
  };

  // runs a step: prints bot lines (with typing delay), then shows options/input
  const runStep = React.useCallback((id, state) => {
    const step = stepMap[id];
    if (!step) return;
    setShowOpts(false);
    setShowInput(false);
    const lines = linesFor(step, state);
    let i = 0;
    const pushNext = () => {
      if (i >= lines.length) {
        // After all lines, set up the interaction
        if (step.type === "end") return;
        if (step.type === "thinking") {
          setTyping(true);
          setTimeout(() => {
            setTyping(false);
            const nid = step.next();
            setStepId(nid); // stepId MUSS mitwandern, sonst rendert der Folgeschritt kein Input
            runStep(nid, state);
          }, 1400);
          return;
        }
        if (step.type === "offer") {
          setTimeout(() => {
            setOffer(buildOffer(state, lang));
          }, 200);
          return;
        }
        if (step.type === "input") {
          setShowInput(true);
          setInputValue("");
          return;
        }
        if (step.options) {
          setShowOpts(true);
          return;
        }
        // No interaction defined -> auto-advance
        if (step.next) {
          const nid = step.next();
          setTimeout(() => { setStepId(nid); runStep(nid, state); }, 600);
        }
        return;
      }
      setTyping(true);
      setTimeout(() => {
        setTyping(false);
        setMessages((m) => [...m, { who: "bot", html: lines[i] }]);
        i++;
        setTimeout(pushNext, 380);
      }, 700 + Math.min(900, lines[i].length * 18));
    };
    pushNext();
  }, [stepMap, lang]);

  const openPanel = () => {
    setOpen(true);
    setBubbleShown(false);
    if (!everOpened.current) {
      everOpened.current = true;
      setTimeout(() => runStep("greet", {}), 350);
    }
  };

  const choose = (opt) => {
    const step = stepMap[stepId];
    if (!step) return;
    const label = (step.options || []).find(o => o.v === opt.v)?.l || opt.v;
    setMessages((m) => [...m, { who: "user", html: label }]);
    const newState = step.save ? { ...collected, [step.save]: opt.v } : collected;
    setCollected(newState);
    setShowOpts(false);
    const nextId = step.next ? step.next(opt.v) : null;
    if (nextId) {
      setStepId(nextId);
      setTimeout(() => runStep(nextId, newState), 240);
    }
  };

  // ❓-Modus: Frage stellen, beantworten (KB), dann dorthin zurück, wo wir waren
  const startAsk = () => {
    askResumeRef.current = { hadOpts: showOpts, hadInput: showInput };
    setAskMode(true);
    setShowOpts(false);
    setShowInput(true);
    setInputValue("");
  };
  const submitAsk = (q) => {
    setMessages((m) => [...m, { who: "user", html: escapeHtml(q) }]);
    setShowInput(false);
    setInputValue("");
    const { a, fallback } = answerQuestion(q, lang);
    if (fallback) setCollected((c) => ({ ...c, frage: c.frage ? c.frage + " · " + q : q }));
    setTyping(true);
    setTimeout(() => {
      setTyping(false);
      setMessages((m) => [...m, { who: "bot", html: a }]);
      setAskMode(false);
      const r = askResumeRef.current || {};
      askResumeRef.current = null;
      if (r.hadOpts) setShowOpts(true);
      else if (r.hadInput) setShowInput(true);
    }, 900);
  };

  // Anfrage absenden (nach Kunden-Bestätigung im Angebots-Karten-Bereich)
  const sendAnfrage = () => {
    if (sending || sent) return;
    setSending(true);
    setSendError("");
    fetch("/api/anfrage", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      credentials: "same-origin",
      body: JSON.stringify({ ...collected, lang, website: "" }),
    }).then(async (res) => {
      const j = await res.json().catch(() => ({}));
      if (!res.ok || !j.ok) throw new Error(j.error || (lang === "en" ? "Sending failed — please use the contact form." : "Senden fehlgeschlagen — bitte nutzt das Kontaktformular."));
      setSent({ mailed: !!j.mailed });
      setMessages((m) => [...m, { who: "bot", html: lang === "en"
        ? (j.mailed
          ? `Your inquiry is on its way! 🎉 A confirmation just went to <em>${escapeHtml(collected.email)}</em> — Dennis will reply personally within about two working days.`
          : "Your inquiry has been received! 🎉 Dennis will reply personally within about two working days.")
        : (j.mailed
          ? `Eure Anfrage ist raus! 🎉 Eine Bestätigung ist gerade an <em>${escapeHtml(collected.email)}</em> gegangen — Dennis meldet sich innerhalb von etwa zwei Werktagen persönlich.`
          : "Eure Anfrage ist eingegangen! 🎉 Dennis meldet sich innerhalb von etwa zwei Werktagen persönlich bei euch.") }]);
    }).catch((e) => {
      setSendError(e.message || "Senden fehlgeschlagen.");
    }).finally(() => setSending(false));
  };

  const submitInput = () => {
    const v = inputValue.trim();
    if (!v) return;
    if (askMode) return submitAsk(v);
    const step = stepMap[stepId];
    if (!step) return;
    setMessages((m) => [...m, { who: "user", html: escapeHtml(v) }]);
    const newState = step.save ? { ...collected, [step.save]: v } : collected;
    setCollected(newState);
    setShowInput(false);
    setInputValue("");
    const nextId = step.next ? step.next(v) : null;
    if (nextId) {
      setStepId(nextId);
      setTimeout(() => runStep(nextId, newState), 240);
    }
  };

  if (!enabled) return null;

  const step = stepMap[stepId];
  const bubbleText = lang === "en"
    ? (<><span className="chat-bubble__text-emph">Build your perfect wedding</span> — 2 min, no commitment.</>)
    : (<><span className="chat-bubble__text-emph">Skizziert eure Traumhochzeit</span> — 2 Min., unverbindlich.</>);

  return (
    <>
      <div
        className={`chat-bubble ${bubbleShown && !open ? "visible" : ""}`}
        onClick={openPanel}
        role="button"
      >
        <div className="chat-bubble__avatar">L</div>
        <div className="chat-bubble__text">{bubbleText}</div>
      </div>

      <div className={`chat-panel ${open ? "open" : ""}`} role="dialog" aria-label="Chatbot Liv">
        <div className="chat-panel__head">
          <div className="chat-bubble__avatar">L</div>
          <div className="chat-panel__head-text">
            <div className="chat-panel__head-name">Liv · Wedding Concierge</div>
            <div className="chat-panel__head-role">
              {lang === "en" ? "Online · Replies instantly" : "Online · antwortet sofort"}
            </div>
          </div>
          <button className="chat-panel__close" onClick={() => setOpen(false)} aria-label="Close">
            <Icon name="x" size={16} />
          </button>
        </div>
        <div className="chat-panel__body" ref={bodyRef}>
          {messages.map((m, i) => (
            <div
              key={i}
              className={`chat-msg chat-msg--${m.who}`}
              dangerouslySetInnerHTML={{ __html: m.html }}
            />
          ))}
          {typing ? (
            <div className="chat-typing"><span></span><span></span><span></span></div>
          ) : null}
          {showOpts && step && step.options ? (
            <div className="chat-options">
              {step.options.map((o) => (
                <button key={o.v} className="chat-option" onClick={() => choose(o)}>
                  {o.l}
                </button>
              ))}
              <button className="chat-option chat-option--ask" onClick={startAsk}>
                {lang === "en" ? "❓ Quick question" : "❓ Kurze Frage stellen"}
              </button>
            </div>
          ) : null}
          {offer ? (
            <div className="chat-offer">
              <div className="chat-offer__head">
                <span className="chat-offer__title">{offer.labels.title}</span>
                <span className="chat-offer__tag">{offer.labels.tag}</span>
              </div>
              {offer.rows.map((r, i) => (
                <div className="chat-offer__row" key={i}>
                  <span className="chat-offer__row-label">{r.k}</span>
                  <strong>{r.v}</strong>
                </div>
              ))}
              <div className="chat-offer__note">{offer.labels.note}</div>
              {sendError ? <div className="chat-offer__error">{sendError}</div> : null}
              {sent ? null : (
                <>
                  <button className="chat-offer__cta" disabled={sending} onClick={sendAnfrage}>
                    {sending
                      ? (lang === "en" ? "Sending…" : "Wird gesendet…")
                      : (lang === "en" ? "✓ Confirm & send inquiry to Dennis" : "✓ Bestätigen & Anfrage an Dennis senden")}
                  </button>
                  <div className="chat-options" style={{ marginTop: 10 }}>
                    <button className="chat-option chat-option--ask" onClick={startAsk}>
                      {lang === "en" ? "❓ One more question first" : "❓ Vorher noch eine Frage"}
                    </button>
                    <button className="chat-option" onClick={() => { setOpen(false); onNavigate && onNavigate("contact"); }}>
                      {lang === "en" ? "Prefer the contact form" : "Lieber übers Kontaktformular"}
                    </button>
                  </div>
                </>
              )}
            </div>
          ) : null}
        </div>
        {showInput && (askMode || (step && step.type === "input")) ? (
          <div className="chat-input-row">
            <input
              className="chat-input"
              type={askMode ? "text" : (step.inputType || "text")}
              value={inputValue}
              onChange={(e) => setInputValue(e.target.value)}
              onKeyDown={(e) => { if (e.key === "Enter") submitInput(); }}
              placeholder={askMode ? (lang === "en" ? "Your question…" : "Eure Frage…") : step.placeholder}
              autoFocus
            />
            <button className="chat-send" onClick={submitInput} disabled={!inputValue.trim()} aria-label="Send">
              <Icon name="send" size={16} stroke={1.8} />
            </button>
          </div>
        ) : null}
      </div>
    </>
  );
};

Object.assign(window, { Chatbot });
