import { ArchivedHabits } from "../components/ArchivedHabits";
import { AccountSettings } from "../components/AccountSettings";
import { useCallback, useEffect, useRef, useState } from "react";
import { useAuth } from "../components/AuthProvider";
import { DiscordSignInButton } from "../components/DiscordSignInButton";
import { PageLayout } from "../components/design-system/PageLayout";
import {
  Button,
  ButtonLink,
  Checkbox,
  Counter,
  SectionHeading,
} from "../components/design-system/primitives";
import { Card, CardGrid } from "../components/design-system/Card";
import { HabitChart } from "../components/design-system/HabitChart";
import { HABIT_COLORS } from "../components/design-system/calendar-model";
import { HabitForm } from "../components/HabitForm";
import { habitRequest, scheduleLabel, type TodayHabit, type TodayResponse } from "../lib/dashboard";
import { HabitHistory } from "../components/HabitHistory";
import { WelcomePanel } from "../components/design-system/WelcomePanel";
import type { PublicUser } from "../shared/user";
import { ItemActions } from "../components/design-system/ItemActions";
import { type ItemMode } from "../components/InlineItemForm";
import { InlineHabitEditor } from "../components/InlineHabitEditor";
import { InlineTaskEditor } from "../components/InlineTaskEditor";
import type { Schedule } from "../habits/contracts";
import { ShareProgress } from "../components/ShareProgress";

export function Home() {
  const { user, loading, busy, error, accountError, signIn, signOut, retry } = useAuth();
  return (
    <PageLayout
      header={
        loading ? (
          <span className="ds-library-label">Loading account…</span>
        ) : user ? (
          <Button variant="text" disabled={busy} onClick={() => void signOut()}>
            {busy ? "Signing out…" : "Sign out"}
          </Button>
        ) : (
          !accountError && <DiscordSignInButton variant="secondary" onClick={signIn} />
        )
      }
    >
      {error && (
        <div className="ds-form-feedback" role="alert">
          {error}
        </div>
      )}
      {loading ? (
        <section className="ds-section" aria-busy="true">
          <p role="status">Getting things ready…</p>
        </section>
      ) : accountError ? (
        <section className="ds-section">
          <h1 className="type-section">Let’s try that again.</h1>
          <Card headingLevel={2} heading="Account unavailable">
            <p>Your account couldn’t be loaded.</p>
            <div className="ds-actions">
              <Button onClick={retry}>Try again</Button>
            </div>
          </Card>
        </section>
      ) : user ? (
        <Dashboard key={user.id} user={user} onExpired={retry} />
      ) : (
        <Welcome onSignIn={signIn} />
      )}
    </PageLayout>
  );
}

