/**
 * Online Board start page matching the Angular `online-board-start-page`
 * component DOM structure and CSS class names.
 *
 * Uses PageLayout for the two-column layout, PageTabs in the header-left,
 * OnlineBoardFilter + SearchHistory in content-left, and the info section
 * + popular requests in the main content area.
 *
 * No API calls on load. Pure presentation that navigates to search
 * routes via the filter component.
 *
 * @module
 */

import { type FC, useCallback, useState, useRef } from "react";
import { useNavigate } from "@modern-js/runtime/router";
import { useLocale } from "@/i18n/useLocale.js";
import { useTranslation } from "@/i18n/provider.js";
import { PageLayout } from "@/ui/layout/PageLayout.js";
import { PageTabs } from "@/ui/layout/PageTabs.js";
import { SearchHistory } from "@/ui/layout/SearchHistory.js";
import { OnlineBoardFilter } from "./OnlineBoardFilter.js";
import { PopularRequestsPanel } from "@/features/popular-requests/components/PopularRequestsPanel.js";
import type { PopularRequest } from "@/features/popular-requests/types.js";
import {
  readAndClearTransientPrefill,
  writeTransientPrefill,
} from "@/shared/state/transientPrefill.js";
import {
  getBoardFilter,
  getScheduleFilter,
  projectScheduleToBoard,
} from "@/shared/state/crossSectionNavigation.js";
import {
  useDictionaries,
  getCityCodeByAirportCode,
} from "@/shared/dictionaries/index.js";
import type { IDictionaries } from "@/shared/dictionaries/index.js";
import { useGeoCityDefault } from "@/shared/hooks/useGeoCityDefault.js";
import { useIsMobileViewport } from "@/shared/hooks/useIsMobileViewport.js";
import { getOnlineBoardDefaultTimeRange } from "../timeDefaults.js";
import "./OnlineBoardStartPage.scss";

/**
 * Normalize a popular-request IATA code to its owning city code so that
 * clicking "Шереметьево - Санкт-Петербург" (SVO → LED) lands on the same
 * Moscow → St Petersburg prefill as any MOW-coded entry. Falls back to
 * the original code when the airport has no parent city or when the
 * dictionaries aren't loaded yet.
 */
function toCityCode(code: string, dictionaries: IDictionaries | null): string {
  if (!dictionaries) return code;
  return getCityCodeByAirportCode(dictionaries, code) ?? code;
}

export const ONLINE_BOARD_PREFILL_SLOT = "online-board";
export const SCHEDULE_PREFILL_SLOT = "schedule";

/**
 * Transient prefill state handed from a popular-request click to the
 * start page form. Mirrors Angular's `OnlineBoardFiltersStateService`
 * cross-page singleton: URLs stay clean of query strings while the
 * start-page form still seeds itself from the click. Held in
 * sessionStorage and read-then-cleared on mount, so a fresh tab or
 * page reload renders an empty form (matching Angular's behavior of a
 * pristine service after navigation).
 */
export interface OnlineBoardPrefillState {
  tab?: "flight" | "route";
  departure?: string;
  arrival?: string;
  flightNumber?: string;
  /** yyyyMMdd — date to pre-populate in the filter calendar (TZ §4.1.5) */
  date?: string;
}

function todayYyyymmdd(): string {
  const d = new Date();
  const y = d.getFullYear().toString();
  const m = (d.getMonth() + 1).toString().padStart(2, "0");
  const day = d.getDate().toString().padStart(2, "0");
  return `${y}${m}${day}`;
}

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

/**
 * Mon-Sun bounds of the ISO week containing `base` (or today), CLAMPED to
 * the Schedule date window's lower edge (today - 1). When today falls mid-week,
 * returning a Monday that is already past the -1-day window would make the
 * subsequent route guard redirect back to the start page, so we clamp the
 * `from` end to max(mon, today - 1). Used by TZ §4.1.5 Schedule popular-click
 * prefill — "показать расписание на = текущая неделя".
 */
function currentWeekBoundsYyyymmdd(base = new Date()): { from: string; to: string } {
  const d = new Date(base);
  d.setHours(0, 0, 0, 0);
  const dow = d.getDay() === 0 ? 7 : d.getDay();
  const mon = new Date(d);
  mon.setDate(d.getDate() - (dow - 1));
  const sun = new Date(mon);
  sun.setDate(mon.getDate() + 6);
  // Clamp `from` to today − 1 so the prefilled range is always inside
  // Schedule's [−1, +330] window and the route guard never rejects it.
  const minFrom = new Date(d);
  minFrom.setDate(d.getDate() - 1);
  const from = mon.getTime() < minFrom.getTime() ? minFrom : mon;
  return { from: dateToYyyymmdd(from), to: dateToYyyymmdd(sun) };
}

