import { useCallback, useMemo, useRef, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import type { Race } from "../api";
import { buildMonthCells, groupRacesByYmd, isRaceDateInPast, toYmd, WEEKDAY_LABELS_SHORT_RU } from "../lib";

const MONTH_NAMES_RU_SHORT = [
  "янв.",
  "февр.",
  "мар.",
  "апр.",
  "май",
  "июн.",
  "июл.",
  "авг.",
  "сент.",
  "окт.",
  "нояб.",
  "дек.",
];

const POPOVER_LEAVE_MS = 140;

function toLocalYmd(date: Date): string {
  const y = date.getFullYear();
  const m = String(date.getMonth() + 1).padStart(2, "0");
  const d = String(date.getDate()).padStart(2, "0");
  return `${y}-${m}-${d}`;
}

interface RacesCalendarProps {
  displayYear: number;
  monthFilter: string;
  races: Race[];
  onMonthFilterChange: (value: string) => void;
}

function DayPopover(props: {
  ymd: string;
  races: Race[];
  onCancelClose: () => void;
  onScheduleClose: () => void;
}): JSX.Element {
  const { ymd, races, onCancelClose, onScheduleClose } = props;
  const hasRaces = races.length > 0;

  return (
    <div
      className="races-cal__popover"
      role="tooltip"
      onMouseEnter={onCancelClose}
      onMouseLeave={onScheduleClose}
    >
      {hasRaces ? (
        <ul className="races-cal__popover-list">
          {races.map((r) => (
            <li key={r.id} className="races-cal__popover-item">
              <Link className="races-cal__popover-link" to={`/races/${r.id}`} onClick={onCancelClose}>
                {r.title}
              </Link>
            </li>
          ))}
        </ul>
      ) : (
        <p className="races-cal__popover-empty">Стартов нет</p>
      )}
      <Link
        className="btn btn--secondary races-cal__popover-add"
        to={`/races/new?date=${ymd}`}
        onClick={onCancelClose}
      >
        Добавить
      </Link>
    </div>
  );
}

function CalendarMonthBlock(props: {
  year: number;
  monthIndex: number;
  racesByYmd: Map<string, Race[]>;
  compact: boolean;
  navigate: ReturnType<typeof useNavigate>;
  openYmd: string | null;
  setOpenYmd: (v: string | null) => void;
  scheduleClose: () => void;
  cancelClose: () => void;
  onMonthSelect?: (monthIndex: number) => void;
  todayYmd: string;
}): JSX.Element {
  const {
    year,
    monthIndex,
    racesByYmd,
    compact,
    navigate,
    openYmd,
    setOpenYmd,
    scheduleClose,
    cancelClose,
    onMonthSelect,
    todayYmd,
  } = props;
  const cells = useMemo(() => buildMonthCells(year, monthIndex), [year, monthIndex]);
  const title = `${MONTH_NAMES_RU_SHORT[monthIndex]} ${year}`;

  const blockClass = compact ? "races-cal__month races-cal__month--compact" : "races-cal__month";

  return (
    <div className={blockClass}>
      <h3 className="races-cal__month-title">
        {onMonthSelect ? (
          <button
            type="button"
            className="races-cal__month-title-button"
            onClick={() => {
              onMonthSelect(monthIndex);
            }}
          >
            {title}
          </button>
        ) : (
          title
        )}
      </h3>
      <div className="races-cal__weekdays" aria-hidden>
        {WEEKDAY_LABELS_SHORT_RU.map((d) => (
          <span key={d} className="races-cal__weekday">
            {d}
          </span>
        ))}
      </div>
      <div className="races-cal__cells">
        {cells.map((day, idx) => {
          if (day === null) {
            return <div key={`e-${idx}`} className="races-cal__cell races-cal__cell--empty" />;
          }
          const ymd = toYmd(year, monthIndex, day);
          const dayRaces = racesByYmd.get(ymd) ?? [];
          const hasRaces = dayRaces.length > 0;
          const isOpen = openYmd === ymd;
          const isPast = isRaceDateInPast(ymd);
          const isToday = ymd === todayYmd;
          const cellClassName = [
            "races-cal__cell",
            hasRaces ? "races-cal__cell--has-race" : "",
            isOpen ? "races-cal__cell--open" : "",
            isPast ? "races-cal__cell--past" : "",
            isToday ? "races-cal__cell--today" : "",
          ]
            .filter(Boolean)
            .join(" ");

          return (
            <div
              key={ymd}
              className={cellClassName}
              onMouseEnter={() => {
                cancelClose();
                setOpenYmd(hasRaces ? ymd : null);
              }}
              onMouseLeave={scheduleClose}
            >
              <button
                type="button"
                className="races-cal__day-btn"
                onClick={() => {
                  navigate(`/races/day/${ymd}`);
                }}
                onFocus={() => {
                  cancelClose();
                  setOpenYmd(hasRaces ? ymd : null);
                }}
                onBlur={(e) => {
                  const next = e.relatedTarget as Node | null;
                  if (next && e.currentTarget.closest(".races-cal__cell")?.contains(next)) {
                    return;
                  }
                  scheduleClose();
                }}
              >
                {day}
              </button>
              {isOpen && hasRaces ? (
                <DayPopover
                  ymd={ymd}
                  races={dayRaces}
                  onCancelClose={cancelClose}
                  onScheduleClose={scheduleClose}
                />
              ) : null}
            </div>
          );
        })}
      </div>
    </div>
  );
}

export function RacesCalendar(props: RacesCalendarProps): JSX.Element {
  const { displayYear, monthFilter, races, onMonthFilterChange } = props;
  const navigate = useNavigate();
  const [openYmd, setOpenYmd] = useState<string | null>(null);
  const leaveTimerRef = useRef<number | null>(null);

  const cancelClose = useCallback(() => {
    if (leaveTimerRef.current !== null) {
      window.clearTimeout(leaveTimerRef.current);
      leaveTimerRef.current = null;
    }
  }, []);

  const scheduleClose = useCallback(() => {
    cancelClose();
    leaveTimerRef.current = window.setTimeout(() => {
      setOpenYmd(null);
      leaveTimerRef.current = null;
    }, POPOVER_LEAVE_MS);
  }, [cancelClose]);

  const racesByYmd = useMemo(() => groupRacesByYmd(races), [races]);
  const todayYmd = useMemo(() => toLocalYmd(new Date()), []);

  const focusedMonthIndex = monthFilter === "" ? null : parseInt(monthFilter, 10) - 1;

  return (
    <div className="races-cal">
      <p className="races-cal__hint">Наведите на дату с забегом — краткая информация. Клик — страница дня.</p>
      {focusedMonthIndex === null || Number.isNaN(focusedMonthIndex) ? (
        <div className="races-cal__year">
          {MONTH_NAMES_RU_SHORT.map((_, mi) => (
            <CalendarMonthBlock
              key={mi}
              year={displayYear}
              monthIndex={mi}
              racesByYmd={racesByYmd}
              compact
              navigate={navigate}
              openYmd={openYmd}
              setOpenYmd={setOpenYmd}
              scheduleClose={scheduleClose}
              cancelClose={cancelClose}
              onMonthSelect={(mi) => {
                onMonthFilterChange(String(mi + 1));
                setOpenYmd(null);
              }}
              todayYmd={todayYmd}
            />
          ))}
        </div>
      ) : (
        <div className="races-cal__month-focus">
          <nav className="races-cal__month-nav" aria-label="Месяцы года">
            {MONTH_NAMES_RU_SHORT.map((label, mi) => (
              <button
                key={label}
                type="button"
                className={`races-cal__month-nav-item${mi === focusedMonthIndex ? " races-cal__month-nav-item--active" : ""}`}
                onClick={() => {
                  onMonthFilterChange(String(mi + 1));
                }}
              >
                {label}
              </button>
            ))}
            <button
              type="button"
              className="races-cal__month-nav-item races-cal__month-nav-item--all"
              onClick={() => {
                onMonthFilterChange("");
              }}
            >
              Весь год
            </button>
          </nav>
          <CalendarMonthBlock
            year={displayYear}
            monthIndex={focusedMonthIndex}
            racesByYmd={racesByYmd}
            compact={false}
            navigate={navigate}
            openYmd={openYmd}
            setOpenYmd={setOpenYmd}
            scheduleClose={scheduleClose}
            cancelClose={cancelClose}
            todayYmd={todayYmd}
          />
        </div>
      )}
    </div>
  );
}
