import { useEffect, useRef, useState } from "react";

import { fetchTripDates } from "@/api/client";
import { BookingIcon } from "@/components/booking/BookingIcons";
import type { SeatClass } from "@/config/booking-config";
import {
  compareIsoDates,
  isoDateToDisplay,
  isoToDatePickerDisplay,
  parseDisplayDate,
} from "@/lib/booking-search";

const WEEKDAYS = ["T2", "T3", "T4", "T5", "T6", "T7", "CN"];

function mondayOffset(year: number, month: number): number {
  const day = new Date(year, month - 1, 1).getDay();
  return day === 0 ? 6 : day - 1;
}

function daysInMonth(year: number, month: number): number {
  return new Date(year, month, 0).getDate();
}

type Props = {
  "aria-label": string;
  valueIso: string;
  minIso: string;
  routeSlug: string | null;
  seatClass: SeatClass;
  onChangeDisplay: (displayDdMmYyyy: string) => void;
};

export function BookingTripDateControl({
  "aria-label": ariaLabel,
  valueIso,
  minIso,
  routeSlug,
  seatClass,
  onChangeDisplay,
}: Props) {
  const rootRef = useRef<HTMLDivElement>(null);
  const [open, setOpen] = useState(false);
  const parsed = parseDisplayDate(isoDateToDisplay(valueIso) || isoDateToDisplay(minIso));
  const initialYear = parsed?.getFullYear() ?? new Date().getFullYear();
  const initialMonth = (parsed?.getMonth() ?? new Date().getMonth()) + 1;

  const [viewYear, setViewYear] = useState(initialYear);
  const [viewMonth, setViewMonth] = useState(initialMonth);
  const [tripDates, setTripDates] = useState<Set<string>>(new Set());

  const monthKey = `${viewYear}-${String(viewMonth).padStart(2, "0")}`;

  useEffect(() => {
    if (!routeSlug) {
      setTripDates(new Set());
      return;
    }
    let cancelled = false;
    fetchTripDates(routeSlug, monthKey, seatClass)
      .then((res) => {
        if (!cancelled) setTripDates(new Set(res.dates));
      })
      .catch(() => {
        if (!cancelled) setTripDates(new Set());
      });
    return () => {
      cancelled = true;
    };
  }, [routeSlug, monthKey, seatClass]);

  useEffect(() => {
    if (!open) return;
    const onDoc = (e: MouseEvent) => {
      if (rootRef.current && !rootRef.current.contains(e.target as Node)) {
        setOpen(false);
      }
    };
    document.addEventListener("mousedown", onDoc);
    return () => document.removeEventListener("mousedown", onDoc);
  }, [open]);

  function openCalendar() {
    if (valueIso) {
      setViewYear(Number(valueIso.slice(0, 4)));
      setViewMonth(Number(valueIso.slice(5, 7)));
    }
    setOpen(true);
  }

  function shiftMonth(delta: number) {
    const d = new Date(viewYear, viewMonth - 1 + delta, 1);
    setViewYear(d.getFullYear());
    setViewMonth(d.getMonth() + 1);
  }

  function selectDay(iso: string) {
    if (compareIsoDates(iso, minIso) < 0) return;
    const display = isoDateToDisplay(iso);
    if (display) onChangeDisplay(display);
    setOpen(false);
  }

  const offset = mondayOffset(viewYear, viewMonth);
  const totalDays = daysInMonth(viewYear, viewMonth);
  const cells: Array<{ iso: string; day: number } | null> = [];
  for (let i = 0; i < offset; i++) cells.push(null);
  for (let day = 1; day <= totalDays; day++) {
    const iso = `${viewYear}-${String(viewMonth).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
    cells.push({ iso, day });
  }

  const display = isoToDatePickerDisplay(valueIso);

  return (
    <div ref={rootRef} className="ferry-booking__trip-date">
      <div
        className="ferry-booking__control ferry-booking__control--date"
        role="button"
        tabIndex={0}
        aria-label={ariaLabel}
        aria-expanded={open}
        onClick={openCalendar}
        onKeyDown={(e) => {
          if (e.key === "Enter" || e.key === " ") {
            e.preventDefault();
            openCalendar();
          }
        }}
      >
        <span className="ferry-booking__control-addon" aria-hidden>
          <BookingIcon name="calendar" />
        </span>
        <div className="ferry-booking__control-body">
          <span className="ferry-booking__date-display" aria-hidden>
            {display || "dd/mm/yyyy"}
          </span>
        </div>
      </div>

      {open ? (
        <div className="ferry-booking__date-popover" role="dialog" aria-label={ariaLabel}>
          <div className="ferry-booking__date-popover-head">
            <button type="button" className="ferry-booking__date-nav" onClick={() => shiftMonth(-1)} aria-label="Tháng trước">
              ‹
            </button>
            <span className="ferry-booking__date-popover-title">
              Tháng {viewMonth}/{viewYear}
            </span>
            <button type="button" className="ferry-booking__date-nav" onClick={() => shiftMonth(1)} aria-label="Tháng sau">
              ›
            </button>
          </div>
          <div className="ferry-booking__date-weekdays" aria-hidden>
            {WEEKDAYS.map((w) => (
              <span key={w}>{w}</span>
            ))}
          </div>
          <div className="ferry-booking__date-grid">
            {cells.map((cell, idx) => {
              if (!cell) {
                return <span key={`e-${idx}`} className="ferry-booking__date-cell ferry-booking__date-cell--empty" />;
              }
              const isPast = compareIsoDates(cell.iso, minIso) < 0;
              const hasTrip = tripDates.has(cell.iso);
              const isSelected = cell.iso === valueIso;
              return (
                <button
                  key={cell.iso}
                  type="button"
                  disabled={isPast}
                  className={[
                    "ferry-booking__date-cell",
                    hasTrip ? "has-trip" : "",
                    isSelected ? "is-selected" : "",
                    isPast ? "is-disabled" : "",
                  ]
                    .filter(Boolean)
                    .join(" ")}
                  onClick={() => selectDay(cell.iso)}
                >
                  {cell.day}
                </button>
              );
            })}
          </div>
        </div>
      ) : null}
    </div>
  );
}