/**
 * Mon–Sun bounds of the ISO week AFTER the one containing `base`.
 * Used by TZ §4.1.5 round-trip prefill — "дата обратного рейса = следующая неделя".
 * Derived from the raw Monday of the current week (not the clamped
 * `currentWeekBoundsYyyymmdd().from`) so the next-Mon is always correct
 * regardless of how mid-week `base` is.
 */
function nextWeekBoundsYyyymmdd(base = new Date()): { from: string; to: string } {
  const d = new Date(base);
  d.setHours(0, 0, 0, 0);
  const dow = d.getDay() === 0 ? 7 : d.getDay();
  const thisMon = new Date(d);
  thisMon.setDate(d.getDate() - (dow - 1));
  const nextMon = new Date(thisMon);
  nextMon.setDate(thisMon.getDate() + 7);
  const nextSun = new Date(nextMon);
  nextSun.setDate(nextMon.getDate() + 6);
  return { from: dateToYyyymmdd(nextMon), to: dateToYyyymmdd(nextSun) };
}

export function buildOnlineBoardPrefillState(
  request: PopularRequest,
  dictionaries: IDictionaries | null = null,
): OnlineBoardPrefillState {
  // TZ §4.1.5: all Board popular-request kinds prefill date = today.
  const date = todayYyyymmdd();
  switch (request.mode) {
    case "FlightNumber":
      return {
        tab: "flight",
        flightNumber: `${request.carrier}${request.flightNumber}`,
        date,
      };
    case "Departure":
      return { tab: "route", departure: toCityCode(request.departure, dictionaries), date };
    case "Arrival":
      return { tab: "route", arrival: toCityCode(request.arrival, dictionaries), date };
    case "Route":
    case "RouteWithBack":
      return {
        tab: "route",
        departure: toCityCode(request.departure, dictionaries),
        arrival: toCityCode(request.arrival, dictionaries),
        date,
      };
  }
}

export interface OnlineBoardStartPageProps {
  /**
   * yyyyMMdd of "today" computed once on the SSR server (route loader)
   * and passed through to children. See OnlineBoardFilterProps.today.
   */
  today?: string;
}