function Welcome({ onSignIn }: { onSignIn: () => void }) {
  const [water, setWater] = useState(3);
  const [read, setRead] = useState(false);
  return (
    <>
      <section className="ds-section ds-split-section" aria-labelledby="welcome-title">
        <div className="ds-section-heading">
          <p className="ds-eyebrow">A LITTLE, EVERY DAY</p>
          <h1 id="welcome-title" className="type-section">
            Small steps.
            <br />
            <em>Lasting rhythm.</em>
          </h1>
        </div>
        <Card headingLevel={2} heading="Make room for what matters.">
          <p>
            Check in, count a little more, or work through a few tasks. See your progress grow, one
            day at a time.
          </p>
          <div className="ds-actions">
            <DiscordSignInButton onClick={onSignIn} />
            <ButtonLink href="#try-it">Try it below ↓</ButtonLink>
          </div>
          <p className="type-small">Sign in with your Discord account to save your habits.</p>
        </Card>
      </section>
      <section className="ds-section" id="try-it" aria-labelledby="try-it-title">
        <div className="ds-section-top">
          <SectionHeading
            number="TRY A CHECK-IN"
            id="try-it-title"
            title={
              <>
                A little progress. <em>Made visible.</em>
              </>
            }
          />
          <span className="ds-demo-note">Example · September 4, 2026 · not saved</span>
        </div>
        <div className="ds-habit-chart-grid">
          <HabitChart
            headingLevel={3}
            name="Drink water"
            method="Count target"
            value={water}
            target={8}
            unit="glasses"
            color={HABIT_COLORS.water}
          >
            <Counter label="glasses of water" value={water} target={8} onChange={setWater} />
          </HabitChart>
          <HabitChart
            headingLevel={3}
            name="Read a little"
            method="Simple check-in"
            value={Number(read)}
            target={1}
            unit="reading session"
            color={HABIT_COLORS.reading}
          >
            <Checkbox
              label="Reading done"
              checked={read}
              onChange={(event) => setRead(event.target.checked)}
            />
          </HabitChart>
        </div>
      </section>
      <section className="ds-section" aria-label="Your own rhythm">
        <CardGrid>
          <Card heading="Your habits. Your pace." eyebrow="MAKE IT FIT">
            <p>
              Pick a check-in, a count target, or a task list. Repeat daily, on selected weekdays,
              or at your own interval.
            </p>
          </Card>
          <Card heading="See the days add up." eyebrow="KEEP PERSPECTIVE" variant="outlined">
            <p>
              Explore your calendar to see the progress behind each square. Days off stay distinct
              from missed days.
            </p>
          </Card>
        </CardGrid>
      </section>
    </>
  );
}

