const { AppHeader, FilterBar, MapPanel, DetailSidebar, NeueKarteikarte, Anrufliste, Anmeldung, NeuesPasswort, SEITEN } = window;
const { istFaellig } = window.TersusTermine;
const { Icon } = window.TersusDesignSystem_b3db99;

/* Aktive Unterseite im URL-Hash, damit ein Neuladen auf derselben Seite bleibt. */
function useSeite() {
  const lesen = () => {
    const h = window.location.hash.replace("#", "");
    return SEITEN.some(s => s.id === h) ? h : "vertrieb";
  };
  const [seite, setSeiteState] = React.useState(lesen);
  React.useEffect(() => {
    const onHash = () => setSeiteState(lesen());
    window.addEventListener("hashchange", onHash);
    return () => window.removeEventListener("hashchange", onHash);
  }, []);
  const setSeite = (id) => { window.location.hash = id; setSeiteState(id); };
  return [seite, setSeite];
}

/* Suche: Kleinschreibung, Umlaute und ss vereinheitlicht, Satzzeichen und Bindestriche als Leerzeichen.
   "Albert Schmidt" findet so "Albert-Schmidt-Haus", "Muelheim" findet "Mülheim". */
function suchNorm(s) {
  return String(s || "").toLowerCase()
    .replace(/ä/g, "ae").replace(/ö/g, "oe").replace(/ü/g, "ue").replace(/ß/g, "ss")
    .normalize("NFD").replace(/[̀-ͯ]/g, "")
    .replace(/[^a-z0-9]+/g, " ").trim();
}

/* Telefonnummer in nationaler Ziffernfolge: "+49 (0) 234 / 33 63 19" und "0234 336319" -> "0234336319". */
function telefonNorm(t) {
  let z = String(t || "").replace(/\(0\)/g, "").replace(/\D/g, "");
  if (z.startsWith("0049")) z = "0" + z.slice(4);
  else if (z.startsWith("49") && String(t).trim().startsWith("+")) z = "0" + z.slice(2);
  return z;
}

Object.assign(window, { suchNorm, telefonNorm });

/* Kleine Komfort-Einstellung pro Browser; darf fehlen oder fehlschlagen. */
function useGemerkt(schluessel, start) {
  const [wert, setWert] = React.useState(() => {
    try { return localStorage.getItem(schluessel) || start; } catch (e) { return start; }
  });
  const setzen = (v) => { setWert(v); try { localStorage.setItem(schluessel, v); } catch (e) {} };
  return [wert, setzen];
}

/* Breite der Seitenleiste: Standard 38 % des Fensters (420 bis 760 px), per Griff
   verstellbar, pro Browser gemerkt. */
const SIDEBAR_MIN = 380;
const sidebarMax = () => Math.max(SIDEBAR_MIN, Math.round(window.innerWidth * 0.65));
const sidebarStandard = () => Math.min(760, Math.max(420, Math.round(window.innerWidth * 0.38)));

function useSidebarBreite() {
  const [breite, setBreite] = React.useState(() => {
    let gemerkt = 0;
    try { gemerkt = parseInt(localStorage.getItem("tersus.sidebarBreite"), 10) || 0; } catch (e) {}
    return Math.min(sidebarMax(), Math.max(SIDEBAR_MIN, gemerkt || sidebarStandard()));
  });
  const ziehen = (e) => {
    e.preventDefault();
    const startX = e.clientX, startBreite = breite;
    let letzte = startBreite;
    const bewegen = (ev) => {
      letzte = Math.min(sidebarMax(), Math.max(SIDEBAR_MIN, startBreite + (startX - ev.clientX)));
      setBreite(letzte);
    };
    const loslassen = () => {
      window.removeEventListener("pointermove", bewegen);
      window.removeEventListener("pointerup", loslassen);
      document.body.style.cursor = ""; document.body.style.userSelect = "";
      try { localStorage.setItem("tersus.sidebarBreite", String(letzte)); } catch (err) {}
    };
    document.body.style.cursor = "col-resize"; document.body.style.userSelect = "none";
    window.addEventListener("pointermove", bewegen);
    window.addEventListener("pointerup", loslassen);
  };
  const zuruecksetzen = () => {
    setBreite(sidebarStandard());
    try { localStorage.removeItem("tersus.sidebarBreite"); } catch (e) {}
  };
  return { breite, ziehen, zuruecksetzen };
}

