/**
 * Calendar view layout: header, grid, duty list, day detail.
 * Composes calendar UI and owns sticky scroll, swipe, month data, and day-detail ref.
 */

"use client";

import { useRef, useState, useEffect, useCallback } from "react";
import { useAppStore } from "@/store/app-store";
import { useShallow } from "zustand/react/shallow";
import { useMonthData } from "@/hooks/use-month-data";
import { useSwipe } from "@/hooks/use-swipe";
import { useStickyScroll } from "@/hooks/use-sticky-scroll";
import { useAutoRefresh } from "@/hooks/use-auto-refresh";
import { CalendarHeader } from "@/components/calendar/CalendarHeader";
import { CalendarGrid } from "@/components/calendar/CalendarGrid";
import { DutyList } from "@/components/duty/DutyList";
import { DayDetail, type DayDetailHandle } from "@/components/day-detail";
import { ErrorState } from "@/components/states/ErrorState";
import { AccessDenied } from "@/components/states/AccessDenied";

/** Fallback height (px) until ResizeObserver reports the sticky block size. Matches --calendar-block-min-height + pb-2 (260 + 8). */
const STICKY_HEIGHT_FALLBACK_PX = 268;

export interface CalendarPageProps {
  /** Whether the user is allowed (for data loading). */
  isAllowed: boolean;
  /** Raw initData string for API auth. */
  initDataRaw: string | undefined;
}

export function CalendarPage({ isAllowed, initDataRaw }: CalendarPageProps) {
  const dayDetailRef = useRef<DayDetailHandle>(null);
  const calendarStickyRef = useRef<HTMLDivElement>(null);
  const [stickyBlockHeight, setStickyBlockHeight] = useState(STICKY_HEIGHT_FALLBACK_PX);

  useEffect(() => {
    const el = calendarStickyRef.current;
    if (!el) return;
    const observer = new ResizeObserver((entries) => {
      const entry = entries[0];
      if (entry) setStickyBlockHeight(entry.contentRect.height);
    });
    observer.observe(el);
    return () => observer.disconnect();
  }, []);

  const {
    currentMonth,
    pendingMonth,
    loading,
    error,
    accessDenied,
    accessDeniedDetail,
    duties,
    calendarEvents,
    selectedDay,
    nextMonth,
    prevMonth,
    setCurrentMonth,
    setSelectedDay,
    setAppContentReady,
  } = useAppStore(
    useShallow((s) => ({
      currentMonth: s.currentMonth,
      pendingMonth: s.pendingMonth,
      loading: s.loading,
      error: s.error,
      accessDenied: s.accessDenied,
      accessDeniedDetail: s.accessDeniedDetail,
      duties: s.duties,
      calendarEvents: s.calendarEvents,
      selectedDay: s.selectedDay,
      nextMonth: s.nextMonth,
      prevMonth: s.prevMonth,
      setCurrentMonth: s.setCurrentMonth,
      setSelectedDay: s.setSelectedDay,
      setAppContentReady: s.setAppContentReady,
    }))
  );

  const { retry } = useMonthData({
    initDataRaw,
    enabled: isAllowed,
  });

  const now = new Date();
  const isCurrentMonth =
    currentMonth.getFullYear() === now.getFullYear() &&
    currentMonth.getMonth() === now.getMonth();
  useAutoRefresh(retry, isCurrentMonth);

  const navDisabled = loading || accessDenied || selectedDay !== null;
  const handlePrevMonth = useCallback(() => {
    if (navDisabled) return;
    prevMonth();
  }, [navDisabled, prevMonth]);
  const handleNextMonth = useCallback(() => {
    if (navDisabled) return;
    nextMonth();
  }, [navDisabled, nextMonth]);

  useSwipe(
    calendarStickyRef,
    handleNextMonth,
    handlePrevMonth,
    { threshold: 50, disabled: navDisabled }
  );
  useStickyScroll(calendarStickyRef);

  const handleDayClick = useCallback(
    (dateKey: string, anchorRect: DOMRect) => {
      const [y, m] = dateKey.split("-").map(Number);
      if (
        y !== currentMonth.getFullYear() ||
        m !== currentMonth.getMonth() + 1
      ) {
        return;
      }
      dayDetailRef.current?.openWithRect(dateKey, anchorRect);
    },
    [currentMonth]
  );

  const handleCloseDayDetail = useCallback(() => {
    setSelectedDay(null);
  }, [setSelectedDay]);

  const readyCalledRef = useRef(false);
  // Mark content ready when first load finishes or access denied, so page can call ready() and show content.
  useEffect(() => {
    if ((!loading || accessDenied) && !readyCalledRef.current) {
      readyCalledRef.current = true;
      setAppContentReady(true);
    }
  }, [loading, accessDenied, setAppContentReady]);

  return (
    <div className="mx-auto flex min-h-screen w-full max-w-[var(--max-width-app)] flex-col bg-background px-3 pb-6 pt-safe">
      <div
        ref={calendarStickyRef}
        className="sticky top-0 z-10 min-h-[var(--calendar-block-min-height)] bg-background pb-2"
      >
        <CalendarHeader
          month={currentMonth}
          disabled={navDisabled}
          onPrevMonth={handlePrevMonth}
          onNextMonth={handleNextMonth}
        />
        <CalendarGrid
          currentMonth={currentMonth}
          duties={duties}
          calendarEvents={calendarEvents}
          onDayClick={handleDayClick}
        />
      </div>

      {accessDenied && (
        <AccessDenied serverDetail={accessDeniedDetail} className="my-3" />
      )}
      {!accessDenied && error && (
        <ErrorState message={error} onRetry={retry} className="my-3" />
      )}
      {!accessDenied && !error && (
        <DutyList
          scrollMarginTop={stickyBlockHeight}
          className="mt-2"
        />
      )}

      <DayDetail
        ref={dayDetailRef}
        duties={duties}
        calendarEvents={calendarEvents}
        onClose={handleCloseDayDetail}
      />
    </div>
  );
}
