import { useCallback, useEffect, useMemo, useState } from "react";
import {
  ActionIcon,
  Alert,
  Badge,
  Box,
  Button,
  Center,
  ColorSwatch,
  Group,
  Indicator,
  Loader,
  Paper,
  ScrollArea,
  SegmentedControl,
  Select,
  Stack,
  Text,
  Title,
  Tooltip,
} from "@mantine/core";
import { Calendar } from "@mantine/dates";
import { notifications } from "@mantine/notifications";
import {
  IconChevronLeft,
  IconChevronRight,
  IconPlus,
} from "@tabler/icons-react";
import {
  deleteEvent,
  fetchCalendar,
  fetchCalendars,
  type CalendarCollection,
  type CalendarEvent,
} from "../api";
import {
  dayjs,
  eventColor,
  eventStart,
  HOURS,
  monthMatrix,
  positionEventsForDay,
  TIMEZONES,
  WEEKDAY_LABELS,
  weekDays,
  browserTz,
} from "../calendar";
import { EventModal, type EventDraft } from "./EventModal";

type ViewMode = "mes" | "semana" | "dia";

// Calendario: vista Mes/Semana/Día con eventos posicionados por hora, selector
// de calendario (con color), selector de zona horaria, colores por evento y CRUD
// completo (crear en un hueco, editar/borrar un evento). El mini-calendario de la
// izquierda navega; el cuerpo muestra la vista activa en la TZ seleccionada.

