/**
 * Duty timeline list for the current month: dates, track line, flip cards.
 * Scrolls to current duty or today on mount. Ported from webapp/js/dutyList.js renderDutyList.
 */

"use client";

import { useEffect, useMemo, useRef, useState } from "react";
import { useAppStore } from "@/store/app-store";
import { useShallow } from "zustand/react/shallow";
import { useTranslation } from "@/i18n/use-translation";
import {
  localDateString,
  firstDayOfMonth,
  lastDayOfMonth,
  dateKeyToDDMM,
} from "@/lib/date-utils";
import { cn } from "@/lib/utils";
import { Skeleton } from "@/components/ui/skeleton";
import { LoadingState } from "@/components/states/LoadingState";
import { DutyTimelineCard } from "./DutyTimelineCard";

/** Extra offset so the sticky calendar slightly overlaps the target card (card sits a bit under the calendar). */
const SCROLL_OVERLAP_PX = 14;

/**
 * Skeleton placeholder for duty list (e.g. when loading a new month).
 * Shows 4 card-shaped placeholders in timeline layout.
 */
export function DutyListSkeleton({ className }: { className?: string }) {
  return (
    <div className={cn("duty-timeline relative text-[0.9rem]", className)}>
      <div
        className="absolute left-[calc(var(--timeline-date-width)+var(--timeline-track-width)/2-1px)] top-0 bottom-0 w-0.5 pointer-events-none bg-gradient-to-b from-muted from-0% to-[85%] to-[var(--muted-fade)]"
        aria-hidden
      />
      {[1, 2, 3, 4].map((i) => (
        <div
          key={i}
          className="grid gap-x-1 items-start mb-2 min-h-0"
          style={{
            gridTemplateColumns:
              "var(--timeline-date-width) var(--timeline-track-width) 1fr",
          }}
        >
          <Skeleton className="h-14 w-12 rounded" />
          <span className="min-w-0" aria-hidden />
          <Skeleton className="h-20 w-full min-w-0 rounded-lg" />
        </div>
      ))}
    </div>
  );
}

export interface DutyListProps {
  /** Offset from viewport top for scroll target (sticky calendar height + its padding, e.g. 268px). */
  scrollMarginTop?: number;
  className?: string;
}

/**
 * Renders duty timeline (duty type only), grouped by date. Shows "Today" label and
 * auto-scrolls to current duty or today block. Uses CSS variables --timeline-date-width, --timeline-track-width.
 */