function useSitzung() {
  const [sitzung, setSitzung] = React.useState(undefined); // undefined = wird geprueft
  const [wiederherstellung, setWiederherstellung] = React.useState(window.TersusDB.wiederherstellung);
  React.useEffect(() => {
    window.TersusDB.sitzung().then(s => setSitzung(s || null));
    return window.TersusDB.beiAnmeldeWechsel((s, event) => {
      setSitzung(s || null);
      if (event === "PASSWORD_RECOVERY") setWiederherstellung(true);
    });
  }, []);
  return { sitzung, wiederherstellung, fertig: () => setWiederherstellung(false) };
}

function GlasreinigungSeite() {
  return (
    <div style={{flex:1,minHeight:0,display:"flex",alignItems:"center",justifyContent:"center",padding:"var(--space-8)"}}>
      <div style={{maxWidth:420,textAlign:"center"}}>
        <Icon name="calendar-range" size={28} style={{color:"var(--text-muted)"}} />
        <h2 style={{font:"var(--text-h1)",marginTop:"var(--space-3)"}}>Glasreinigung Auftragsplanung</h2>
        <p style={{margin:"8px 0 0",fontSize:"var(--fs-sm)",color:"var(--text-muted)"}}>
          Diese Ansicht ist angelegt. Inhalte und Funktionen folgen.
        </p>
      </div>
    </div>
  );
}

function useObjekte() {
  const [objekte, setObjekte] = React.useState([]);
  const [laden, setLaden] = React.useState(true);
  const [quelle, setQuelle] = React.useState(null);
  const [fehler, setFehler] = React.useState(null);
  const [speichert, setSpeichert] = React.useState(0);
  const [zuletzt, setZuletzt] = React.useState(null);
  const objekteRef = React.useRef(objekte);
  objekteRef.current = objekte;

  React.useEffect(() => {
    let aktiv = true;
    window.TersusDB.loadAll().then(({ rows, source, error }) => {
      if (!aktiv) return;
      setObjekte(rows); setQuelle(source); setFehler(error ? "Laden fehlgeschlagen: " + error : null); setLaden(false);
    });
    return () => { aktiv = false; };
  }, []);

  /* Optimistisch schreiben, dann persistieren. Schlaegt das Speichern fehl, gehen die
     alten Werte zurueck in die Ansicht, damit nichts Ungespeichertes als gespeichert erscheint. */
  const patch = React.useCallback(async (id, aenderung) => {
    const vorher = objekteRef.current.find(o => o.id === id);
    const alt = vorher ? Object.fromEntries(Object.keys(aenderung).map(k => [k, vorher[k]])) : {};
    setObjekte(os => os.map(o => (o.id === id ? { ...o, ...aenderung } : o)));
    setSpeichert(n => n + 1);
    const res = await window.TersusDB.save(id, aenderung);
    setSpeichert(n => n - 1);
    if (res.ok) { setZuletzt(new Date()); setFehler(null); }
    else {
      setObjekte(os => os.map(o => (o.id === id ? { ...o, ...alt } : o)));
      setFehler("Speichern fehlgeschlagen: " + res.error);
    }
    return res;
  }, []);

  /* Neue Karteikarte: erst speichern, dann in die Liste uebernehmen (keine optimistische Zeile,
     damit nie eine Karteikarte sichtbar ist, die es in der Datenbank nicht gibt). */
  const anlegen = React.useCallback(async (daten) => {
    setSpeichert(n => n + 1);
    const res = await window.TersusDB.anlegen(daten);
    setSpeichert(n => n - 1);
    if (res.ok) { setObjekte(os => [...os, res.row]); setZuletzt(new Date()); setFehler(null); }
    return res;
  }, []);

  return { objekte, laden, quelle, fehler, speichert, zuletzt, patch, anlegen };
}

