import {
  addDays,
  addMonths,
  addWeeks,
  format,
  isSameMonth,
  startOfDay,
  startOfWeek,
  subDays,
  subMonths,
  subWeeks
} from "date-fns";
import { ChevronLeft, ChevronRight, Plus } from "lucide-solid";
import { createMemo, createSignal, For, Show } from "solid-js";

import Modal from "~/components/modal";
import { useApp } from "~/lib/app-context";
import { colorOptions, daysForMonth, formatDayLabel, formatPrettyDate, getColorStyle, sameDay } from "~/lib/utils";

type CalendarView = "month" | "week" | "day";

export default function CalendarRoute() {
  const app = useApp();
  const workspaceSlug = () => app.primaryWorkspace()?.slug ?? "personal";
  const [view, setView] = createSignal<CalendarView>("month");
  const [cursorDate, setCursorDate] = createSignal(startOfDay(new Date()));
  const [selectedDate, setSelectedDate] = createSignal(startOfDay(new Date()));
  const [showQuickAdd, setShowQuickAdd] = createSignal(false);
  const [moreDate, setMoreDate] = createSignal<Date | null>(null);
  const [quickType, setQuickType] = createSignal<"task" | "event">("task");
  const [quickColor, setQuickColor] = createSignal("blue");
  const [title, setTitle] = createSignal("");
  const [description, setDescription] = createSignal("");

  const scheduledTasks = createMemo(() =>
    app.state.tasks.filter(task => task.workspaceSlug === workspaceSlug() && task.scheduledStart),
  );
  const events = createMemo(() => app.state.events.filter(event => event.workspaceSlug === workspaceSlug()));

  const dayItems = (day: Date) => {
    const tasks = scheduledTasks().filter(task => sameDay(task.scheduledStart, day)).map(task => ({
      kind: "task" as const,
      id: task.id,
      title: task.title,
      startsAt: task.scheduledStart!,
      color: task.color
    }));
    const calendarEvents = events().filter(event => sameDay(event.startsAt, day)).map(event => ({
      kind: "event" as const,
      id: event.id,
      title: event.title,
      startsAt: event.startsAt,
      color: event.color
    }));
    return [...tasks, ...calendarEvents].sort((left, right) => left.startsAt.localeCompare(right.startsAt));
  };

  const visibleDays = createMemo(() => {
    if (view() === "month") {
      return daysForMonth(cursorDate());
    }
    if (view() === "week") {
      const weekStart = startOfWeek(cursorDate(), { weekStartsOn: 1 });
      return Array.from({ length: 7 }, (_, index) => addDays(weekStart, index));
    }
    return [startOfDay(selectedDate())];
  });

  const rangeLabel = createMemo(() => {
    if (view() === "month") {
      return format(cursorDate(), "MMMM yyyy");
    }
    if (view() === "week") {
      const days = visibleDays();
      const start = days[0];
      const end = days[days.length - 1];
      return `${format(start, "MMM d")} - ${format(end, "MMM d, yyyy")}`;
    }
    return format(selectedDate(), "EEEE, MMM d, yyyy");
  });

  const changeRange = (direction: "next" | "prev") => {
    if (view() === "month") {
      setCursorDate(direction === "next" ? addMonths(cursorDate(), 1) : subMonths(cursorDate(), 1));
      return;
    }

    if (view() === "week") {
      setCursorDate(direction === "next" ? addWeeks(cursorDate(), 1) : subWeeks(cursorDate(), 1));
      return;
    }

    const nextDay = direction === "next" ? addDays(selectedDate(), 1) : subDays(selectedDate(), 1);
    setSelectedDate(startOfDay(nextDay));
    setCursorDate(startOfDay(nextDay));
  };

  const submitQuickAdd = () => {
    if (!title().trim()) {
      return;
    }

    const day = selectedDate();
    if (quickType() === "task") {
      const start = new Date(day);
      start.setHours(10, 0, 0, 0);
      const end = new Date(day);
      end.setHours(11, 0, 0, 0);
      app.createTask({
        workspaceSlug: workspaceSlug(),
        boardGroupId: "group-inbox",
        title: title(),
        description: description(),
        color: quickColor(),
        scheduledStart: start.toISOString(),
        scheduledEnd: end.toISOString()
      });
    } else {
      const start = new Date(day);
      start.setHours(13, 0, 0, 0);
      const end = new Date(day);
      end.setHours(14, 0, 0, 0);
      app.createEvent({
        workspaceSlug: workspaceSlug(),
        title: title(),
        description: description(),
        startsAt: start.toISOString(),
        endsAt: end.toISOString(),
        color: quickColor()
      });
    }

    setTitle("");
    setDescription("");
    setShowQuickAdd(false);
  };

  const openQuickAddForDay = (day: Date) => {
    setSelectedDate(startOfDay(day));
    setShowQuickAdd(true);
  };

  const handleDrop = (day: Date, payload: string) => {
    try {
      const parsed = JSON.parse(payload) as { kind: "task" | "event"; id: string };
      if (parsed.kind === "task") {
        app.scheduleTaskOnDay(parsed.id, new Date(day));
      } else {
        app.moveEventToDay(parsed.id, new Date(day));
      }
    } catch {
      return;
    }
  };

  return (
    <div class="space-y-6">
      {/* Page header */}
      <div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
        <div>
          <h1 class="text-xl font-semibold">Calendar</h1>
          <p class="mt-1 text-sm text-[var(--text-muted)]">Time-block the work</p>
        </div>
        <button
          class="button-primary inline-flex items-center gap-2 px-4 py-2 text-sm"
          onClick={() => setShowQuickAdd(true)}
        >
          <Plus size={16} />
          Quick add
        </button>
      </div>

      {/* Calendar controls */}
      <div class="rounded-lg border border-[var(--border)] bg-[var(--surface)] p-4">
        <div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
          {/* View switcher */}
          <div class="flex rounded-lg bg-[var(--bg-subtle)] p-1">
            <For each={["month", "week", "day"] as CalendarView[]}>
              {item => (
                <button
                  class="rounded-md px-3 py-1.5 text-sm font-medium transition-colors"
                  classList={{
                    "bg-[var(--surface)] text-[var(--text)] shadow-sm": view() === item,
                    "text-[var(--text-muted)] hover:text-[var(--text)]": view() !== item
                  }}
                  onClick={() => {
                    setView(item);
                    if (item === "day") {
                      setSelectedDate(startOfDay(cursorDate()));
                    }
                  }}
                >
                  {item}
                </button>
              )}
            </For>
          </div>

          {/* Range navigation */}
          <div class="flex items-center gap-3">
            <button
              class="flex h-8 w-8 items-center justify-center rounded-md text-[var(--text-muted)] transition-colors hover:bg-[var(--bg-subtle)] hover:text-[var(--text)]"
              onClick={() => changeRange("prev")}
              aria-label="Previous period"
            >
              <ChevronLeft size={18} />
            </button>
            <span class="min-w-[140px] text-center text-sm font-medium">{rangeLabel()}</span>
            <button
              class="flex h-8 w-8 items-center justify-center rounded-md text-[var(--text-muted)] transition-colors hover:bg-[var(--bg-subtle)] hover:text-[var(--text)]"
              onClick={() => changeRange("next")}
              aria-label="Next period"
            >
              <ChevronRight size={18} />
            </button>
          </div>
        </div>
      </div>

      {/* Calendar grid */}
      <div class="rounded-lg border border-[var(--border)] bg-[var(--surface)]">
        {/* Day headers */}
        <Show when={view() !== "day"}>
          <div class="grid grid-cols-7 border-b border-[var(--border)]">
            <For each={["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]}>
              {day => (
                <div class="px-2 py-2 text-center text-xs font-medium text-[var(--text-muted)]">
                  {day}
                </div>
              )}
            </For>
          </div>
        </Show>

        {/* Days grid */}
        <div class={`grid ${view() === "day" ? "grid-cols-1" : "grid-cols-7"}`}>
          <For each={visibleDays()}>
            {day => {
              const items = () => dayItems(day);
              const isSelected = () => format(day, "yyyy-MM-dd") === format(selectedDate(), "yyyy-MM-dd");
              const isCurrentMonth = () => view() === "month" ? isSameMonth(day, cursorDate()) : true;

              return (
                <div
                  class={`min-h-[120px] border-r border-b border-[var(--border)] p-2 transition-colors last:border-r-0 ${
                    view() === "day" ? "min-h-[400px]" : ""
                  } ${isSelected() ? "bg-[var(--accent-subtle)]" : "hover:bg-[var(--bg-subtle)]"}`}
                  onDragOver={event => event.preventDefault()}
                  onDrop={event => handleDrop(day, event.dataTransfer?.getData("text/plain") ?? "")}
                >
                  {/* Day header */}
                  <div class="mb-2 flex items-center justify-between">
                    <button
                      class="flex items-center gap-1 text-left"
                      onClick={() => setSelectedDate(day)}
                    >
                      <span class={`text-sm font-medium ${isCurrentMonth() ? "" : "text-[var(--text-muted)]"}`}>
                        {formatDayLabel(day)}
                      </span>
                      <Show when={items().length > 0}>
                        <span class="rounded-full bg-[var(--accent-subtle)] px-1.5 py-0.5 text-xs font-medium text-[var(--accent)]">
                          {items().length}
                        </span>
                      </Show>
                    </button>
                    <button
                      class="flex h-6 w-6 items-center justify-center rounded text-[var(--text-muted)] opacity-0 transition-opacity hover:bg-[var(--bg-muted)] hover:text-[var(--text)] group-hover:opacity-100"
                      onClick={() => openQuickAddForDay(day)}
                      aria-label="Add item"
                    >
                      <Plus size={14} />
                    </button>
                  </div>

                  {/* Items */}
                  <div class="space-y-1">
                    <For each={items().slice(0, view() === "day" ? 20 : 3)}>
                      {item => {
                        const color = getColorStyle(item.color);
                        return (
                          <div
                            draggable
                            onDragStart={event =>
                              event.dataTransfer?.setData("text/plain", JSON.stringify({ kind: item.kind, id: item.id }))
                            }
                            class="truncate rounded px-2 py-1 text-xs font-medium cursor-grab"
                            style={{ background: color.bg, color: color.text }}
                          >
                            {item.title}
                          </div>
                        );
                      }}
                    </For>
                    <Show when={items().length > (view() === "day" ? 20 : 3)}>
                      <button
                        class="w-full rounded px-2 py-1 text-left text-xs text-[var(--text-muted)] hover:bg-[var(--bg-subtle)]"
                        onClick={() => setMoreDate(day)}
                      >
                        +{items().length - (view() === "day" ? 20 : 3)} more
                      </button>
                    </Show>
                  </div>
                </div>
              );
            }}
          </For>
        </div>
      </div>

      {/* Selected day detail */}
      <div class="rounded-lg border border-[var(--border)] bg-[var(--surface)]">
        <div class="border-b border-[var(--border)] px-4 py-3">
          <h2 class="text-sm font-medium">{format(selectedDate(), "EEEE, MMMM d, yyyy")}</h2>
        </div>
        <div class="divide-y divide-[var(--border)]">
          <Show when={dayItems(selectedDate()).length === 0}>
            <div class="px-4 py-6 text-center text-sm text-[var(--text-muted)]">
              No items scheduled
            </div>
          </Show>
          <For each={dayItems(selectedDate())}>
            {item => {
              const color = getColorStyle(item.color);
              return (
                <div class="flex items-center gap-3 px-4 py-3">
                  <span
                    class="shrink-0 rounded px-2 py-0.5 text-xs font-medium"
                    style={{ background: color.bg, color: color.text }}
                  >
                    {item.kind}
                  </span>
                  <div class="min-w-0 flex-1">
                    <p class="truncate text-sm font-medium">{item.title}</p>
                    <p class="text-xs text-[var(--text-muted)]">{formatPrettyDate(item.startsAt)}</p>
                  </div>
                </div>
              );
            }}
          </For>
        </div>
      </div>

      {/* Quick add modal */}
      <Modal open={showQuickAdd()} title="Quick add" onClose={() => setShowQuickAdd(false)}>
        <div class="space-y-4">
          <p class="text-sm text-[var(--text-muted)]">{format(selectedDate(), "EEEE, MMM d, yyyy")}</p>

          {/* Type toggle */}
          <div class="flex rounded-lg bg-[var(--bg-subtle)] p-1">
            <For each={["task", "event"] as const}>
              {item => (
                <button
                  class="flex-1 rounded-md px-3 py-2 text-sm font-medium transition-colors"
                  classList={{
                    "bg-[var(--surface)] text-[var(--text)] shadow-sm": quickType() === item,
                    "text-[var(--text-muted)] hover:text-[var(--text)]": quickType() !== item
                  }}
                  onClick={() => {
                    setQuickType(item);
                    setQuickColor(item === "task" ? "blue" : "amber");
                  }}
                >
                  {item}
                </button>
              )}
            </For>
          </div>

          <select
            class="input-base"
            value={quickColor()}
            onInput={event => setQuickColor(event.currentTarget.value)}
          >
            <For each={colorOptions}>{option => <option value={option}>{option}</option>}</For>
          </select>

          <input
            class="input-base"
            placeholder="Title"
            value={title()}
            onInput={event => setTitle(event.currentTarget.value)}
          />

          <textarea
            class="input-base min-h-24 resize-none"
            placeholder="Description (optional)"
            value={description()}
            onInput={event => setDescription(event.currentTarget.value)}
          />

          <button class="button-primary w-full px-4 py-2 text-sm" onClick={submitQuickAdd}>
            Save item
          </button>
        </div>
      </Modal>

      {/* Day preview modal */}
      <Modal open={Boolean(moreDate())} title={moreDate() ? format(moreDate()!, "EEEE, MMM d") : "Day preview"} onClose={() => setMoreDate(null)}>
        <div class="divide-y divide-[var(--border)]">
          <For each={moreDate() ? dayItems(moreDate()!) : []}>
            {item => {
              const color = getColorStyle(item.color);
              return (
                <div class="flex items-center gap-3 py-3">
                  <span
                    class="shrink-0 rounded px-2 py-0.5 text-xs font-medium"
                    style={{ background: color.bg, color: color.text }}
                  >
                    {item.kind}
                  </span>
                  <div class="min-w-0 flex-1">
                    <p class="truncate text-sm font-medium">{item.title}</p>
                    <p class="text-xs text-[var(--text-muted)]">{formatPrettyDate(item.startsAt)}</p>
                  </div>
                </div>
              );
            }}
          </For>
        </div>
      </Modal>
    </div>
  );
}
