/** Fast Log page: save an entry; high-FSE offers proof brick; pending offers Finish. */

import { useEffect, useMemo, useState } from "preact/hooks";
import {
  ApiError,
  createEntry,
  getDaily,
  putDaily,
  type DailyUpsert,
  type ProofBrickKind,
} from "../api";
import { Button } from "../components/Button";
import { Pressable } from "../components/Pressable";
import { ResultChips, type ResultValue } from "../components/ResultChips";
import { Sheet } from "../components/Sheet";
import { usePendingResolve } from "../components/PendingResolve";
import { useToast } from "../components/Toast";
import { isoDate } from "../dates";
import { navigate } from "../router";

const EMOTION_PICKS = [
  "calm",
  "shame",
  "urge",
  "anxiety",
  "anger",
  "lonely",
  "tired",
  "hope",
];

const TAG_SUGGESTS = [
  "indoors",
  "freeze",
  "movement",
  "interrupt_walk",
  "fantasy_walk",
  "headphones",
  "proof",
];

const FSE_EMOTIONS = new Set(["shame", "urge", "anxiety", "anger", "lonely"]);
const FSE_TAGS = new Set([
  "freeze",
  "indoors",
  "rerun",
  "court",
  "daydream",
  "fantasy",
  "fantasy_walk",
]);

const AVOIDANCE_PICKS = [
  "freeze",
  "bed",
  "scroll",
  "fantasy",
  "headphones",
  "court",
  "rerun",
];

const PROOF_KINDS: { value: ProofBrickKind; label: string }[] = [
  { value: "interrupt_walk", label: "Interrupt walk" },
  { value: "body_or_room", label: "Body or room" },
  { value: "trip_admin", label: "Trip admin" },
  { value: "earn", label: "Earn" },
  { value: "boundary", label: "Boundary" },
  { value: "food", label: "Food" },
  { value: "survive", label: "Survive" },
  { value: "other", label: "Other" },
];

function isHighFse(intensity: number, emotions: string[], tagList: string[]): boolean {
  if (intensity < 7) return false;
  const emos = emotions.map((e) => e.toLowerCase());
  const tags = tagList.map((t) => t.toLowerCase());
  return emos.some((e) => FSE_EMOTIONS.has(e)) || tags.some((t) => FSE_TAGS.has(t));
}

function isLabelOnlyRemedy(remedy: string): boolean {
  return /\blabel(?:ing|ling)?\b/i.test(remedy.trim());
}

const EMPTY_DAILY_PATCH: Partial<DailyUpsert> = {
  primary_brick: "none",
  brick_done: false,
  corn_sessions: 0,
  delay_ok: true,
  daydream: "none",
  rerun: "clean",
  court: "closed",
  stayed_indoors_all_day: false,
  left_room: false,
  left_home: false,
  movement_minutes: 0,
  interrupt_walk_minutes: 0,
  fantasy_walk_minutes: 0,
  headphones_on_walk: false,
  music_cinematic_on_walk: false,
  proof_brick_done: false,
  proof_brick_kind: null,
  fantasy_minutes_scheduled: 0,
  fantasy_minutes_unplanned: 0,
  note: "",
};