function SyncHinweis({ laden, speichert, zuletzt, quelle, fehler }) {
  const text = fehler ? fehler
    : laden ? "Daten werden geladen"
    : speichert ? "Speichert"
    : quelle === "seed" ? "Testdaten, Aenderungen werden nicht gespeichert"
    : zuletzt ? "Gespeichert " + zuletzt.toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" })
    : "Datenbank verbunden";
  return (
    <span title={text} style={{display:"flex",alignItems:"center",gap:6,fontSize:"var(--fs-xs)",color:fehler?"var(--status-declined)":"var(--text-muted)",whiteSpace:"nowrap",maxWidth:320,overflow:"hidden",textOverflow:"ellipsis"}}>
      <Icon name={fehler ? "triangle-alert" : laden || speichert ? "loader" : quelle === "seed" ? "database-zap" : "database"} size={13} />
      {text}
    </span>
  );
}

function App() {
  const [seite, setSeite] = useSeite();
  const { sitzung, wiederherstellung, fertig: passwortGesetzt } = useSitzung();
  const mitDatenbank = window.TersusDB.backend === "supabase";

  let inhalt;
  if (sitzung === undefined) inhalt = <div style={{flex:1}} />;
  else if (sitzung && wiederherstellung) inhalt = <NeuesPasswort onFertig={passwortGesetzt} />;
  else if (!sitzung) inhalt = <Anmeldung />;
  else inhalt = (
    <React.Fragment>
      {/* Vertrieb bleibt montiert, damit Filter und Auswahl beim Seitenwechsel erhalten bleiben. */}
      <div style={{display:seite === "vertrieb" ? "flex" : "none",flexDirection:"column",flex:1,minHeight:0}}>
        <VertriebSeite />
      </div>
      {seite === "glasreinigung" ? <GlasreinigungSeite /> : null}
    </React.Fragment>
  );

  return (
    <div style={{display:"flex",flexDirection:"column",height:"100vh",minHeight:0,background:"var(--surface-page)"}}>
      <AppHeader seite={seite} setSeite={setSeite}
        nutzer={sitzung && sitzung.user ? sitzung.user.email : ""}
        onAbmelden={mitDatenbank && sitzung ? () => window.TersusDB.abmelden() : null} />
      {inhalt}
    </div>
  );
}