export function CalendarView() {
  const [events, setEvents] = useState<CalendarEvent[]>([]);
  const [calendars, setCalendars] = useState<CalendarCollection[]>([]);
  const [activeCal, setActiveCal] = useState<string>("");
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [view, setView] = useState<ViewMode>("semana");
  const [tz, setTz] = useState<string>(browserTz());
  // `cursor` es el día de referencia (la vista lo expande a su mes/semana/día).
  const [cursor, setCursor] = useState<string>(dayjs().format("YYYY-MM-DD"));
  const [modal, setModal] = useState<EventDraft | null>(null);
  const [reloadKey, setReloadKey] = useState(0);

  // Carga de calendarios (selector) una vez.
  useEffect(() => {
    let alive = true;
    fetchCalendars()
      .then((d) => {
        if (!alive) return;
        if (d.status === "ok" && d.calendars) {
          setCalendars(d.calendars);
          if (!activeCal && d.default) setActiveCal(d.default);
          else if (!activeCal && d.calendars[0]) setActiveCal(d.calendars[0].href);
        }
      })
      .catch(() => {
        /* el selector degrada a "calendario" implícito; no es fatal */
      });
    return () => {
      alive = false;
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // Carga de eventos de la colección activa.
  useEffect(() => {
    let alive = true;
    setLoading(true);
    setError(null);
    fetchCalendar(activeCal)
      .then((d) => {
        if (!alive) return;
        if (d.status !== "ok") {
          setError(d.error || "Xandikos no respondió");
          return;
        }
        setEvents(d.events ?? []);
      })
      .catch((e) => alive && setError(String(e)))
      .finally(() => alive && setLoading(false));
    return () => {
      alive = false;
    };
  }, [activeCal, reloadKey]);

  const calColor = useMemo(() => {
    const c = calendars.find((c) => c.href === activeCal);
    return c?.color ?? null;
  }, [calendars, activeCal]);

  // Días con eventos (para el punto en el mini-calendario), en la TZ activa.
  const daysWithEvents = useMemo(() => {
    const set = new Set<string>();
    for (const e of events) {
      const s = eventStart(e, tz);
      if (s) set.add(s.format("YYYY-MM-DD"));
    }
    return set;
  }, [events, tz]);

  const reload = useCallback(() => setReloadKey((k) => k + 1), []);

  const onSaved = useCallback(
    (msg: string) => {
      notifications.show({ color: "teal", title: "Calendario", message: msg });
      setModal(null);
      reload();
    },
    [reload],
  );

  const onDelete = useCallback(
    async (uid: string) => {
      try {
        await deleteEvent(uid, activeCal);
        notifications.show({
          color: "teal",
          title: "Evento borrado",
          message: "",
        });
        setModal(null);
        reload();
      } catch (e) {
        notifications.show({
          color: "red",
          title: "No se pudo borrar",
          message: String(e),
        });
      }
    },
    [activeCal, reload],
  );

  // Abre el modal en modo "nuevo" en un hueco (día + hora opcional).
  const openNew = useCallback(
    (day: string, hour?: number) => {
      const start =
        hour === undefined
          ? `${day}T09:00`
          : `${day}T${String(hour).padStart(2, "0")}:00`;
      const endHour = hour === undefined ? 10 : Math.min(hour + 1, 23);
      const end =
        hour === undefined
          ? `${day}T10:00`
          : `${day}T${String(endHour).padStart(2, "0")}:00`;
      setModal({
        mode: "new",
        summary: "",
        dtstart: start,
        dtend: end,
        tz,
        all_day: false,
        location: "",
        description: "",
        color: "",
        cal: activeCal,
      });
    },
    [tz, activeCal],
  );

  // Abre el modal en modo "editar" desde un evento existente.
  const openEdit = useCallback(
    (ev: CalendarEvent) => {
      const s = eventStart(ev, tz);
      const e = ev.dtend ? eventStart({ ...ev, dtstart: ev.dtend }, tz) : null;
      setModal({
        mode: "edit",
        uid: ev.uid ?? undefined,
        summary: ev.summary ?? "",
        dtstart: ev.all_day
          ? (ev.dtstart ?? "").slice(0, 10)
          : (s?.format("YYYY-MM-DDTHH:mm") ?? ""),
        dtend: ev.all_day
          ? (ev.dtend ?? "").slice(0, 10)
          : (e?.format("YYYY-MM-DDTHH:mm") ?? ""),
        tz: ev.tz || tz,
        all_day: !!ev.all_day,
        location: ev.location ?? "",
        description: ev.description ?? "",
        color: ev.color ?? "",
        cal: activeCal,
      });
    },
    [tz, activeCal],
  );

  function navigate(dir: -1 | 1) {
    const unit = view === "mes" ? "month" : view === "semana" ? "week" : "day";
    setCursor(dayjs(cursor).add(dir, unit).format("YYYY-MM-DD"));
  }

  if (error) {
    return (
      <Center h="100%" p="xl">
        <Alert color="orange" title="Calendario no disponible" maw={500}>
          {error}
          <Text size="sm" mt="xs" c="dimmed">
            El calendario viene del servidor Xandikos. El resto de la app (grafo,
            tablas) funciona sin él.
          </Text>
        </Alert>
      </Center>
    );
  }

  const headerTitle =
    view === "mes"
      ? dayjs(cursor).format("MMMM YYYY")
      : view === "semana"
        ? (() => {
            const wd = weekDays(dayjs(cursor));
            return `${wd[0].format("D MMM")} – ${wd[6].format("D MMM YYYY")}`;
          })()
        : dayjs(cursor).format("dddd D [de] MMMM YYYY");

  return (
    <Group h="100%" gap={0} wrap="nowrap" align="stretch">
      {/* Panel izquierdo: navegación + selectores */}
      <Paper
        p="md"
        radius={0}
        withBorder
        style={{ borderTop: 0, borderBottom: 0, borderLeft: 0, width: 280 }}
      >
        <Stack gap="md">
          <Button
            leftSection={<IconPlus size={16} />}
            onClick={() => openNew(cursor)}
            fullWidth
          >
            Nuevo evento
          </Button>

          <Calendar
            date={cursor}
            onDateChange={(d) => setCursor(d)}
            getDayProps={(date) => ({
              selected: cursor === date,
              onClick: () => {
                setCursor(date);
                if (view === "mes") setView("dia");
              },
            })}
            renderDay={(date) => {
              const has = daysWithEvents.has(date);
              const day = Number(date.slice(8, 10));
              return (
                <Indicator size={6} color="brand" offset={-2} disabled={!has}>
                  <div>{day}</div>
                </Indicator>
              );
            }}
          />

          <Select
            label="Calendario"
            data={calendars.map((c) => ({ value: c.href, label: c.name }))}
            value={activeCal || null}
            onChange={(v) => v && setActiveCal(v)}
            leftSection={
              <ColorSwatch
                size={14}
                color={eventColor({} as CalendarEvent, calColor)}
              />
            }
            allowDeselect={false}
            comboboxProps={{ withinPortal: true }}
          />

          <Select
            label="Zona horaria"
            data={TIMEZONES}
            value={tz}
            onChange={(v) => v && setTz(v)}
            searchable
            comboboxProps={{ withinPortal: true }}
          />

          <Text size="xs" c="dimmed">
            {events.length} eventos · TZ {tz}
          </Text>
        </Stack>
      </Paper>

      {/* Cuerpo: barra de vista + rejilla */}
      <Box style={{ flex: 1, minWidth: 0, display: "flex", flexDirection: "column" }}>
        <Group justify="space-between" p="sm" wrap="nowrap" style={{ flexShrink: 0 }}>
          <Group gap="xs" wrap="nowrap">
            <Tooltip label="Anterior">
              <ActionIcon variant="default" onClick={() => navigate(-1)}>
                <IconChevronLeft size={16} />
              </ActionIcon>
            </Tooltip>
            <Button
              variant="default"
              size="xs"
              onClick={() => setCursor(dayjs().format("YYYY-MM-DD"))}
            >
              Hoy
            </Button>
            <Tooltip label="Siguiente">
              <ActionIcon variant="default" onClick={() => navigate(1)}>
                <IconChevronRight size={16} />
              </ActionIcon>
            </Tooltip>
            <Title order={4} style={{ textTransform: "capitalize" }}>
              {headerTitle}
            </Title>
          </Group>
          <SegmentedControl
            value={view}
            onChange={(v) => setView(v as ViewMode)}
            data={[
              { value: "mes", label: "Mes" },
              { value: "semana", label: "Semana" },
              { value: "dia", label: "Día" },
            ]}
          />
        </Group>

        <Box style={{ flex: 1, minHeight: 0 }}>
          {loading ? (
            <Center h="100%">
              <Loader />
            </Center>
          ) : view === "mes" ? (
            <MonthView
              cursor={cursor}
              events={events}
              tz={tz}
              calColor={calColor}
              onDay={(d) => {
                setCursor(d);
                setView("dia");
              }}
              onEvent={openEdit}
            />
          ) : view === "semana" ? (
            <TimeGrid
              days={weekDays(dayjs(cursor))}
              events={events}
              tz={tz}
              calColor={calColor}
              onSlot={openNew}
              onEvent={openEdit}
            />
          ) : (
            <TimeGrid
              days={[dayjs(cursor)]}
              events={events}
              tz={tz}
              calColor={calColor}
              onSlot={openNew}
              onEvent={openEdit}
            />
          )}
        </Box>
      </Box>

      <EventModal
        draft={modal}
        calendars={calendars}
        onClose={() => setModal(null)}
        onSaved={onSaved}
        onDelete={onDelete}
      />
    </Group>
  );
}

// --- Vista de semana / día: rejilla horas × días ---------------------------

function TimeGrid({
  days,
  events,
  tz,
  calColor,
  onSlot,
  onEvent,
}: {
  days: dayjs.Dayjs[];
  events: CalendarEvent[];
  tz: string;
  calColor: string | null;
  onSlot: (day: string, hour: number) => void;
  onEvent: (ev: CalendarEvent) => void;
}) {
  const HOUR_PX = 44;
  const today = dayjs().format("YYYY-MM-DD");
  return (
    <ScrollArea h="100%" type="auto">
      <Box style={{ display: "flex", minWidth: days.length > 1 ? 640 : 0 }}>
        {/* Columna de horas */}
        <Box style={{ width: 52, flexShrink: 0 }}>
          <Box style={{ height: 28 }} />
          {HOURS.map((h) => (
            <Box
              key={h}
              style={{
                height: HOUR_PX,
                textAlign: "right",
                paddingRight: 6,
                fontSize: 11,
                color: "var(--mantine-color-dimmed)",
                transform: "translateY(-7px)",
              }}
            >
              {String(h).padStart(2, "0")}:00
            </Box>
          ))}
        </Box>

        {/* Una columna por día */}
        {days.map((day) => {
          const dayKey = day.format("YYYY-MM-DD");
          const positioned = positionEventsForDay(events, dayKey, tz);
          const allDay = positioned.filter((p) => p.allDay);
          const timed = positioned.filter((p) => !p.allDay);
          const isToday = dayKey === today;
          return (
            <Box
              key={dayKey}
              style={{
                flex: 1,
                minWidth: 0,
                borderLeft: "1px solid var(--mantine-color-default-border)",
              }}
            >
              {/* Cabecera del día */}
              <Box
                style={{
                  height: 28,
                  textAlign: "center",
                  fontSize: 12,
                  fontWeight: 600,
                  color: isToday
                    ? "var(--mantine-color-brand-6)"
                    : "var(--mantine-color-text)",
                }}
              >
                {WEEKDAY_LABELS[(day.day() + 6) % 7]} {day.format("D")}
              </Box>

              {/* Franja de "todo el día" */}
              {allDay.length > 0 && (
                <Box style={{ padding: "2px 3px" }}>
                  {allDay.map((p, i) => (
                    <EventChip
                      key={(p.ev.uid ?? "") + i}
                      ev={p.ev}
                      calColor={calColor}
                      onClick={() => onEvent(p.ev)}
                      compact
                    />
                  ))}
                </Box>
              )}

              {/* Rejilla horaria con posicionado absoluto */}
              <Box style={{ position: "relative", height: HOUR_PX * 24 }}>
                {HOURS.map((h) => (
                  <Box
                    key={h}
                    onClick={() => onSlot(dayKey, h)}
                    style={{
                      position: "absolute",
                      top: h * HOUR_PX,
                      left: 0,
                      right: 0,
                      height: HOUR_PX,
                      borderTop: "1px solid var(--mantine-color-default-border)",
                      cursor: "pointer",
                    }}
                  />
                ))}
                {timed.map((p, i) => (
                  <Box
                    key={(p.ev.uid ?? "") + i}
                    onClick={(e) => {
                      e.stopPropagation();
                      onEvent(p.ev);
                    }}
                    style={{
                      position: "absolute",
                      top: `${p.topPct}%`,
                      height: `${p.heightPct}%`,
                      left: 2,
                      right: 2,
                      overflow: "hidden",
                      borderRadius: 4,
                      padding: "1px 5px",
                      fontSize: 11,
                      lineHeight: 1.25,
                      color: "#fff",
                      background: eventColor(p.ev, calColor),
                      cursor: "pointer",
                    }}
                  >
                    <Text size="xs" fw={600} truncate c="#fff">
                      {eventStart(p.ev, tz)?.format("HH:mm")} ·{" "}
                      {p.ev.summary || "(sin título)"}
                    </Text>
                  </Box>
                ))}
              </Box>
            </Box>
          );
        })}
      </Box>
    </ScrollArea>
  );
}

// --- Vista de mes: rejilla de semanas --------------------------------------

function MonthView({
  cursor,
  events,
  tz,
  calColor,
  onDay,
  onEvent,
}: {
  cursor: string;
  events: CalendarEvent[];
  tz: string;
  calColor: string | null;
  onDay: (day: string) => void;
  onEvent: (ev: CalendarEvent) => void;
}) {
  const weeks = monthMatrix(dayjs(cursor));
  const month = dayjs(cursor).month();
  const today = dayjs().format("YYYY-MM-DD");

  const byDay = useMemo(() => {
    const map = new Map<string, CalendarEvent[]>();
    for (const e of events) {
      const s = eventStart(e, tz);
      if (!s) continue;
      const k = s.format("YYYY-MM-DD");
      const list = map.get(k) ?? [];
      list.push(e);
      map.set(k, list);
    }
    return map;
  }, [events, tz]);

  return (
    <ScrollArea h="100%">
      <Box style={{ display: "flex", padding: "0 8px" }}>
        {WEEKDAY_LABELS.map((l) => (
          <Box
            key={l}
            style={{
              flex: 1,
              textAlign: "center",
              fontSize: 11,
              fontWeight: 600,
              color: "var(--mantine-color-dimmed)",
              padding: "4px 0",
            }}
          >
            {l}
          </Box>
        ))}
      </Box>
      <Stack gap={0} px={8} pb={8}>
        {weeks.map((week, wi) => (
          <Group key={wi} gap={0} wrap="nowrap" align="stretch">
            {week.map((day) => {
              const k = day.format("YYYY-MM-DD");
              const list = (byDay.get(k) ?? []).sort((a, b) =>
                (a.dtstart ?? "").localeCompare(b.dtstart ?? ""),
              );
              const dim = day.month() !== month;
              const isToday = k === today;
              return (
                <Box
                  key={k}
                  onClick={() => onDay(k)}
                  style={{
                    flex: 1,
                    minWidth: 0,
                    minHeight: 92,
                    border: "1px solid var(--mantine-color-default-border)",
                    padding: 3,
                    cursor: "pointer",
                    background: dim
                      ? "var(--mantine-color-default-hover)"
                      : undefined,
                  }}
                >
                  <Text
                    size="xs"
                    ta="right"
                    fw={isToday ? 700 : 400}
                    c={
                      isToday
                        ? "brand"
                        : dim
                          ? "dimmed"
                          : undefined
                    }
                  >
                    {day.format("D")}
                  </Text>
                  <Stack gap={2} mt={2}>
                    {list.slice(0, 3).map((ev, i) => (
                      <EventChip
                        key={(ev.uid ?? "") + i}
                        ev={ev}
                        calColor={calColor}
                        onClick={() => onEvent(ev)}
                        compact
                      />
                    ))}
                    {list.length > 3 && (
                      <Text size="xs" c="dimmed">
                        +{list.length - 3} más
                      </Text>
                    )}
                  </Stack>
                </Box>
              );
            })}
          </Group>
        ))}
      </Stack>
    </ScrollArea>
  );
}

// Chip compacto de un evento (mes / franja all-day).
function EventChip({
  ev,
  calColor,
  onClick,
  compact,
}: {
  ev: CalendarEvent;
  calColor: string | null;
  onClick: () => void;
  compact?: boolean;
}) {
  const color = eventColor(ev, calColor);
  return (
    <Badge
      onClick={(e) => {
        e.stopPropagation();
        onClick();
      }}
      variant="filled"
      radius="sm"
      fullWidth
      style={{
        background: color,
        cursor: "pointer",
        justifyContent: "flex-start",
        textTransform: "none",
        fontWeight: 500,
        height: compact ? 16 : 20,
      }}
    >
      {ev.summary || "(sin título)"}
    </Badge>
  );
}