export function Log({
  initialRemedy = "",
  initialHappened = "",
}: {
  initialRemedy?: string;
  initialHappened?: string;
}) {
  const toast = useToast();
  const { openResolveId } = usePendingResolve();
  const [happened, setHappened] = useState(initialHappened);
  const [emotions, setEmotions] = useState<string[]>([]);
  const [intensity, setIntensity] = useState(5);
  const [result, setResult] = useState<ResultValue>("pending");
  const [remedy, setRemedy] = useState(initialRemedy);
  const [activity, setActivity] = useState("");
  const [tags, setTags] = useState("");
  const [notes, setNotes] = useState("");
  const [moreOpen, setMoreOpen] = useState(false);
  const [saving, setSaving] = useState(false);
  const [finishPrompt, setFinishPrompt] = useState<{ id: string } | null>(null);
  const [proofOpen, setProofOpen] = useState(false);
  const [proofBusy, setProofBusy] = useState(false);
  const [avoidance, setAvoidance] = useState<string[]>([]);
  const [pendingAfterProof, setPendingAfterProof] = useState<{
    id: string;
    pending: boolean;
  } | null>(null);

  useEffect(() => {
    setRemedy(initialRemedy);
  }, [initialRemedy]);

  useEffect(() => {
    if (initialHappened) setHappened(initialHappened);
  }, [initialHappened]);

  const canSave = useMemo(
    () => happened.trim().length > 0 && !saving,
    [happened, saving],
  );

  const tagList = useMemo(
    () =>
      tags
        .split(",")
        .map((t) => t.trim())
        .filter(Boolean),
    [tags],
  );

  const toggleEmotion = (emotion: string) => {
    setEmotions((current) =>
      current.includes(emotion)
        ? current.filter((item) => item !== emotion)
        : current.length >= 12
          ? current
          : [...current, emotion],
    );
  };

  const toggleTag = (tag: string) => {
    const parts = tagList;
    const lower = parts.map((t) => t.toLowerCase());
    if (lower.includes(tag)) {
      setTags(parts.filter((t) => t.toLowerCase() !== tag).join(", "));
    } else {
      setTags([...parts, tag].join(", "));
    }
  };

  const afterSaveNavigate = (entryId: string, isPending: boolean) => {
    if (isPending) {
      setFinishPrompt({ id: entryId });
    } else {
      navigate("/");
    }
  };

  const labelOnlyWarn =
    intensity >= 7 && remedy.trim().length > 0 && isLabelOnlyRemedy(remedy);

  const toggleAvoidance = (tag: string) => {
    setAvoidance((current) =>
      current.includes(tag)
        ? current.filter((item) => item !== tag)
        : [...current, tag],
    );
  };

  const onSave = async () => {
    if (!canSave) return;
    setSaving(true);
    try {
      const high = isHighFse(intensity, emotions, tagList);
      const entry = await createEntry({
        activity: activity.trim() || "Log",
        happened: happened.trim(),
        emotions,
        intensity,
        remedy: remedy.trim(),
        result,
        tags: tagList,
        notes: notes.trim(),
        fse_spike: high || intensity >= 7 ? true : null,
        avoidance_types: avoidance,
        walk_type: tagList.includes("interrupt_walk")
          ? "interrupt"
          : tagList.includes("fantasy_walk")
            ? "fantasy"
            : null,
      });
      toast.show("Saved");
      if (
        typeof navigator !== "undefined" &&
        "vibrate" in navigator &&
        !window.matchMedia("(prefers-reduced-motion: reduce)").matches
      ) {
        navigator.vibrate(10);
      }
      const pending = entry.result === "pending";
      if (high) {
        setPendingAfterProof({ id: entry.id, pending });
        setProofOpen(true);
      } else {
        afterSaveNavigate(entry.id, pending);
      }
    } catch (err) {
      const message =
        err instanceof ApiError ? err.message : "Could not save entry";
      toast.show(message, "error");
    } finally {
      setSaving(false);
    }
  };

  const closeProof = () => {
    setProofOpen(false);
    const next = pendingAfterProof;
    setPendingAfterProof(null);
    if (next) afterSaveNavigate(next.id, next.pending);
    else navigate("/");
  };

  const saveProof = async (kind: ProofBrickKind) => {
    setProofBusy(true);
    const today = isoDate(new Date());
    try {
      let base: DailyUpsert = { ...EMPTY_DAILY_PATCH } as DailyUpsert;
      try {
        const row = await getDaily(today);
        base = {
          primary_brick: row.primary_brick,
          brick_done: row.brick_done,
          corn_sessions: row.corn_sessions,
          delay_ok: row.delay_ok,
          daydream: row.daydream,
          rerun: row.rerun,
          court: row.court,
          stayed_indoors_all_day: Boolean(row.stayed_indoors_all_day),
          left_room:
            Boolean(row.left_room) ||
            kind === "body_or_room" ||
            kind === "leave_room",
          left_home: Boolean(row.left_home),
          movement_minutes: row.movement_minutes ?? 0,
          interrupt_walk_minutes: row.interrupt_walk_minutes ?? 0,
          fantasy_walk_minutes: row.fantasy_walk_minutes ?? 0,
          headphones_on_walk: Boolean(row.headphones_on_walk),
          music_cinematic_on_walk: Boolean(row.music_cinematic_on_walk),
          proof_brick_done: true,
          proof_brick_kind: kind,
          fantasy_minutes_scheduled: row.fantasy_minutes_scheduled ?? 0,
          fantasy_minutes_unplanned: row.fantasy_minutes_unplanned ?? 0,
          note: row.note || "",
        };
      } catch {
        base = {
          ...(EMPTY_DAILY_PATCH as DailyUpsert),
          left_room: kind === "body_or_room" || kind === "leave_room",
          proof_brick_done: true,
          proof_brick_kind: kind,
        };
      }
      if (kind === "interrupt_walk" && !base.interrupt_walk_minutes) {
        base.interrupt_walk_minutes = 10;
        base.movement_minutes =
          (base.interrupt_walk_minutes || 0) + (base.fantasy_walk_minutes || 0);
      }
      await putDaily(today, base);
      toast.show("Proof noted on Daily");
      closeProof();
    } catch {
      try {
        await createEntry({
          activity: "Proof brick",
          happened: `Proof step: ${kind}`,
          emotions: [],
          intensity: Math.min(intensity, 5),
          remedy: kind.replace(/_/g, " "),
          result: "worked",
          tags: ["proof", kind],
        });
        toast.show("Proof logged");
        closeProof();
      } catch (err) {
        toast.show(
          err instanceof ApiError ? err.message : "Could not save proof",
          "error",
        );
      }
    } finally {
      setProofBusy(false);
    }
  };

  return (
    <div class="app-shell">
      <header class="top-bar material-bar">
        <Pressable
          className="btn btn-plain"
          ariaLabel="Back"
          onClick={() => navigate("/")}
        >
          ‹ Back
        </Pressable>
        <h1 class="page-title">Log</h1>
      </header>
      <main class="page stack">
        <div>
          <label class="field-label" for="happened">
            Happened
          </label>
          <textarea
            id="happened"
            class="field-textarea"
            autofocus
            required
            value={happened}
            onInput={(e) => setHappened((e.target as HTMLTextAreaElement).value)}
            placeholder="What happened?"
          />
        </div>

        <div>
          <span class="field-label" id="emotions-label">
            Emotions
          </span>
          <div class="chip-row" role="group" aria-labelledby="emotions-label">
            {EMOTION_PICKS.map((emotion) => (
              <Pressable
                key={emotion}
                className="chip"
                ariaPressed={emotions.includes(emotion)}
                onClick={() => toggleEmotion(emotion)}
              >
                {emotion}
              </Pressable>
            ))}
          </div>
        </div>

        <div class="slider-wrap">
          <label class="field-label" for="intensity">
            Intensity · {intensity}
          </label>
          <input
            id="intensity"
            type="range"
            min={1}
            max={10}
            step={1}
            value={intensity}
            onInput={(e) =>
              setIntensity(Number((e.target as HTMLInputElement).value))
            }
          />
        </div>

        <div>
          <span class="field-label">Result</span>
          <ResultChips value={result} onChange={setResult} />
        </div>

        <div>
          <label class="field-label" for="remedy">
            Remedy
          </label>
          <input
            id="remedy"
            class="field-input"
            type="text"
            value={remedy}
            onInput={(e) => setRemedy((e.target as HTMLInputElement).value)}
            placeholder="One concrete action — not more labeling at high heat"
          />
          {labelOnlyWarn ? (
            <p class="muted" role="status">
              Label once (30s). Proof is visible outside your head. At intensity{" "}
              {intensity}, pick a proof brick — not labeling-only.
            </p>
          ) : null}
        </div>

        {intensity >= 7 ? (
          <div>
            <span class="field-label">Avoidance (optional)</span>
            <div class="chip-row" role="group" aria-label="Avoidance">
              {AVOIDANCE_PICKS.map((tag) => (
                <Pressable
                  key={tag}
                  className="chip"
                  ariaPressed={avoidance.includes(tag)}
                  onClick={() => toggleAvoidance(tag)}
                >
                  {tag}
                </Pressable>
              ))}
            </div>
            <p class="muted">Preview, not proof. Proof brick comes after save.</p>
          </div>
        ) : null}

        <div>
          <span class="field-label">Tags</span>
          <div class="chip-row" role="group" aria-label="Suggested tags">
            {TAG_SUGGESTS.map((tag) => (
              <Pressable
                key={tag}
                className="chip"
                ariaPressed={tagList.map((t) => t.toLowerCase()).includes(tag)}
                onClick={() => toggleTag(tag)}
              >
                {tag}
              </Pressable>
            ))}
          </div>
        </div>

        <Button disabled={!canSave} onClick={onSave} className="btn-large">
          {saving ? "Saving…" : "Save entry"}
        </Button>

        <Pressable
          className="btn btn-plain"
          ariaPressed={moreOpen}
          onClick={() => setMoreOpen((open) => !open)}
        >
          {moreOpen ? "Hide more" : "More"}
        </Pressable>

        {moreOpen ? (
          <div class="more-block stack">
            <div>
              <label class="field-label" for="activity">
                Activity
              </label>
              <input
                id="activity"
                class="field-input"
                type="text"
                value={activity}
                onInput={(e) =>
                  setActivity((e.target as HTMLInputElement).value)
                }
              />
            </div>
            <div>
              <label class="field-label" for="tags">
                More tags
              </label>
              <input
                id="tags"
                class="field-input"
                type="text"
                value={tags}
                onInput={(e) => setTags((e.target as HTMLInputElement).value)}
                placeholder="comma,separated"
              />
            </div>
            <div>
              <label class="field-label" for="notes">
                Notes
              </label>
              <textarea
                id="notes"
                class="field-textarea"
                value={notes}
                onInput={(e) =>
                  setNotes((e.target as HTMLTextAreaElement).value)
                }
              />
            </div>
          </div>
        ) : null}
      </main>

      <Sheet
        open={proofOpen}
        title="One proof step (not more labeling)"
        onClose={closeProof}
      >
        <div class="stack">
          <p class="muted">
            High spike logged. Pick one small real step you can do now — body,
            room, message, or interrupt walk.
          </p>
          <div class="choice-list">
            {PROOF_KINDS.map((opt) => (
              <Pressable
                key={opt.value}
                className="choice-item"
                disabled={proofBusy}
                onClick={() => void saveProof(opt.value)}
              >
                {opt.label}
              </Pressable>
            ))}
          </div>
          <Button className="btn-secondary" disabled={proofBusy} onClick={closeProof}>
            Skip for now
          </Button>
        </div>
      </Sheet>

      <Sheet
        open={Boolean(finishPrompt)}
        title="Finish now?"
        onClose={() => {
          setFinishPrompt(null);
          navigate("/");
        }}
      >
        <div class="stack">
          <p class="muted">Set a result for this entry, or leave it pending.</p>
          <Button
            className="btn-large"
            onClick={() => {
              const id = finishPrompt?.id;
              setFinishPrompt(null);
              if (id) openResolveId(id);
              navigate("/");
            }}
          >
            Finish now
          </Button>
          <Button
            variant="secondary"
            onClick={() => {
              setFinishPrompt(null);
              navigate("/");
            }}
          >
            Later
          </Button>
        </div>
      </Sheet>
    </div>
  );
}
