/** Plan day: timeline, paste ChatPlan, check-off, annotate, day review, export. */

import { ChevronLeft, ChevronRight } from "lucide-preact";
import { useEffect, useMemo, useState } from "preact/hooks";
import {
  ApiError,
  checkPlanBlock,
  exportChatPlan,
  getPlan,
  importChatPlan,
  patchPlanMeta,
  reschedulePlan,
  seedPlan,
  type DayPlanView,
  type DayReview,
  type ScheduledBlock,
} from "../api";
import { BlockSheet } from "../components/BlockSheet";
import { Button } from "../components/Button";
import { Pressable } from "../components/Pressable";
import { Sheet } from "../components/Sheet";
import { Timeline } from "../components/Timeline";
import { useToast } from "../components/Toast";
import { isoDate } from "../dates";
import { navigate } from "../router";

function shiftDay(day: string, delta: number): string {
  const d = new Date(`${day}T00:00:00`);
  d.setDate(d.getDate() + delta);
  return isoDate(d);
}

function friendly(day: string): string {
  return new Date(`${day}T00:00:00`).toLocaleDateString(undefined, {
    weekday: "short",
    month: "short",
    day: "numeric",
  });
}

function parseMin(hhmm: string): number {
  const [h, m] = hhmm.split(":").map(Number);
  return h * 60 + m;
}

const EMPTY_REVIEW: DayReview = {
  comment: "",
  emotions: [],
  fse_events: "",
  what_moved: "",
  what_avoided: "",
  tomorrow_change: "",
};