export const OnlineBoardStartPage: FC<OnlineBoardStartPageProps> = ({ today }) => {
  const { t } = useTranslation();
  const navigate = useNavigate();
  const { locale, language } = useLocale();
  const { dictionaries } = useDictionaries(language);
  const isMobile = useIsMobileViewport();

  // TZ §4.1.1-R4/R5: mobile-aware default time range, computed once on mount.
  // On desktop/tablet: 00:00-24:00. On mobile: [-1h, +3h] from now.
  // Stored in a ref so the value doesn't shift on re-renders.
  const defaultTimeRange = useRef(getOnlineBoardDefaultTimeRange(isMobile)).current;

  // Read-and-clear any prefill the previous page wrote. Stored in
  // useState (with a one-shot initializer) so React strict mode's
  // double-render doesn't lose the value on the second pass.
  // Falls back to cross-section store projection (Schedule → Board) per
  // TZ §4.1.8 Table 10 when no transient prefill is present.
  const [prefill, setPrefill] = useState<OnlineBoardPrefillState>(() => {
    const transient = readAndClearTransientPrefill<OnlineBoardPrefillState>(
      ONLINE_BOARD_PREFILL_SLOT,
    );
    if (transient) return transient;

    // TZ §4.1.8: try own snapshot first, then project from Schedule.
    const boardSnap = getBoardFilter();
    if (boardSnap) {
      return {
        tab: "route",
        ...(boardSnap.departure ? { departure: boardSnap.departure } : {}),
        ...(boardSnap.arrival ? { arrival: boardSnap.arrival } : {}),
      };
    }
    const schedSnap = getScheduleFilter();
    if (schedSnap) {
      const projected = projectScheduleToBoard(schedSnap);
      return {
        tab: "route",
        ...(projected.departure ? { departure: projected.departure } : {}),
        ...(projected.arrival ? { arrival: projected.arrival } : {}),
      };
    }
    return {};
  });
  // Same-page popular clicks need to re-mount the filter so its
  // useState initial values pick up the new prefill. Key bump does it.
  const [filterKey, setFilterKey] = useState(0);

  // TZ §4.1.1-R1: on first session entry with geo consent, auto-fill
  // departure city with the user's nearest city when:
  //   - no stored board filter is present (fresh session), AND
  //   - the current prefill has no departure already (not populated by
  //     transient prefill, cross-section hydration, or user interaction).
  useGeoCityDefault({
    dictionaries,
    shouldApply: () => !getBoardFilter() && !prefill.departure,
    onCity: (cityCode) => {
      setPrefill((prev) =>
        prev.departure ? prev : { ...prev, departure: cityCode },
      );
    },
  });

  const filterInitialProps = {
    ...(prefill.tab ? { initialTab: prefill.tab } : {}),
    ...(prefill.departure ? { initialDeparture: prefill.departure } : {}),
    ...(prefill.arrival ? { initialArrival: prefill.arrival } : {}),
    ...(prefill.flightNumber ? { initialFlightNumber: prefill.flightNumber } : {}),
    // TZ §4.1.5: popular-request click prefills date=today for all Board modes.
    ...(prefill.date ? { initialDate: prefill.date } : {}),
    initialTimeFrom: defaultTimeRange.timeFrom,
    initialTimeTo: defaultTimeRange.timeTo,
  };

  const handlePopularRequestClick = useCallback(
    (request: PopularRequest) => {
      // Schedule-type requests open the Schedule start page with the form
      // prefilled from the clicked popular item, matching Angular's shared
      // filter-state behavior.
      if (request.type === "Schedule") {
        const state: Record<string, unknown> =
          request.mode === "Route" || request.mode === "RouteWithBack"
            ? (() => {
                const base: Record<string, unknown> = {
                  departure: toCityCode(request.departure, dictionaries),
                  arrival: toCityCode(request.arrival, dictionaries),
                  withReturn: request.mode === "RouteWithBack",
                };
                const cur = currentWeekBoundsYyyymmdd();
                base.dateFrom = cur.from;
                base.dateTo = cur.to;
                if (request.mode === "RouteWithBack") {
                  const nxt = nextWeekBoundsYyyymmdd();
                  base.returnDateFrom = nxt.from;
                  base.returnDateTo = nxt.to;
                }
                return base;
              })()
            : {};
        writeTransientPrefill(SCHEDULE_PREFILL_SLOT, state);
        navigate(`/${locale}/schedule`);
        return;
      }

      // Onlineboard request — same page. Update local prefill +
      // remount the filter via key bump so its useState initializers
      // see the new initial* props.
      setPrefill(buildOnlineBoardPrefillState(request, dictionaries));
      setFilterKey((n) => n + 1);
    },
    [navigate, locale, dictionaries],
  );

  return (
    <div className="online-board-start-page" data-testid="online-board-start">
      <PageLayout
        headerLeft={
          <PageTabs viewType="onlineboard" />
        }
        title={
          <h1 className="text--white page-title">
            {t("BOARD.TITLE")}
          </h1>
        }
        breadcrumbs={[]}
        contentLeft={
          <>
            <OnlineBoardFilter key={filterKey} {...filterInitialProps} {...(today ? { today } : {})} />
            <SearchHistory />
          </>
        }
      >
        <section className="frame">
          <h2>{t("BOARD.BOARD-START")}</h2>

          <div className="titles-container">
            <div className="title title1">
              <a>{t("BOARD.BOARD-START-TITLE1")}</a>
              <div>
                {t("BOARD.BOARD-START-TITLE1-DESCRIPTION")}
              </div>
            </div>

            <div className="title title2">
              <a>{t("BOARD.BOARD-START-TITLE2")}</a>
              <div>
                {t("BOARD.BOARD-START-TITLE2-DESCRIPTION")}
              </div>
            </div>

            <div className="title title3">
              <a>{t("BOARD.BOARD-START-TITLE3")}</a>
              <div>
                {t("BOARD.BOARD-START-TITLE3-DESCRIPTION")}
              </div>
            </div>

            <div className="title title4">
              <a>{t("BOARD.BOARD-START-TITLE4")}</a>
              <div>
                {t("BOARD.BOARD-START-TITLE4-DESCRIPTION")}
              </div>
            </div>
          </div>

          <PopularRequestsPanel onRequestClick={handlePopularRequestClick} />
        </section>
      </PageLayout>
    </div>
  );
};