function Dashboard({ user, onExpired }: { user: PublicUser; onExpired: () => void }) {
  const [today, setToday] = useState<TodayResponse | null>(null);
  const [error, setError] = useState("");
  const [notice, setNotice] = useState("");
  const [revision, setRevision] = useState(0);
  const [attempt, setAttempt] = useState(0);
  const [loading, setLoading] = useState(true);
  const [adding, setAdding] = useState(false);
  const [sharing, setSharing] = useState(false);
  const [settings, setSettings] = useState(false);
  const [archive, setArchive] = useState(false);
  const [busy, setBusy] = useState(false);
  const [needsRefresh, setNeedsRefresh] = useState(false);
  const saving = useRef(false);
  const mounted = useRef(true);
  const expired = useRef(onExpired);
  expired.current = onExpired;
  useEffect(() => {
    mounted.current = true;
    return () => {
      mounted.current = false;
    };
  }, []);

  const reportError = useCallback((error: unknown) => {
    if (error instanceof Error && error.message.includes("session has expired")) expired.current();
    else
      setError(
        error instanceof Error ? error.message : "Could not load your habits. Please try again."
      );
  }, []);

  useEffect(() => {
    const controller = new AbortController();
    setLoading(true);
    setError("");
    habitRequest<TodayResponse>("/today", { signal: controller.signal })
      .then((data) => {
        if (!controller.signal.aborted) {
          setToday(data);
          setNeedsRefresh(false);
          setRevision((value) => value + 1);
        }
      })
      .catch((error) => {
        if (!controller.signal.aborted) reportError(error);
      })
      .finally(() => {
        if (!controller.signal.aborted) setLoading(false);
      });
    return () => controller.abort();
  }, [attempt, reportError]);

  // Refresh after returning to the page and across the account's local midnight.
  useEffect(() => {
    const refresh = () => {
      if (!saving.current && document.visibilityState === "visible")
        setAttempt((value) => value + 1);
    };
    const timer = window.setInterval(refresh, 60_000);
    window.addEventListener("focus", refresh);
    return () => {
      window.clearInterval(timer);
      window.removeEventListener("focus", refresh);
    };
  }, []);

  async function update(
    habit: TodayHabit,
    body: { count: number } | { done: boolean },
    taskId?: string
  ) {
    if (saving.current || loading || needsRefresh || !today) return;
    saving.current = true;
    setBusy(true);
    setError("");
    setNotice("");
    try {
      const path = `/habits/${habit.habitId}/days/${today.date}/${taskId ? `tasks/${taskId}` : "progress"}`;
      const updated = await habitRequest<TodayHabit>(path, {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(body),
      });
      if (!mounted.current) return;
      setToday((current) => {
        if (!current) return current;
        const habits = current.habits.map((item) =>
          item.habitId === updated.habitId ? updated : item
        );
        return {
          ...current,
          habits,
          due: habits.filter((item) => item.due).length,
          completed: habits.filter((item) => item.complete).length,
        };
      });
      setRevision((value) => value + 1);
      setNotice(`${habit.name} saved.`);
    } catch (error) {
      if (mounted.current) reportError(error);
    } finally {
      saving.current = false;
      if (mounted.current) setBusy(false);
    }
  }

  async function manage(
    habit: TodayHabit,
    patch: Record<string, unknown> | null,
    taskId?: string,
    createTask = false
  ) {
    if (saving.current || loading || needsRefresh)
      throw new Error("Please wait for the dashboard to refresh.");
    saving.current = true;
    setBusy(true);
    setError("");
    setNotice("");
    try {
      await habitRequest(
        `/habits/${habit.habitId}${createTask ? "/tasks" : taskId ? `/tasks/${taskId}` : ""}`,
        {
          method: createTask ? "POST" : patch ? "PATCH" : "DELETE",
          ...(patch
            ? { headers: { "Content-Type": "application/json" }, body: JSON.stringify(patch) }
            : {}),
        }
      );
      // A refresh failure must never turn a successful delete into a retryable delete.
      try {
        const refreshed = await habitRequest<TodayResponse>("/today");
        if (mounted.current) {
          setToday(refreshed);
          setNeedsRefresh(false);
          setRevision((value) => value + 1);
        }
      } catch (refreshError) {
        if (refreshError instanceof Error && refreshError.message.includes("session has expired"))
          expired.current();
        if (mounted.current) {
          setNeedsRefresh(true);
          setError(
            "Your change was saved, but the dashboard could not refresh. Try again to load the latest data."
          );
        }
      }
      if (mounted.current) {
        setNotice(
          `${taskId || createTask ? "Task" : "Habit"} ${createTask ? "added" : patch ? "updated" : taskId ? "deleted" : "archived"}.`
        );
        if (!patch)
          window.requestAnimationFrame(() => {
            (
              document.getElementById("habits-title") ?? document.getElementById("add-habit")
            )?.focus();
          });
      }
    } catch (error) {
      if (error instanceof Error && error.message.includes("session has expired"))
        expired.current();
      throw error;
    } finally {
      saving.current = false;
      if (mounted.current) setBusy(false);
    }
  }

  return (
    <>
      <WelcomePanel
        name={user.displayName || user.username}
        avatarUrl={user.avatarUrl}
        date={today?.date}
        timezone={today?.timezone || user.timezone}
      >
        {today && (
          <>
            <div className="ds-welcome-progress" role="status">
              {today.due > 0 ? (
                <>
                  <span className="type-title">
                    {today.completed} / {today.due}
                  </span>
                  <span>
                    habits complete today
                    {today.completed === today.due && (
                      <span className="ds-welcome-complete">A little, all done.</span>
                    )}
                  </span>
                </>
              ) : (
                <p>
                  {today.habits.length
                    ? "Nothing scheduled today. Enjoy a little breathing room."
                    : "Start with one habit. Your first small step starts here."}
                </p>
              )}
            </div>
            <div className="ds-actions">
              <Button variant="text" disabled={busy} aria-expanded={archive} onClick={() => setArchive(!archive)}>Archived habits</Button>
              <Button variant="text" disabled={busy} aria-expanded={settings} onClick={() => setSettings(!settings)}>Settings</Button>
              <Button
                id="add-habit"
                disabled={adding || busy || loading || needsRefresh}
                onClick={() => setAdding(true)}
                aria-expanded={adding}
                aria-controls={adding ? "new-habit" : undefined}
              >
                Add a habit +
              </Button>
              {today.habits.length > 0 && (
                <>
                  <Button id="open-sharing" variant="secondary" disabled={busy || loading || needsRefresh || sharing} aria-expanded={sharing} aria-controls={sharing ? "share-progress" : undefined} onClick={() => setSharing(true)}>Share progress</Button>
                  <ButtonLink href="#habits-title">View your habits ↓</ButtonLink>
                </>
              )}
            </div>
          </>
        )}
      </WelcomePanel>
      {archive && today && <ArchivedHabits date={today.date} revision={revision} onExpired={onExpired} onChanged={() => setAttempt(value => value + 1)} />}
      {settings && <AccountSettings user={user} onChanged={onExpired} onClose={() => setSettings(false)} />}
      {sharing && today && <ShareProgress user={user} today={today} revision={revision} onClose={() => { setSharing(false); requestAnimationFrame(() => document.getElementById("open-sharing")?.focus()); }} />}
      {error && (
        <div className="ds-form-feedback" role="alert">
          <p>{error}</p>
          <Button
            variant="secondary"
            disabled={loading}
            onClick={() => setAttempt((value) => value + 1)}
          >
            Try again
          </Button>
        </div>
      )}
      {!today && loading && (
        <section className="ds-section">
          <p role="status">Loading your habits…</p>
        </section>
      )}
      {today && (
        <>
          {adding && (
            <HabitForm
              date={today.date}
              onCancel={() => setAdding(false)}
              onExpired={() => expired.current()}
              onCreated={(name) => {
                setAdding(false);
                setNotice(`${name} created.`);
                setAttempt((value) => value + 1);
              }}
            />
          )}
          <p className="ds-form-feedback" role="status">
            {notice}
          </p>
          {today.habits.length > 0 && (
            <section
              className="ds-section"
              aria-labelledby="habits-title"
              aria-busy={busy || loading}
            >
              <div className="ds-section-top">
                <SectionHeading
                  number="YOUR HABITS"
                  id="habits-title"
                  title={
                    <>
                      One day <em>at a time.</em>
                    </>
                  }
                />
              </div>
              <div className="ds-habit-chart-grid">
                {today.habits.map((habit) => (
                  <SavedHabit
                    key={habit.habitId}
                    habit={habit}
                    date={today.date}
                    revision={revision}
                    disabled={busy || loading || needsRefresh}
                    onHistorySaved={() => setAttempt(value => value + 1)}
                    onUpdate={update}
                    onManage={manage}
                    onExpired={() => expired.current()}
                  />
                ))}
              </div>
            </section>
          )}
        </>
      )}
    </>
  );
}

