import { useEffect, useLayoutEffect, useRef, useState } from "react";
import type { HabitConfig, Schedule } from "../../habits/contracts";
import { Button, Checkbox } from "./primitives";
import { Field } from "./Field";
import { CalendarHeatmap } from "./CalendarHeatmap";
import {
  HabitColorPicker,
  isHabitColor,
  previewHabitColors,
  type HabitColors,
} from "./HabitColorPicker";
import {
  EDITOR_HABITS,
  EDITOR_START,
  EDITOR_TODAY,
  backfillError,
  historicalDays,
  validateEditor,
  validateTasks,
} from "./editing-model";

const WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];

export function ScheduleEditor({
  value,
  onChange,
  anchorDate = EDITOR_TODAY,
}: {
  value: Schedule;
  onChange: (schedule: Schedule) => void;
  anchorDate?: string;
}) {
  return (
    <div className="ds-schedule-editor">
      <Field label="Repeats">
        {(id) => (
          <select
            id={id}
            value={value.type}
            onChange={(event) => {
              const type = event.target.value;
              onChange(
                type === "daily"
                  ? { type }
                  : type === "weekdays"
                    ? { type, days: [1, 2, 3, 4, 5] }
                    : type === "interval"
                    ? { type, every: 2, anchor: anchorDate }
                      : {
                          type: "weekly",
                          every: 1,
                          weekday: 1,
                          anchor: anchorDate,
                        },
              );
            }}
          >
            <option value="daily">Every day</option>
            <option value="weekdays">Selected weekdays</option>
            <option value="interval">Every few days</option>
            <option value="weekly">Every few weeks</option>
          </select>
        )}
      </Field>
      {value.type === "weekdays" && (
        <fieldset className="ds-weekday-field">
          <legend>Scheduled days · choose at least one</legend>
          <div className="ds-weekdays">
            {WEEKDAYS.map((day, index) => (
              <button
                type="button"
                key={day}
                aria-label={day}
                aria-pressed={value.days.includes(index)}
                onClick={() =>
                  onChange({
                    ...value,
                    days: value.days.includes(index)
                      ? value.days.filter((item) => item !== index)
                      : [...value.days, index].sort(),
                  })
                }
              >
                {day}
              </button>
            ))}
          </div>
        </fieldset>
      )}
      {(value.type === "interval" || value.type === "weekly") && (
        <div className="ds-form-grid">
          <Field
            label={value.type === "interval" ? "Every (days)" : "Every (weeks)"}
          >
            {(id) => (
              <input
                id={id}
                type="number"
                required
                min={1}
                max={value.type === "interval" ? 3650 : 520}
                value={Number.isNaN(value.every) ? "" : value.every}
                onChange={(event) =>
                  onChange({ ...value, every: event.target.valueAsNumber })
                }
              />
            )}
          </Field>
          <Field label="Starting on">
            {(id) => (
              <input
                id={id}
                type="date"
                required
                min="1970-01-01"
                max="9998-12-31"
                value={value.anchor}
                onInput={(event) =>
                  onChange({ ...value, anchor: event.currentTarget.value })
                }
              />
            )}
          </Field>
        </div>
      )}
      {value.type === "weekly" && (
        <Field label="On weekday">
          {(id) => (
            <select
              id={id}
              value={value.weekday}
              onChange={(event) =>
                onChange({ ...value, weekday: Number(event.target.value) })
              }
            >
              {WEEKDAYS.map((day, index) => (
                <option key={day} value={index}>
                  {day}
                </option>
              ))}
            </select>
          )}
        </Field>
      )}
    </div>
  );
}

function SaveBar({
  dirty,
  error,
  onCancel,
  label = "Save changes",
  disabled = false,
}: {
  dirty: boolean;
  error: string | null;
  onCancel: () => void;
  label?: string;
  disabled?: boolean;
}) {
  return (
    <>
      <p className="ds-form-feedback" role={error ? "alert" : "status"}>
        {error || (dirty ? "Unsaved changes" : "No unsaved changes")}
      </p>
      <div className="ds-form-actions">
        <Button type="submit" disabled={!dirty || disabled}>
          {label} <span aria-hidden="true">↗</span>
        </Button>
        <Button variant="text" onClick={onCancel} disabled={!dirty}>
          Cancel
        </Button>
      </div>
    </>
  );
}