export function DutyList({ scrollMarginTop = 268, className }: DutyListProps) {
  const { t } = useTranslation();
  const listRef = useRef<HTMLDivElement>(null);
  const { currentMonth, duties, dataForMonthKey } = useAppStore(
    useShallow((s) => ({
      currentMonth: s.currentMonth,
      duties: s.duties,
      dataForMonthKey: s.dataForMonthKey,
    }))
  );
  const monthKey = `${currentMonth.getFullYear()}-${String(currentMonth.getMonth() + 1).padStart(2, "0")}`;
  const hasDataForMonth = dataForMonthKey === monthKey;

  const { filtered, dates, dutiesByDateKey } = useMemo(() => {
    const filteredList = duties.filter((d) => d.event_type === "duty");
    const todayKey = localDateString(new Date());
    const firstKey = localDateString(firstDayOfMonth(currentMonth));
    const lastKey = localDateString(lastDayOfMonth(currentMonth));
    const showTodayInMonth = todayKey >= firstKey && todayKey <= lastKey;

    const dateSet = new Set<string>();
    filteredList.forEach((d) =>
      dateSet.add(localDateString(new Date(d.start_at)))
    );
    if (showTodayInMonth) dateSet.add(todayKey);
    const datesList = Array.from(dateSet).sort();

    const byDate: Record<string, typeof filteredList> = {};
    datesList.forEach((date) => {
      byDate[date] = filteredList
        .filter((d) => localDateString(new Date(d.start_at)) === date)
        .sort(
          (a, b) =>
            new Date(a.start_at).getTime() - new Date(b.start_at).getTime()
        );
    });

    return {
      filtered: filteredList,
      dates: datesList,
      dutiesByDateKey: byDate,
    };
  }, [currentMonth, duties]);

  const todayKey = localDateString(new Date());
  const [now, setNow] = useState(() => new Date());
  const [flippedDutyId, setFlippedDutyId] = useState<number | null>(null);
  useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 60_000);
    return () => clearInterval(id);
  }, []);
  const scrolledForMonthRef = useRef<string | null>(null);
  const prevScrollMarginTopRef = useRef<number>(scrollMarginTop);
  const prevMonthKeyRef = useRef<string>(monthKey);

  useEffect(() => {
    if (scrollMarginTop !== prevScrollMarginTopRef.current) {
      scrolledForMonthRef.current = null;
      prevScrollMarginTopRef.current = scrollMarginTop;
    }
    if (prevMonthKeyRef.current !== monthKey) {
      scrolledForMonthRef.current = null;
      prevMonthKeyRef.current = monthKey;
    }

    const el = listRef.current;
    if (!el) return;
    const currentCard = el.querySelector<HTMLElement>("[data-current-duty]");
    const todayBlock = el.querySelector<HTMLElement>("[data-today-block]");
    const target = currentCard ?? todayBlock;
    if (!target || scrolledForMonthRef.current === monthKey) return;

    const effectiveMargin = Math.max(0, scrollMarginTop + SCROLL_OVERLAP_PX);
    const scrollTo = () => {
      const rect = target.getBoundingClientRect();
      const scrollTop = window.scrollY + rect.top - effectiveMargin;
      window.scrollTo({ top: scrollTop, behavior: "smooth" });
      scrolledForMonthRef.current = monthKey;
    };
    requestAnimationFrame(() => {
      requestAnimationFrame(scrollTo);
    });
  }, [filtered, dates.length, scrollMarginTop, monthKey]);

  if (!hasDataForMonth) {
    return (
      <div aria-busy="true" className={className}>
        <LoadingState asPlaceholder />
      </div>
    );
  }

  if (filtered.length === 0) {
    return (
      <div className={cn("flex flex-col gap-1", className)}>
        <p className="text-sm text-muted m-0">{t("duty.none_this_month")}</p>
        <p className="text-xs text-muted m-0">{t("duty.none_this_month_hint")}</p>
      </div>
    );
  }

  return (
    <div ref={listRef} className={className}>
      <div className="duty-timeline relative text-[0.9rem]">
        {/* Vertical track line */}
        <div
          className="absolute left-[calc(var(--timeline-date-width)+var(--timeline-track-width)/2-1px)] top-0 bottom-0 w-0.5 pointer-events-none bg-gradient-to-b from-muted from-0% to-[85%] to-[var(--muted-fade)]"
          aria-hidden
        />
        {dates.map((date) => {
          const isToday = date === todayKey;
          const dateLabel = dateKeyToDDMM(date);
          const dayDuties = dutiesByDateKey[date] ?? [];

          return (
            <div
              key={date}
              className="mb-0"
              style={isToday ? { scrollMarginTop: `${scrollMarginTop}px` } : undefined}
              data-date={date}
              data-today-block={isToday ? true : undefined}
            >
              {dayDuties.length > 0 ? (
                dayDuties.map((duty) => {
                  const start = new Date(duty.start_at);
                  const end = new Date(duty.end_at);
                  const isCurrent = start <= now && now < end;
                  return (
                    <div
                      key={duty.id}
                      className="grid gap-x-1 items-start mb-2 min-h-0"
                      style={{
                        gridTemplateColumns:
                          "var(--timeline-date-width) var(--timeline-track-width) 1fr",
                      }}
                    >
                      <TimelineDateCell
                        dateLabel={dateLabel}
                        isToday={isToday}
                      />
                      <span
                        className="min-w-0"
                        aria-hidden
                      />
                      <div
                        className="min-w-0 overflow-hidden"
                        {...(isCurrent ? { "data-current-duty": true } : {})}
                      >
                        <DutyTimelineCard
                          duty={duty}
                          isCurrent={isCurrent}
                          isFlipped={flippedDutyId === duty.id}
                          onFlipChange={(flip) =>
                            setFlippedDutyId(flip ? duty.id : null)
                          }
                        />
                      </div>
                    </div>
                  );
                })
              ) : (
                <div
                  className="grid gap-x-1 items-start mb-2 min-h-0"
                  style={{
                    gridTemplateColumns:
                      "var(--timeline-date-width) var(--timeline-track-width) 1fr",
                  }}
                >
                  <TimelineDateCell
                    dateLabel={dateLabel}
                    isToday={isToday}
                  />
                  <span className="min-w-0" aria-hidden />
                  <div className="min-w-0" />
                </div>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

function TimelineDateCell({
  dateLabel,
  isToday,
}: {
  dateLabel: string;
  isToday: boolean;
}) {
  const { t } = useTranslation();
  return (
    <span
      className={cn(
        "duty-timeline-date relative text-[0.8rem] text-muted pt-2.5 pb-2.5 flex-shrink-0 overflow-visible",
        isToday && "duty-timeline-date--today flex flex-col items-start pt-1 text-today font-semibold"
      )}
    >
      {isToday ? (
        <>
          <span className="duty-timeline-date-label text-today block leading-tight">
            {t("duty.today")}
          </span>
          <span className="duty-timeline-date-day text-muted font-normal text-[0.75rem] block self-start text-left">
            {dateLabel}
          </span>
        </>
      ) : (
        dateLabel
      )}
    </span>
  );
}