function SavedHabit({
  habit,
  date,
  revision,
  disabled,
  onUpdate,
  onManage,
  onHistorySaved,
  onExpired,
}: {
  habit: TodayHabit;
  date: string;
  revision: number;
  disabled: boolean;
  onUpdate: (
    habit: TodayHabit,
    body: { count: number } | { done: boolean },
    taskId?: string
  ) => Promise<void>;
  onExpired: () => void;
  onHistorySaved: () => void;
  onManage: (
    habit: TodayHabit,
    patch: Record<string, unknown> | null,
    taskId?: string,
    createTask?: boolean
  ) => Promise<void>;
}) {
  const [editing, setEditing] = useState<{ mode: ItemMode; color: string } | null>(null);
  const [addingTask, setAddingTask] = useState(false);
  const blocked = disabled || !habit.due;
  return (
    <HabitHistory
      habit={habit}
      date={date}
      revision={revision}
      disabled={disabled}
      onExpired={onExpired}
      onHistorySaved={onHistorySaved}
      onEdit={(color) => setEditing({ mode: "edit", color })}
      onDelete={() => setEditing({ mode: "delete", color: "#196127" })}
      editor={
        editing && habit.requirements ? (
          <InlineHabitEditor
            key={editing.mode}
            config={habit.requirements}
            date={date}
            color={editing.color}
            mode={editing.mode}
            disabled={disabled}
            onClose={() => setEditing(null)}
            onSave={(patch) => onManage(habit, patch)}
            onDelete={() => onManage(habit, null)}
          />
        ) : undefined
      }
      tasks={
        habit.requirements?.method === "tasks" ? (
          <>
            {habit.requirements.tasks.map((config) => {
              const occurrence = habit.tasks.find((task) => task.taskId === config.id);
              return (
                <SavedTask
                  key={config.id}
                  task={{ taskId: config.id, name: config.name, done: occurrence?.done ?? false }}
                  disabled={disabled}
                  blocked={blocked || !occurrence}
                  scheduled={!!occurrence}
                  schedule={config.schedule}
                  habitSchedule={habit.requirements!.schedule}
                  date={date}
                  onCheck={(done) => void onUpdate(habit, { done }, config.id)}
                  onSave={(patch) => onManage(habit, patch, config.id)}
                  onDelete={() => onManage(habit, null, config.id)}
                />
              );
            })}
            {addingTask ? (
              <InlineTaskEditor
                name=""
                schedule={{ type: "daily" }}
                habitSchedule={habit.requirements.schedule}
                date={date}
                mode="edit"
                creating
                disabled={disabled}
                onClose={() => setAddingTask(false)}
                onSave={(patch) => onManage(habit, patch, undefined, true)}
                onDelete={async () => {}}
              />
            ) : (
              <div className="ds-task-add-action">
                {!habit.requirements.tasks.length && (
                  <p className="ds-footnote">Add a task to start checking in.</p>
                )}
                <Button
                  variant="text"
                  disabled={disabled || habit.requirements.tasks.length >= 100}
                  onClick={() => setAddingTask(true)}
                >
                  Add task +
                </Button>
              </div>
            )}
          </>
        ) : undefined
      }
    >
      {habit.method === "count" ? (
        <Counter
          label={habit.name ?? "habit count"}
          value={habit.value}
          target={habit.target ?? 0}
          disabled={blocked}
          onChange={(count) => void onUpdate(habit, { count })}
        />
      ) : habit.method === "manual" ? (
        <Checkbox
          label={`${habit.name} done`}
          checked={habit.complete}
          disabled={blocked}
          onChange={(event) => void onUpdate(habit, { done: event.target.checked })}
        />
      ) : undefined}
    </HabitHistory>
  );
}