type EditorTab = "Habit settings" | "Tasks" | "Backfill progress";
type TaskConfig = Extract<HabitConfig, { method: "tasks" }>;
type Correction = {
  habitId: string;
  name: string;
  date: string;
  before: number;
  after: number;
  target: number;
  unit: string;
};

export function EditingWorkbench({
  onColorsChange,
}: {
  onColorsChange?: (colors: HabitColors) => void;
}) {
  const [tab, setTab] = useState<EditorTab>("Habit settings");
  const taskFields = useRef<Record<string, HTMLInputElement | null>>({});
  const taskEditor = useRef<HTMLFieldSetElement | null>(null);
  const taskFocus = useRef<string | "add" | null>(null);
  const [habits, setHabits] = useState(() => structuredClone(EDITOR_HABITS));
  const [selectedId, setSelectedId] = useState("water");
  const selected = habits.find((habit) => habit.id === selectedId)!;
  const [draftColor, setDraftColor] = useState(selected.color);
  const previewColor = isHabitColor(draftColor) ? draftColor : selected.color;
  useEffect(() => {
    onColorsChange?.(previewHabitColors(habits, selectedId, previewColor));
  }, [habits, selectedId, previewColor, onColorsChange]);
  const [draft, setDraft] = useState<HabitConfig>(() =>
    structuredClone(selected.config),
  );
  const [error, setError] = useState<string | null>(null);
  const [notice, setNotice] = useState("");
  const [corrections, setCorrections] = useState<Correction[]>([]);
  const [entries, setEntries] = useState<
    Record<string, { value: number; tasks: boolean[] }>
  >({});
  const [date, setDate] = useState("2026-09-03");
  const historical = EDITOR_HABITS.find(
    (habit) => habit.id === selectedId,
  )!.config;
  const [progress, setProgress] = useState(
    () => historicalDays("water").find((day) => day.date === date)!.value,
  );
  const [taskChecks, setTaskChecks] = useState<boolean[]>([]);
  const entryKey = `${selectedId}:${date}`;
  const day = historicalDays(selectedId).find((day) => day.date === date);
  const savedProgress = entries[entryKey]?.value ?? day?.value ?? 0;
  const savedChecks =
    entries[entryKey]?.tasks ??
    (historical.method === "tasks"
      ? historical.tasks.map((_, index) => index < savedProgress)
      : []);
  const configDirty =
    JSON.stringify(draft) !== JSON.stringify(selected.config) ||
    draftColor.toLowerCase() !== selected.color.toLowerCase();
  const progressDirty =
    progress !== savedProgress ||
    (historical.method === "tasks" &&
      JSON.stringify(taskChecks) !== JSON.stringify(savedChecks));
  const dirty = tab === "Backfill progress" ? progressDirty : configDirty;
  const blocked = backfillError(selectedId, date, progress);
  useLayoutEffect(() => {
    if (taskFocus.current === "add")
      taskEditor.current?.querySelector<HTMLButtonElement>("[data-add-task]")?.focus();
    else if (taskFocus.current) taskFields.current[taskFocus.current]?.focus();
    taskFocus.current = null;
  });

  function loadProgress(id: string, nextDate: string) {
    const original = EDITOR_HABITS.find((habit) => habit.id === id)!.config;
    const entry = entries[`${id}:${nextDate}`];
    const value =
      entry?.value ??
      historicalDays(id).find((day) => day.date === nextDate)?.value ??
      0;
    setProgress(value);
    setTaskChecks(
      entry?.tasks ??
        (original.method === "tasks"
          ? original.tasks.map((_, index) => index < value)
          : []),
    );
    setError(null);
  }
  function canLeave() {
    return !dirty || window.confirm("Discard unsaved changes in this preview?");
  }
  function selectHabit(id: string) {
    if (!canLeave()) return;
    setSelectedId(id);
    setDraft(structuredClone(habits.find((habit) => habit.id === id)!.config));
    setDraftColor(habits.find((habit) => habit.id === id)!.color);
    loadProgress(id, date);
    setNotice("");
  }
  function selectTab(next: EditorTab) {
    if (next === tab || !canLeave()) return;
    const id = next === "Tasks" ? "tasks" : selectedId;
    setTab(next);
    setSelectedId(id);
    setDraft(structuredClone(habits.find((habit) => habit.id === id)!.config));
    setDraftColor(habits.find((habit) => habit.id === id)!.color);
    loadProgress(id, date);
    setNotice("");
  }
  function selectDate(next: string) {
    if (next === date || !canLeave()) return;
    setDate(next);
    loadProgress(selectedId, next);
    setNotice("");
  }
  function saveConfig() {
    if (!isHabitColor(draftColor)) {
      setError("Use a six-digit hex color, such as #426582.");
      return;
    }
    const validation =
      tab === "Tasks" && draft.method === "tasks"
        ? validateTasks(draft.tasks)
        : validateEditor(draft);
    if (validation) {
      setError(validation);
      return;
    }
    const clean = {
      ...draft,
      name: draft.name.trim(),
      ...(draft.method === "count" ? { unit: draft.unit.trim() } : {}),
      ...(draft.method === "tasks"
        ? {
            tasks: draft.tasks.map((task) => ({
              ...task,
              name: task.name.trim(),
            })),
          }
        : {}),
    } as HabitConfig;
    setHabits((current) =>
      current.map((habit) =>
        habit.id === selectedId
          ? {
              ...habit,
              config: structuredClone(clean),
              color: draftColor.toLowerCase(),
            }
          : habit,
      ),
    );
    setDraft(clean);
    setDraftColor(draftColor.toLowerCase());
    setError(null);
    setNotice(
      `${tab === "Tasks" ? "Tasks" : clean.name} saved in this preview. Effective September 4; earlier dates are unchanged.`,
    );
  }
  function updateTask(id: string, patch: Partial<TaskConfig["tasks"][number]>) {
    if (draft.method === "tasks")
      setDraft({
        ...draft,
        tasks: draft.tasks.map((task) =>
          task.id === id ? { ...task, ...patch } : task,
        ),
      });
    setError(null);
  }

  return (
    <section
      className="ds-section"
      id="editing"
      aria-labelledby="editing-title"
    >
      <div className="ds-section-top">
        <div>
          <span className="ds-eyebrow">EDITING & HISTORY</span>
          <h2 id="editing-title">
            Room for <em>real life.</em>
          </h2>
        </div>
        <span className="ds-demo-note">
          Independent editor demo · not saved
        </span>
      </div>
      <div className="ds-preview-toolbar">
        <div className="ds-view-switch" role="group" aria-label="Editing view">
          {(["Habit settings", "Tasks", "Backfill progress"] as const).map(
            (item) => (
              <button
                key={item}
                type="button"
                aria-pressed={tab === item}
                onClick={() => selectTab(item)}
              >
                {item}
              </button>
            ),
          )}
        </div>
        <Button
          variant="text"
          onClick={() => {
            if (
              !window.confirm(
                "Reset all editor changes and corrections? This only affects the preview.",
              )
            )
              return;
            setHabits(structuredClone(EDITOR_HABITS));
            setSelectedId("water");
            setDraft(structuredClone(EDITOR_HABITS[0]!.config));
            setDraftColor(EDITOR_HABITS[0]!.color);
            setTab("Habit settings");
            setEntries({});
            setCorrections([]);
            setDate("2026-09-03");
            setProgress(
              historicalDays("water").find((day) => day.date === "2026-09-03")!
                .value,
            );
            setTaskChecks([]);
            setError(null);
            setNotice("Editor demo reset.");
          }}
        >
          Reset editor ↺
        </Button>
      </div>
      <div className="ds-edit-layout">
        <aside className="ds-edit-context">
          <span className="ds-eyebrow">
            {tab === "Backfill progress"
              ? "A DATE, NOT A RESET"
              : "MAKE IT YOURS"}
          </span>
          <h3>
            {tab === "Habit settings"
              ? "Shape your rhythm."
              : tab === "Tasks"
                ? "Small steps, clearly defined."
                : "A missed log is not a missed day."}
          </h3>
          <p>
            {tab === "Habit settings"
              ? "Adjust a name, target, or schedule. Changes start today; your history stays as it was."
              : tab === "Tasks"
                ? "Give each task its own repeat rule. It is due only when both the habit and task schedules match."
                : "Choose a past date and enter what actually happened. That date keeps its original requirements."}
          </p>
          {tab !== "Tasks" && (
            <Field label="Habit">
              {(id) => (
                <select
                  id={id}
                  value={selectedId}
                  onChange={(event) => selectHabit(event.target.value)}
                >
                  {habits.map((habit) => (
                    <option value={habit.id} key={habit.id}>
                      {habit.config.name}
                      {habit.config.archived ? " · Archived" : ""}
                    </option>
                  ))}
                </select>
              )}
            </Field>
          )}
          <div className="ds-edit-meta">
            <span
              className="ds-habit-dot"
              style={{ background: previewColor }}
            />
            <span>
              {tab === "Backfill progress"
                ? historical.name
                : selected.config.name}
            </span>
          </div>
          <p className="ds-footnote">
            Demo clock · September 4, 2026
            <br />
            Tracking timezone · Europe/Belgrade
          </p>
        </aside>
        <div className="ds-edit-content">
          {tab === "Habit settings" && (
            <form
              onSubmit={(event) => {
                event.preventDefault();
                saveConfig();
              }}
            >
              <div className="ds-spec-heading">
                <h3>Edit habit</h3>
                <span className="ds-code">Effective today</span>
              </div>
              <Field label="Habit name">
                {(id) => (
                  <input
                    id={id}
                    required
                    maxLength={200}
                    value={draft.name}
                    onChange={(event) => {
                      setDraft({ ...draft, name: event.target.value });
                      setError(null);
                    }}
                  />
                )}
              </Field>
              <HabitColorPicker
                value={draftColor}
                onChange={(color) => {
                  setDraftColor(color);
                  setError(null);
                  setNotice("");
                }}
              />
              <Field
                label="Completion method"
                hint="Method is fixed in this preview. Choose another habit to try its editor."
              >
                {(id, describedBy) => (
                  <input
                    id={id}
                    aria-describedby={describedBy}
                    readOnly
                    value={
                      draft.method === "count"
                        ? "Count target"
                        : draft.method === "manual"
                          ? "Manual checkbox"
                          : "Task checklist"
                    }
                  />
                )}
              </Field>
              {draft.method === "count" && (
                <>
                  <div className="ds-form-grid">
                    <Field label="Daily target">
                      {(id) => (
                        <input
                          id={id}
                          type="number"
                          required
                          min={1}
                          max={10000}
                          value={Number.isNaN(draft.target) ? "" : draft.target}
                          onChange={(event) =>
                            setDraft({
                              ...draft,
                              target: event.target.valueAsNumber,
                            })
                          }
                        />
                      )}
                    </Field>
                    <Field label="Unit">
                      {(id) => (
                        <input
                          id={id}
                          required
                          maxLength={80}
                          value={draft.unit}
                          onChange={(event) =>
                            setDraft({ ...draft, unit: event.target.value })
                          }
                        />
                      )}
                    </Field>
                  </div>
                  <Checkbox
                    label="Carry unfinished counts to the next scheduled day"
                    checked={draft.carryPartialProgress}
                    onChange={(event) =>
                      setDraft({
                        ...draft,
                        carryPartialProgress: event.target.checked,
                      })
                    }
                  />
                  <p className="ds-footnote">
                    Completed counts reset. Earlier explicit logs are never
                    overwritten.
                  </p>
                </>
              )}
              <div className="ds-form-section">
                <ScheduleEditor
                  value={draft.schedule}
                  onChange={(schedule) => {
                    setDraft({ ...draft, schedule });
                    setError(null);
                  }}
                />
              </div>
              <div className="ds-form-section">
                <Checkbox
                  label="Archive this habit"
                  checked={draft.archived}
                  onChange={(event) =>
                    setDraft({ ...draft, archived: event.target.checked })
                  }
                />
                <p className="ds-footnote">
                  Stops tracking from today. History is kept. Uncheck and save
                  to restore.
                </p>
              </div>
              <SaveBar
                dirty={configDirty}
                error={error}
                onCancel={() => {
                  setDraft(structuredClone(selected.config));
                  setDraftColor(selected.color);
                  setError(null);
                }}
              />
            </form>
          )}
          {tab === "Tasks" && draft.method === "tasks" && (
            <form
              onSubmit={(event) => {
                event.preventDefault();
                saveConfig();
              }}
            >
              <div className="ds-spec-heading">
                <h3>Edit tasks</h3>
                <span className="ds-code">
                  {draft.tasks.length} / 100 tasks
                </span>
              </div>
              {draft.archived && (
                <p className="ds-form-feedback" role="status">
                  This habit is archived. Restore it in Habit settings before
                  editing tasks.
                </p>
              )}
              <fieldset
                ref={taskEditor}
                className="ds-task-editor-fieldset"
                disabled={draft.archived}
                aria-label="Task definitions"
              >
                {draft.tasks.length === 0 && (
                  <p className="ds-empty-state">
                    No tasks yet. Add a first step; a habit with no due tasks is
                    not scored.
                  </p>
                )}
                {draft.tasks.map((task, index) => (
                  <fieldset className="ds-edit-task" key={task.id}>
                    <legend className="ds-code">Step {index + 1}</legend>
                    <div className="ds-spec-heading">
                      <Button
                        variant="text"
                        aria-label={`Remove ${task.name || "unnamed task"}`}
                        onClick={() => {
                          taskFocus.current = draft.tasks[index + 1]?.id ?? draft.tasks[index - 1]?.id ?? "add";
                          setDraft({
                            ...draft,
                            tasks: draft.tasks.filter(
                              (item) => item.id !== task.id,
                            ),
                          });
                          setNotice(`${task.name || "Unnamed task"} removed from the draft. Save tasks to apply.`);
                        }}
                      >
                        Remove −
                      </Button>
                    </div>
                    <Field label="Task name">
                      {(id) => (
                        <input
                          id={id}
                          ref={(element) => { taskFields.current[task.id] = element; }}
                          required
                          maxLength={200}
                          value={task.name}
                          onChange={(event) =>
                            updateTask(task.id, { name: event.target.value })
                          }
                        />
                      )}
                    </Field>
                    <ScheduleEditor
                      value={task.schedule}
                      onChange={(schedule) => updateTask(task.id, { schedule })}
                    />
                  </fieldset>
                ))}
                <Button
                  variant="secondary"
                  data-add-task
                  disabled={draft.tasks.length >= 100}
                  onClick={() => {
                    const id = crypto.randomUUID();
                    taskFocus.current = id;
                    setDraft({
                      ...draft,
                      tasks: [
                        ...draft.tasks,
                        {
                          id,
                          name: "",
                          schedule: { type: "daily" },
                        },
                      ],
                    });
                    setNotice("Task added to the draft. Enter its name and schedule.");
                  }}
                >
                  Add task +
                </Button>
              </fieldset>
              <p className="ds-footnote">
                Removing a task takes effect when you save. Earlier checklists
                and their completion records remain available for backfilling.
              </p>
              <SaveBar
                dirty={configDirty}
                error={error}
                disabled={draft.archived}
                onCancel={() => {
                  setDraft(structuredClone(selected.config));
                  setDraftColor(selected.color);
                  setError(null);
                }}
                label="Save tasks"
              />
            </form>
          )}
          {tab === "Backfill progress" && (
            <>
              <div className="ds-spec-heading">
                <h3>Correct a past day</h3>
                <span className="ds-code">Historical snapshot</span>
              </div>
              <Field
                label="Progress date"
                hint="Select a date here or in the calendar below."
              >
                {(id, describedBy) => (
                  <input
                    id={id}
                    aria-describedby={describedBy}
                    type="date"
                    required
                    min={EDITOR_START}
                    max="2026-09-03"
                    value={date}
                    onInput={(event) => selectDate(event.currentTarget.value)}
                  />
                )}
              </Field>
              <form
                onSubmit={(event) => {
                  event.preventDefault();
                  if (blocked) {
                    setError(blocked);
                    return;
                  }
                  const correction = {
                    habitId: selectedId,
                    name: historical.name,
                    date,
                    before: savedProgress,
                    after: progress,
                    target: day!.target,
                    unit:
                      historical.method === "count"
                        ? historical.unit
                        : historical.method === "tasks"
                          ? "tasks"
                          : "session",
                  };
                  setEntries((current) => ({
                    ...current,
                    [entryKey]: { value: progress, tasks: [...taskChecks] },
                  }));
                  setCorrections((current) => [correction, ...current]);
                  setError(null);
                  setNotice(
                    `Correction saved in this preview for ${date}. The calendar below is updated.`,
                  );
                }}
              >
                {blocked && (
                  <p className="ds-form-feedback" role="status">
                    {blocked}
                  </p>
                )}
                {day?.state === "due" && date < EDITOR_TODAY && (
                  <>
                    <div className="ds-history-requirement">
                      <span>Required on {date}</span>
                      <strong>
                        {day.target}{" "}
                        {historical.method === "count"
                          ? historical.unit
                          : historical.method === "tasks"
                            ? "tasks"
                            : "session"}
                      </strong>
                    </div>
                    {historical.method === "count" ? (
                      <Field
                        label="Actual count"
                        hint="Enter the total, not an increment. Zero clears progress; counts may exceed the target."
                      >
                        {(id, describedBy) => (
                          <input
                            id={id}
                            aria-describedby={describedBy}
                            type="number"
                            required
                            min={0}
                            max={1_000_000_000}
                            step={1}
                            value={Number.isNaN(progress) ? "" : progress}
                            onChange={(event) => {
                              setProgress(event.target.valueAsNumber);
                              setError(null);
                            }}
                          />
                        )}
                      </Field>
                    ) : historical.method === "manual" ? (
                      <Checkbox
                        label="Completed on this date"
                        checked={progress === 1}
                        onChange={(event) =>
                          setProgress(Number(event.target.checked))
                        }
                      />
                    ) : (
                      <div className="ds-backfill-tasks">
                        {historical.tasks.map((task, index) => (
                          <Checkbox
                            key={task.id}
                            label={task.name}
                            checked={taskChecks[index] ?? false}
                            onChange={(event) => {
                              const next = taskChecks.map((done, i) =>
                                i === index ? event.target.checked : done,
                              );
                              setTaskChecks(next);
                              setProgress(next.filter(Boolean).length);
                            }}
                          />
                        ))}
                      </div>
                    )}
                    <p className="ds-correction-summary">
                      {savedProgress} →{" "}
                      {Number.isFinite(progress) ? progress : "—"} /{" "}
                      {day.target}
                      <span>
                        {progress >= day.target ? "Complete" : "Incomplete"}
                      </span>
                    </p>
                  </>
                )}
                <SaveBar
                  dirty={progressDirty}
                  error={error}
                  disabled={!!blocked}
                  label="Save correction"
                  onCancel={() => loadProgress(selectedId, date)}
                />
              </form>
              <div className="ds-backfill-calendar">
                <CalendarHeatmap
                  key={selectedId}
                  compact
                  color={selected.color}
                  days={historicalDays(selectedId).map((item) => ({
                    ...item,
                    value:
                      entries[`${selectedId}:${item.date}`]?.value ??
                      item.value,
                  }))}
                  unit={
                    historical.method === "count"
                      ? historical.unit
                      : historical.method === "tasks"
                        ? "tasks"
                        : "session"
                  }
                  label="Backfill history"
                  selectedDate={date}
                  onSelectDate={selectDate}
                />
              </div>
              <p className="ds-footnote">
                This preview updates only the calendar here. In the connected
                app, corrections also recalculate combined charts and inherited
                counts.
              </p>
            </>
          )}
          <p className="ds-form-feedback ds-save-notice" role="status">
            {notice}
          </p>
        </div>
      </div>
      {corrections.length > 0 && (
        <div className="ds-correction-log">
          <div className="ds-spec-heading">
            <h3>Correction history</h3>
            <span className="ds-code">This preview only · newest first</span>
          </div>
          <ol>
            {corrections.map((item, index) => (
              <li key={corrections.length - index}>
                <time dateTime={item.date}>{item.date}</time>
                <span>{item.name}</span>
                <span>
                  {item.before} → {item.after} / {item.target} {item.unit}
                </span>
              </li>
            ))}
          </ol>
        </div>
      )}
    </section>
  );
}