function VertriebSeite() {
  const hq = window.TERSUS_HQ;
  const { objekte, laden, quelle, fehler, speichert, zuletzt, patch, anlegen } = useObjekte();
  const [neuAnlegen, setNeuAnlegen] = React.useState(false);
  const [ansicht, setAnsicht] = useGemerkt("tersus.ansicht", "liste");
  const [query, setQuery] = React.useState("");
  const [aktiveStatus, setAktiveStatus] = React.useState([]);
  const [aktiveKategorien, setAktiveKategorien] = React.useState([]);
  const [nurFaellig, setNurFaellig] = React.useState(false);
  const [selectedId, setSelectedId] = React.useState(null);
  const [sidebarOffen, setSidebarOffen] = React.useState(true);
  const sidebar = useSidebarBreite();

  const toggle = (setter) => (k) => setter(a => a.includes(k) ? a.filter(x => x !== k) : [...a, k]);

  /* Suchindex je Karteikarte: normalisierter Text aller Felder plus Telefonnummer in nationaler Form. */
  const suchindex = React.useMemo(() => {
    const idx = new Map();
    objekte.forEach(o => {
      const text = suchNorm([o.name, o.traeger, o.kategorie, o.kontakt, o.position, o.strasse, o.plz, o.ort, o.region, o.email, o.website].join(" "));
      idx.set(o.id, { text, telefon: telefonNorm(o.telefon), web: [o.email, o.website].join(" ").toLowerCase() });
    });
    return idx;
  }, [objekte]);

  const sichtbar = React.useMemo(() => {
    const woerter = suchNorm(query).split(" ").filter(Boolean);
    /* Besteht die Eingabe nur aus Ziffern, Leerzeichen und Telefonzeichen, zusaetzlich als Nummer suchen. */
    const alsNummer = /^[\d\s+()\/.-]+$/.test(query.trim()) ? telefonNorm(query) : "";
    /* Sieht die Eingabe nach Web- oder E-Mail-Adresse aus ("ng-se.de", "info@..."), am Stueck suchen,
       sonst wuerde "de" als eigenes Wort fast alles treffen. */
    const alsAdresse = /^[^\s]*[a-z][^\s]*[.@][^\s]+$/i.test(query.trim()) ? query.trim().toLowerCase() : "";
    return objekte.filter(o => {
      const i = suchindex.get(o.id);
      const passtQ = !woerter.length
        || (alsAdresse ? i.web.includes(alsAdresse) : woerter.every(w => i.text.includes(w)))
        || (alsNummer.length >= 4 && i.telefon.includes(alsNummer));
      const passtS = !aktiveStatus.length || aktiveStatus.includes(o.status);
      const passtK = !aktiveKategorien.length || aktiveKategorien.includes(o.kategorie);
      const passtF = !nurFaellig || istFaellig(o);
      return passtQ && passtS && passtK && passtF;
    });
  }, [objekte, suchindex, query, aktiveStatus, aktiveKategorien, nurFaellig]);

  const counts = React.useMemo(() => objekte.reduce((a, o) => ((a[o.status] = (a[o.status] || 0) + 1), a), {}), [objekte]);
  const kategorien = React.useMemo(() => {
    const n = objekte.reduce((a, o) => ((a[o.kategorie] = (a[o.kategorie] || 0) + 1), a), {});
    return Object.entries(n).sort((a, b) => b[1] - a[1]);
  }, [objekte]);
  const faellig = React.useMemo(() => objekte.filter(istFaellig).length, [objekte]);
  const selected = objekte.find(o => o.id === selectedId) || null;
  const auswaehlen = React.useCallback(id => { setSelectedId(id); setNeuAnlegen(false); setSidebarOffen(true); }, []);
  const neueKarteikarte = async (daten) => {
    const res = await anlegen(daten);
    if (res.ok) { setNeuAnlegen(false); setSelectedId(res.row.id); }
    return res;
  };

  return (
    <div style={{display:"flex",flexDirection:"column",flex:1,minHeight:0}}>
      <FilterBar
        ansicht={ansicht} setAnsicht={setAnsicht}
        query={query} setQuery={setQuery}
        aktiveStatus={aktiveStatus} toggleStatus={toggle(setAktiveStatus)}
        kategorien={kategorien} aktiveKategorien={aktiveKategorien} toggleKategorie={toggle(setAktiveKategorien)}
        nurFaellig={nurFaellig} setNurFaellig={setNurFaellig} faellig={faellig}
        counts={counts} treffer={sichtbar.length} gesamt={objekte.length}
        onReset={() => { setQuery(""); setAktiveStatus([]); setAktiveKategorien([]); setNurFaellig(false); }}
        sidebarOffen={sidebarOffen} setSidebarOffen={setSidebarOffen}
        onNeu={() => { setNeuAnlegen(true); setSidebarOffen(true); }}
        status={<SyncHinweis laden={laden} speichert={speichert} zuletzt={zuletzt} quelle={quelle} fehler={fehler} />}
      />
      <div style={{display:"flex",flex:1,minHeight:0}}>
        {ansicht === "karte"
          ? <MapPanel objekte={sichtbar} hq={hq} selectedId={selectedId} onSelect={auswaehlen} counts={{ ...counts, standort: 1 }} sidebarOffen={sidebarOffen} />
          : <Anrufliste objekte={sichtbar} selectedId={selectedId} onSelect={auswaehlen} />}
        {sidebarOffen ? (
          <aside style={{position:"relative",flex:"0 0 " + sidebar.breite + "px",width:sidebar.breite,background:"var(--surface-card)",borderLeft:"1px solid var(--divider)",display:"flex",flexDirection:"column",minHeight:0,overflow:"hidden"}}>
            <div className="sidebar-griff" role="separator" aria-orientation="vertical" aria-label="Breite der Seitenleiste ziehen, Doppelklick setzt zurueck"
              title="Ziehen, um die Breite zu aendern. Doppelklick: Standardbreite"
              onPointerDown={sidebar.ziehen} onDoubleClick={sidebar.zuruecksetzen} />
            {neuAnlegen
              ? <NeueKarteikarte objekte={objekte} onAnlegen={neueKarteikarte} onAbbrechen={() => setNeuAnlegen(false)} onOeffnen={auswaehlen} />
              : <DetailSidebar objekt={selected} hq={hq} alleObjekte={objekte} onPatch={patch} onClose={() => setSelectedId(null)} />}
          </aside>
        ) : null}
      </div>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