function SavedTask({
  task,
  schedule,
  habitSchedule,
  date,
  disabled,
  blocked,
  scheduled,
  onCheck,
  onSave,
  onDelete,
}: {
  task: Pick<TodayHabit["tasks"][number], "taskId" | "name" | "done">;
  disabled: boolean;
  blocked: boolean;
  scheduled: boolean;
  schedule: Schedule;
  habitSchedule: Schedule;
  date: string;
  onCheck: (done: boolean) => void;
  onSave: (patch: { name?: string; schedule?: Schedule }) => Promise<void>;
  onDelete: () => Promise<void>;
}) {
  const [mode, setMode] = useState<ItemMode | null>(null);
  return (
    <div className="ds-editable-task">
      <div className="ds-editable-task-row">
        <Checkbox
          label={task.name}
          description={`${scheduleLabel(schedule)}${scheduled ? "" : " · Not scheduled today"}`}
          checked={task.done}
          disabled={blocked || !!mode}
          onChange={(event) => onCheck(event.target.checked)}
        />
        <ItemActions
          name={task.name}
          kind="task"
          disabled={disabled || !!mode}
          onEdit={() => setMode("edit")}
          onDelete={() => setMode("delete")}
        />
      </div>
      {mode && (
        <InlineTaskEditor
          name={task.name}
          schedule={schedule}
          habitSchedule={habitSchedule}
          date={date}
          mode={mode}
          disabled={disabled}
          onSave={onSave}
          onDelete={onDelete}
          onClose={() => setMode(null)}
        />
      )}
    </div>
  );
}