export function Plan({ initialDate }: { initialDate?: string }) {
  const toast = useToast();
  const [day, setDay] = useState(initialDate || isoDate(new Date()));
  const [plan, setPlan] = useState<DayPlanView | null>(null);
  const [busy, setBusy] = useState(false);
  const [selected, setSelected] = useState<ScheduledBlock | null>(null);
  const [sheetMode, setSheetMode] = useState<"view" | "create">("view");
  const [sheetOpen, setSheetOpen] = useState(false);
  const [pasteOpen, setPasteOpen] = useState(false);
  const [pasteText, setPasteText] = useState("");
  const [pasteMode, setPasteMode] = useState<"replace" | "merge">("replace");
  const [pasteError, setPasteError] = useState("");
  const [dayReview, setDayReview] = useState<DayReview>(EMPTY_REVIEW);

  const load = async (date: string) => {
    try {
      const next = await getPlan(date);
      setPlan(next);
      setDayReview({ ...EMPTY_REVIEW, ...(next.day_review || {}) });
    } catch {
      setPlan(null);
      setDayReview(EMPTY_REVIEW);
    }
  };

  useEffect(() => {
    if (initialDate && initialDate !== day) setDay(initialDate);
  }, [initialDate]);

  useEffect(() => {
    void load(day);
    navigate(`/plan/${day}`);
  }, [day]);

  const progress = useMemo(() => {
    const blocks = plan?.blocks ?? [];
    const total = blocks.length;
    const done = blocks.filter((b) => b.status === "done" || b.status === "partial").length;
    const p0 = blocks.filter((b) => b.priority === "P0");
    const p0Left = p0.filter((b) => b.status === "planned").length;
    return { total, done, p0Left, p0Total: p0.length };
  }, [plan]);

  const openBlock = (block: ScheduledBlock) => {
    setSelected(block);
    setSheetMode("view");
    setSheetOpen(true);
  };

  const openCreate = () => {
    setSelected(null);
    setSheetMode("create");
    setSheetOpen(true);
  };

  const onSeed = async () => {
    setBusy(true);
    try {
      setPlan(await seedPlan(day));
      toast.show("Starter day added");
    } catch (e) {
      toast.show(e instanceof Error ? e.message : "Seed failed", "error");
    } finally {
      setBusy(false);
    }
  };

  const onReschedule = async () => {
    if (!confirm("Reschedule the rest of this day? Locked blocks stay put.")) return;
    setBusy(true);
    try {
      const result = await reschedulePlan(day, { reason: "user", force: true });
      setPlan(result.plan);
      toast.show(result.source === "rules" ? "Rescheduled (rules)" : "Rescheduled");
    } catch (e) {
      toast.show(e instanceof Error ? e.message : "Reschedule failed", "error");
    } finally {
      setBusy(false);
    }
  };

  const onExport = async () => {
    setBusy(true);
    try {
      const chat = await exportChatPlan(day);
      await navigator.clipboard.writeText(JSON.stringify(chat, null, 2));
      toast.show("Copied — paste to your chat");
    } catch (e) {
      toast.show(e instanceof Error ? e.message : "Export failed", "error");
    } finally {
      setBusy(false);
    }
  };

  const onPasteSubmit = async () => {
    setPasteError("");
    let parsed: unknown;
    try {
      parsed = JSON.parse(pasteText);
    } catch {
      setPasteError("Not valid JSON");
      return;
    }
    setBusy(true);
    try {
      const next = await importChatPlan(day, parsed, pasteMode);
      setPlan(next);
      setDayReview({ ...EMPTY_REVIEW, ...(next.day_review || {}) });
      setPasteOpen(false);
      setPasteText("");
      toast.show("Plan pasted");
      if (next.import_warnings?.length) {
        toast.show(next.import_warnings[0], "error");
      }
    } catch (e) {
      const msg = e instanceof ApiError ? e.message : e instanceof Error ? e.message : "Import failed";
      setPasteError(msg);
    } finally {
      setBusy(false);
    }
  };

  const toggleCheck = async (block: ScheduledBlock) => {
    const nextStatus = block.status === "done" || block.status === "partial" ? "planned" : "done";
    setBusy(true);
    try {
      setPlan(await checkPlanBlock(day, block.id, { status: nextStatus }));
    } catch (e) {
      toast.show(e instanceof Error ? e.message : "Check failed", "error");
    } finally {
      setBusy(false);
    }
  };

  const skipBlock = async (block: ScheduledBlock) => {
    setBusy(true);
    try {
      setPlan(
        await checkPlanBlock(day, block.id, {
          status: "skipped",
          skip_reason: "other",
        }),
      );
    } catch (e) {
      toast.show(e instanceof Error ? e.message : "Skip failed", "error");
    } finally {
      setBusy(false);
    }
  };

  const saveDayReview = async () => {
    setBusy(true);
    try {
      const next = await patchPlanMeta(day, { day_review: dayReview });
      setPlan(next);
      toast.show("Day review saved");
    } catch (e) {
      toast.show(e instanceof Error ? e.message : "Save failed", "error");
    } finally {
      setBusy(false);
    }
  };

  const capacity = plan?.capacity_hint ?? 1;
  const health = plan?.health;
  const empty = !plan?.blocks.length;
  const nowMin = new Date().getHours() * 60 + new Date().getMinutes();
  const sorted = [...(plan?.blocks ?? [])].sort(
    (a, b) => parseMin(a.start) - parseMin(b.start),
  );

  return (
    <div class="app-shell">
      <header class="top-bar material-bar plan-header">
        <div class="plan-day-nav">
          <Pressable className="icon-btn" ariaLabel="Previous day" onClick={() => setDay(shiftDay(day, -1))}>
            <ChevronLeft size={22} />
          </Pressable>
          <div>
            <h1 class="page-title">Plan</h1>
            <span class="daily-date">{friendly(day)}</span>
          </div>
          <Pressable className="icon-btn" ariaLabel="Next day" onClick={() => setDay(shiftDay(day, 1))}>
            <ChevronRight size={22} />
          </Pressable>
        </div>
        <Pressable className="chip-btn" onClick={() => setDay(isoDate(new Date()))}>
          Today
        </Pressable>
      </header>

      <main class="page stack plan-page">
        <div class="plan-meta-row">
          <span class="caption">Capacity {Math.round(capacity * 100)}%</span>
          {health && (
            <span class="caption">
              Health {Math.round(health.score * 100)}% · v{plan?.version ?? 1}
            </span>
          )}
          {!empty && (
            <span class="caption">
              {progress.done}/{progress.total}
              {progress.p0Total ? ` · P0 left ${progress.p0Left}` : ""}
            </span>
          )}
        </div>

        {(plan?.title || plan?.intention) && (
          <section class="tip-card" role="status">
            {plan?.title ? <strong>{plan.title}</strong> : null}
            {plan?.intention ? <p class="muted">{plan.intention}</p> : null}
          </section>
        )}

        {capacity < 0.55 && (
          <div class="tip-card" role="status">
            <p>High load risk — lighter plan recommended.</p>
          </div>
        )}

        <div class="plan-actions">
          <Button className="btn-secondary" disabled={busy} onClick={() => setPasteOpen(true)}>
            Paste plan
          </Button>
          <Button className="btn-secondary" disabled={busy || empty} onClick={onExport}>
            Export / Copy
          </Button>
          <Button className="btn-secondary" disabled={busy} onClick={openCreate}>
            + Block
          </Button>
          <Button className="btn-secondary" disabled={busy} onClick={onReschedule}>
            Reschedule
          </Button>
        </div>

        <p class="muted">
          Paste the JSON your chat wrote. Check off when done in the real world. Export and paste
          back for review.
        </p>

        {empty ? (
          <section class="surface-card empty-card stack">
            <p>No blocks yet. Paste a plan from chat, seed a starter day, or add one block.</p>
            <Button disabled={busy} onClick={() => setPasteOpen(true)}>
              Paste plan from chat
            </Button>
            <Button className="btn-secondary" disabled={busy} onClick={onSeed}>
              Seed starter day
            </Button>
            <Button className="btn-secondary" disabled={busy} onClick={openCreate}>
              Add block
            </Button>
          </section>
        ) : (
          <>
            <section class="surface-card stack" aria-label="Checklist">
              <h2 class="section-title">Checklist</h2>
              {sorted.map((block) => {
                const overdue =
                  day === isoDate(new Date()) &&
                  block.status === "planned" &&
                  parseMin(block.end) < nowMin;
                const checked = block.status === "done" || block.status === "partial";
                return (
                  <div
                    key={block.id}
                    class={`entry-row ${overdue ? "indoors-nudge" : ""}`.trim()}
                  >
                    <Pressable
                      className="chip"
                      ariaPressed={checked}
                      ariaLabel={checked ? "Mark planned" : "Mark done"}
                      onClick={() => void toggleCheck(block)}
                    >
                      {checked ? "✓" : "○"}
                    </Pressable>
                    <Pressable className="entry-row-main" onClick={() => openBlock(block)}>
                      <span class="entry-preview">
                        {block.priority === "P0" ? "P0 · " : ""}
                        {block.title}
                      </span>
                      <time>
                        {block.start}–{block.end}
                        {overdue ? " · overdue" : ""}
                      </time>
                    </Pressable>
                    {block.status === "planned" && (
                      <Pressable className="chip-btn" onClick={() => void skipBlock(block)}>
                        Skip
                      </Pressable>
                    )}
                  </div>
                );
              })}
            </section>

            <section class="surface-card timeline-card">
              <Timeline blocks={plan!.blocks} onSelect={openBlock} />
            </section>
          </>
        )}

        <section class="surface-card form-card stack" aria-label="Day review">
          <h2 class="section-title">Day review</h2>
          <p class="muted">One line is enough. FSE = fear / shame / embarrassment spike — short phrase.</p>
          <label class="field-label" for="dr-comment">
            Comment
          </label>
          <textarea
            id="dr-comment"
            class="field-textarea"
            value={dayReview.comment}
            onInput={(e) =>
              setDayReview({ ...dayReview, comment: (e.target as HTMLTextAreaElement).value })
            }
          />
          <label class="field-label" for="dr-fse">
            FSE events
          </label>
          <input
            id="dr-fse"
            class="field-input"
            value={dayReview.fse_events}
            onInput={(e) =>
              setDayReview({ ...dayReview, fse_events: (e.target as HTMLInputElement).value })
            }
          />
          <label class="field-label" for="dr-moved">
            What moved
          </label>
          <input
            id="dr-moved"
            class="field-input"
            value={dayReview.what_moved}
            onInput={(e) =>
              setDayReview({ ...dayReview, what_moved: (e.target as HTMLInputElement).value })
            }
          />
          <label class="field-label" for="dr-avoided">
            What avoided
          </label>
          <input
            id="dr-avoided"
            class="field-input"
            value={dayReview.what_avoided}
            onInput={(e) =>
              setDayReview({ ...dayReview, what_avoided: (e.target as HTMLInputElement).value })
            }
          />
          <label class="field-label" for="dr-tomorrow">
            Tomorrow change
          </label>
          <input
            id="dr-tomorrow"
            class="field-input"
            value={dayReview.tomorrow_change}
            onInput={(e) =>
              setDayReview({
                ...dayReview,
                tomorrow_change: (e.target as HTMLInputElement).value,
              })
            }
          />
          <Button disabled={busy} onClick={() => void saveDayReview()}>
            Save day review
          </Button>
        </section>
      </main>

      <BlockSheet
        open={sheetOpen}
        day={day}
        block={selected}
        mode={sheetMode}
        onClose={() => setSheetOpen(false)}
        onChanged={() => void load(day)}
      />

      <Sheet open={pasteOpen} title="Paste plan" onClose={() => setPasteOpen(false)} tall>
        <div class="stack">
          <p class="muted">Paste the JSON your chat wrote.</p>
          <textarea
            class="field-textarea"
            rows={14}
            value={pasteText}
            onInput={(e) => setPasteText((e.target as HTMLTextAreaElement).value)}
            placeholder='{ "schema_version": 1, "blocks": [ ... ] }'
          />
          <span class="field-label">Mode</span>
          <div class="chip-row" role="group">
            <Pressable
              className="chip"
              ariaPressed={pasteMode === "replace"}
              onClick={() => setPasteMode("replace")}
            >
              Replace
            </Pressable>
            <Pressable
              className="chip"
              ariaPressed={pasteMode === "merge"}
              onClick={() => setPasteMode("merge")}
            >
              Merge
            </Pressable>
          </div>
          {pasteError ? <p class="muted" role="alert">{pasteError}</p> : null}
          <Button disabled={busy || !pasteText.trim()} onClick={() => void onPasteSubmit()}>
            Import plan
          </Button>
        </div>
      </Sheet>
    </div>
  );
}
