import { useEffect, useMemo, useState, type ReactNode } from "react";
import { useHive } from "../store";
import { pruneTelemetry, saveOverride } from "../store/wiring";
import { confirmAction, deleteProject } from "../store/raw";
import { fetchStorage, type StorageBreakdown } from "../api";
import { fmtBytes, fmtNum } from "../lib/format";

// One labelled metric tile in the storage summary. `accent` gives the prune-
// preview tile a subtly tinted card so it reads as the actionable figure.
function StorageTile(props: { label: string; value: string; valueClass?: string; hint?: ReactNode; accent?: boolean }) {
  return (
    <div className={`storage-tile${props.accent ? " accent" : ""}`}>
      <div className="storage-tile-label">{props.label}</div>
      <div className={`storage-tile-value${props.valueClass ? ` ${props.valueClass}` : ""}`}>{props.value}</div>
      {props.hint && <div className="storage-tile-hint">{props.hint}</div>}
    </div>
  );
}

// Settings: rename projects (persisted in the telemetry DB, keyed by cwd).
// Future settings can be added as additional sections below.
export default function Settings() {
  const projectGroups = useHive((s) => s.projectGroups);
  const overrides = useHive((s) => s.projectOverrides);
  const scope = useHive((s) => s.scope);

  // Settings is scoped to the selected project.
  const scopedProject = scope.level !== "fleet" ? scope.project : undefined;

  // Local edit buffer per group key so typing doesn't fight the store.
  const [draft, setDraft] = useState<Record<string, string>>({});
  const [busy, setBusy] = useState<string>("");
  const [pruneDays, setPruneDays] = useState<string>("30");
  const [pruning, setPruning] = useState(false);
  const [storage, setStorage] = useState<StorageBreakdown | null>(null);
  const [storageLoading, setStorageLoading] = useState(false);

  // The cwd of the scoped project (first cwd of its group), or undefined at fleet
  // scope → whole-DB storage. The server resolves it to the project's full cwd set.
  const scopedCwd = useMemo(() => {
    if (!scopedProject) return undefined;
    return projectGroups.find((g) => g.name === scopedProject)?.cwds[0];
  }, [projectGroups, scopedProject]);

  // Fetch storage usage + prune preview whenever the scope or the days input
  // settles. Debounced so typing in the days field doesn't spam the endpoint.
  useEffect(() => {
    const days = Number(pruneDays.trim());
    const validDays = Number.isFinite(days) && days >= 0 ? days : undefined;
    let cancelled = false;
    setStorageLoading(true);
    const t = setTimeout(async () => {
      const res = await fetchStorage(scopedCwd, validDays);
      if (!cancelled) { setStorage(res); setStorageLoading(false); }
    }, 300);
    return () => { cancelled = true; clearTimeout(t); };
  }, [scopedCwd, pruneDays]);

  // Seed the draft from current labels when the group set changes.
  useEffect(() => {
    setDraft((prev) => {
      const next = { ...prev };
      for (const g of projectGroups) if (next[g.name] === undefined) next[g.name] = g.label;
      return next;
    });
  }, [projectGroups]);

  const rows = useMemo(
    () => projectGroups
      .filter((g) => !scopedProject || g.name === scopedProject)
      .map((g) => ({
        key: g.name,
        derived: g.name,
        label: g.label,
        cwd: g.cwds[0] || "",
        cwds: g.cwds,
        sessions: g.sessions.length,
        overridden: g.cwds.some((c) => overrides.has(c)),
      })),
    [projectGroups, overrides, scopedProject],
  );

  async function save(cwd: string, key: string, label: string) {
    if (!cwd) return;
    setBusy(key);
    await saveOverride(cwd, label);
    setBusy("");
  }

  function requestDelete(project: string, label: string, sessions: number) {
    confirmAction({
      title: "Delete project telemetry?",
      message: <>This permanently removes all telemetry for <b>{label}</b> — {sessions} session{sessions === 1 ? "" : "s"} plus their events, delegations, tool calls, and messages. Project files on disk are not touched.</>,
      confirmLabel: "Delete telemetry",
      danger: true,
      onConfirm: () => deleteProject(project),
    });
  }

  function requestPrune() {
    const days = Number(pruneDays.trim());
    if (!Number.isFinite(days) || days < 0) return;
    confirmAction({
      title: "Prune telemetry history?",
      message: <>This permanently deletes all telemetry older than <b>{days} day{days === 1 ? "" : "s"}</b> across every project — events, delegations, tool calls, and any session whose entire history predates the cutoff. Project logs on disk are not touched.</>,
      confirmLabel: "Prune history",
      danger: true,
      onConfirm: async () => {
        setPruning(true);
        try { await pruneTelemetry(days); await refreshStorage(); } finally { setPruning(false); }
      },
    });
  }

  async function refreshStorage() {
    const days = Number(pruneDays.trim());
    const validDays = Number.isFinite(days) && days >= 0 ? days : undefined;
    setStorage(await fetchStorage(scopedCwd, validDays));
  }

  return (
    <div className="settings">
      <section className="tab-card settings-card">
        <div className="settings-head">
          <div>
            <h2 className="settings-title">Projects</h2>
            <p className="settings-sub">Rename how a project appears across the dashboard. The change is saved locally and applies to every session from that working directory.</p>
          </div>
        </div>

        {!rows.length ? <div className="empty">No projects yet.</div> : (
          <div className="settings-list">
            {rows.map((r) => {
              const value = draft[r.key] ?? r.label;
              const dirty = value.trim() !== r.label && value.trim() !== "";
              return (
                <div className="setting-row" key={r.key}>
                  <div className="setting-meta">
                    <div className="setting-name">
                      {r.derived}
                      {r.overridden && <span className="setting-badge">renamed</span>}
                    </div>
                    <div className="setting-path" title={r.cwd}>{r.cwd || "—"}{r.cwds.length > 1 ? ` (+${r.cwds.length - 1} more)` : ""} · {r.sessions} session{r.sessions === 1 ? "" : "s"}</div>
                  </div>
                  <input
                    className="setting-input"
                    value={value}
                    placeholder={r.derived}
                    onChange={(e) => setDraft((d) => ({ ...d, [r.key]: e.target.value }))}
                    onKeyDown={(e) => { if (e.key === "Enter" && dirty) save(r.cwd, r.key, value); }}
                  />
                  <div className="setting-actions">
                    <button className="btn sm primary" disabled={!dirty || busy === r.key} onClick={() => save(r.cwd, r.key, value)}>
                      {busy === r.key ? "Saving…" : "Save"}
                    </button>
                    {r.overridden && (
                      <button className="btn sm" disabled={busy === r.key} onClick={() => { setDraft((d) => ({ ...d, [r.key]: r.derived })); save(r.cwd, r.key, ""); }}>Reset</button>
                    )}
                    <button className="btn sm danger" disabled={busy === r.key} onClick={() => requestDelete(r.derived, r.label, r.sessions)}>Delete…</button>
                  </div>
                </div>
              );
            })}
          </div>
        )}
      </section>

      <section className="tab-card settings-card">
        <div className="settings-head">
          <div>
            <h2 className="settings-title">Storage &amp; prune</h2>
            <p className="settings-sub">Telemetry content size {scopedProject ? "for this project" : "across all projects"} (payloads + tool/message text; the physical database file is larger and shrinks after a prune). Pruning removes telemetry older than a chosen age and cannot be undone.</p>
          </div>
        </div>

        <div className="storage-panel">
          <div className="storage-scope">{scopedProject ? "This project" : "All telemetry"}</div>
          {storageLoading && !storage ? (
            <div className="storage-muted">Measuring…</div>
          ) : storage ? (
            <div className="storage-tiles">
              <StorageTile label="Content size" value={fmtBytes(storage.bytes)} hint="payloads + text" />
              <StorageTile label="Events" value={fmtNum(storage.events)} />
              <StorageTile label="Sessions" value={String(storage.sessions)} />
              {storage.prune && (
                storage.prune.removeEvents > 0 ? (
                  <StorageTile
                    accent
                    label={`Prune ≥ ${pruneDays.trim() || "0"}d frees`}
                    value={fmtBytes(storage.prune.removeBytes)}
                    valueClass="text-crit"
                    hint={<>{storage.prune.removeSessions > 0 && `${storage.prune.removeSessions} session${storage.prune.removeSessions === 1 ? "" : "s"} · `}{fmtBytes(storage.prune.keepBytes)} would remain</>}
                  />
                ) : (
                  <StorageTile accent label={`Prune ≥ ${pruneDays.trim() || "0"}d frees`} value="0 B" hint={`nothing older${scopedProject ? " here" : ""}`} />
                )
              )}
            </div>
          ) : (
            <div className="storage-muted">Unavailable — is the dashboard daemon running?</div>
          )}
        </div>

        <div className="prune-row">
          <div className="prune-copy">
            <div className="prune-copy-title">Retain the last</div>
            <div className="prune-copy-sub">Older events are removed and sessions entirely older than this are dropped. Prune runs across <b>all</b> projects, not just this one.</div>
          </div>
          <div className="prune-control">
            <input
              className="prune-input"
              type="number"
              min={0}
              value={pruneDays}
              onChange={(e) => setPruneDays(e.target.value)}
              aria-label="Days of telemetry to retain"
            />
            <span className="prune-unit">day{pruneDays.trim() === "1" ? "" : "s"}</span>
            <button className="btn sm danger" disabled={pruning || !(Number(pruneDays) >= 0)} onClick={requestPrune}>
              {pruning ? "Pruning…" : "Prune…"}
            </button>
          </div>
        </div>
      </section>
    </div>
  );
}
