/**
 * Schedule flight details page component.
 *
 * Receives parsed flight IDs from catch-all route, fetches details
 * via useScheduleDetails, renders multi-leg flight info.
 * No SignalR -- schedule data is static.
 *
 * @module
 */

import type { FC } from "react";
import { Fragment, useCallback, useMemo } from "react";
import { Link, useNavigate, useSearchParams } from "@modern-js/runtime/router";
import { useTranslation } from "@/i18n/provider.js";
import { FlightCard } from "@/ui/flights/FlightCard.js";
import { FlightListSkeleton } from "@/ui/flights/FlightListSkeleton.js";
import { IFlyWarning } from "@/ui/flights/IFlyWarning.js";
import { SeoHead } from "@/ui/seo/SeoHead.js";
import { PageLayout } from "@/ui/layout/PageLayout.js";
import { JsonLdRenderer } from "@/shared/seo/json-ld.js";
import { parseDetailsRequestParam } from "@/shared/detailsRequestParam.js";
import {
  buildAeroflotSbSearchUrl,
  formatAeroflotRouteDate,
} from "@/shared/booking/aeroflot.js";
import { buildScheduleUrl } from "../url.js";
import { useScheduleDetails } from "../hooks/useScheduleDetails.js";
import { useAppSettings } from "@/shared/hooks/useAppSettings.js";
import { useCityName } from "@/shared/hooks/useDictionaries.js";
import { buildScheduleDetailsSeo } from "../seo.js";
import { buildScheduleFlightJsonLd } from "../json-ld.js";
import { ScheduleFlightBody } from "./ScheduleFlightBody.js";
import { ScheduleLegDetails } from "./ScheduleLegDetails.js";
import { TransferBar } from "@/features/online-board/components/TransferBar/TransferBar.js";
import { FlightSchedule } from "@/features/online-board/components/FlightSchedule/index.js";
import { FullRouteTimeline } from "@/features/online-board/components/FullRouteTimeline/index.js";
import { ScheduleFlightsMiniList } from "./ScheduleFlightsMiniList.js";
import { DetailsHeaderBadge } from "@/features/online-board/components/BoardDetailsHeader/DetailsHeaderBadge.js";
import { FlightActions } from "@/features/online-board/components/BoardDetailsHeader/FlightActions.js";
import { LastUpdate } from "@/features/online-board/components/BoardDetailsHeader/LastUpdate.js";
import { DayTabs } from "@/features/online-board/components/DayTabs/index.js";
import type { IScheduleFlightId, IFlightLeg, ISimpleFlight } from "../types.js";
import "./ScheduleDetailsPage.scss";

export interface ScheduleDetailsPageProps {
  /** Parsed flight identifiers from the catch-all URL */
  flights: IScheduleFlightId[];
  /** Current locale for SEO */
  locale: string;
  /** Canonical origin for SEO URLs */
  canonicalOrigin: string;
}

/**
 * Convert yyyyMMdd to yyyy-MM-ddT00:00:00 for the API (matches Angular's
 * ApiFormatterService.formatDate).
 */
function formatApiDate(yyyymmdd: string): string {
  if (yyyymmdd.length !== 8) return yyyymmdd;
  return `${yyyymmdd.slice(0, 4)}-${yyyymmdd.slice(4, 6)}-${yyyymmdd.slice(6, 8)}T00:00:00`;
}



/**
 * Extract legs from a flight (handles both Direct and MultiLeg).
 */
function getLegs(flight: { routeType: string; leg?: IFlightLeg; legs?: IFlightLeg[] }): IFlightLeg[] {
  if (flight.routeType === "Direct" && "leg" in flight && flight.leg) {
    return [flight.leg];
  }
  if ("legs" in flight && flight.legs) {
    return flight.legs;
  }
  return [];
}

export const ScheduleDetailsPage: FC<ScheduleDetailsPageProps> = ({
  flights: flightIds,
  locale,
  canonicalOrigin,
}) => {
  const { t } = useTranslation();
  const navigate = useNavigate();
  const { scheduleSearchFrom, scheduleSearchTo } = useAppSettings();

  // Build API params from flight IDs
  const detailsParams = {
    flights: flightIds.map(
      (f) => `${f.carrier}${f.flightNumber}${f.suffix ?? ""}`,
    ),
    dates: flightIds.map((f) => formatApiDate(f.date)),
    // Use first flight's inferred departure/arrival as fallback
    departure: "",
    arrival: "",
  };

  const { flights, loading, error } = useScheduleDetails(detailsParams);

  const scheduleHref = `/${locale}/schedule`;

  // Parse ?request= to build the leaf breadcrumb (TZ §4.1.4 Table 7 rows 11-13)
  const [searchParams] = useSearchParams();
  const parentRequest = useMemo(() => {
    const raw = searchParams.get("request");
    return raw ? parseDetailsRequestParam(raw) : null;
  }, [searchParams]);

  // Angular's `schedule-flights-mini-list` only shows a multi-day
  // accordion when `schedule.length > 1`; for a connecting itinerary
  // it renders a single card — the currently-open flight, with the
  // combined "SU 6188, SU 6341" label and the full Moscow→Murmansk
  // origin/destination. We never need the day-±1 sibling search
  // because adjacent-day siblings read as visual duplicates of the
  // open flight. Synthesize the MultiLeg from the URL legs when
  // there's more than one; otherwise surface `flights[0]` as-is.
  const miniListCurrentFlight = useMemo<ISimpleFlight | null>(() => {
    if (flights.length > 1) {
      const first = flights[0] as ISimpleFlight | undefined;
      if (!first) return null;
      const legs: IFlightLeg[] = [];
      for (const f of flights) {
        const fl = f as ISimpleFlight;
        if (fl.routeType === "Direct") legs.push(fl.leg);
        else legs.push(...fl.legs);
      }
      const synthetic = {
        routeType: "MultiLeg",
        flightId: first.flightId,
        flyingTime: first.flyingTime,
        operatingBy: first.operatingBy,
        id: flights.map((f) => (f as ISimpleFlight).id).join("+"),
        status: first.status,
        legs,
        _childFlightIds: flights.map((f) => (f as ISimpleFlight).flightId),
      } as unknown as ISimpleFlight;
      return synthetic;
    }
    return (flights[0] ?? null) as ISimpleFlight | null;
  }, [flights]);

  // Angular's schedule details page adds a third crumb only when the
  // user reached the page from a search context (?request=schedule-…).
  // The leaf reads "{depCity} - {arrCity}" (no "Маршрут:" prefix —
  // unlike the onlineboard variant) and links back to the route search
  // URL with the original date range / filter state preserved.
  const requestDep =
    parentRequest && parentRequest.area === "schedule" ? parentRequest.departure : "";
  const requestArr =
    parentRequest && parentRequest.area === "schedule" ? parentRequest.arrival : "";
  const requestDepCity = useCityName(requestDep);
  const requestArrCity = useCityName(requestArr);

  const breadcrumbs = useMemo(() => {
    const baseCrumbs = [{ label: t("SCHEDULE.TITLE"), url: scheduleHref }];
    if (!parentRequest || parentRequest.area !== "schedule") return baseCrumbs;

    const backUrl = `/${locale}/${buildScheduleUrl(
      parentRequest.returnTrip
        ? {
            type: "roundtrip",
            outbound: {
              departure: parentRequest.departure,
              arrival: parentRequest.arrival,
              dateFrom: parentRequest.dateFrom,
              dateTo: parentRequest.dateTo,
              ...(parentRequest.timeFrom && parentRequest.timeTo
                ? { timeFrom: parentRequest.timeFrom, timeTo: parentRequest.timeTo }
                : {}),
              ...(parentRequest.connections !== undefined
                ? { connections: parentRequest.connections }
                : {}),
            },
            inbound: {
              departure: parentRequest.returnTrip.departure,
              arrival: parentRequest.returnTrip.arrival,
              dateFrom: parentRequest.returnTrip.dateFrom,
              dateTo: parentRequest.returnTrip.dateTo,
              ...(parentRequest.returnTrip.timeFrom && parentRequest.returnTrip.timeTo
                ? {
                    timeFrom: parentRequest.returnTrip.timeFrom,
                    timeTo: parentRequest.returnTrip.timeTo,
                  }
                : {}),
              ...(parentRequest.returnTrip.connections !== undefined
                ? { connections: parentRequest.returnTrip.connections }
                : {}),
            },
          }
        : {
            type: "route",
            outbound: {
              departure: parentRequest.departure,
              arrival: parentRequest.arrival,
              dateFrom: parentRequest.dateFrom,
              dateTo: parentRequest.dateTo,
              ...(parentRequest.timeFrom && parentRequest.timeTo
                ? { timeFrom: parentRequest.timeFrom, timeTo: parentRequest.timeTo }
                : {}),
              ...(parentRequest.connections !== undefined
                ? { connections: parentRequest.connections }
                : {}),
            },
          },
    )}`;

    const leafLabel = t("BREADCRUMBS.SCHEDULE-ROUTE", {
      departureCity: requestDepCity,
      arrivalCity: requestArrCity,
    });

    return [...baseCrumbs, { label: leafLabel, url: backUrl }];
  }, [parentRequest, locale, scheduleHref, t, requestDepCity, requestArrCity]);

  /**
   * TZ §4.1.16.3 R22-R28: navigate to a new day for the same flight.
   * Note: §4.1.16.3.1 full navigation algorithm (re-run schedule search and
   * auto-open the matching day) is deferred — this implementation does a
   * direct URL navigation to the new date, which satisfies the structural
   * requirement. Full §4.1.16.3.1 is tracked as a separate task.
   */
  const handleNavigateDate = useCallback(
    (newDate: string) => {
      if (!flightIds[0]) return;
      const newFlightIds = flightIds.map((fid, i) =>
        i === 0 ? { ...fid, date: newDate } : fid,
      );
      const url = buildScheduleUrl({
        type: "details",
        flights: newFlightIds,
      });
      void navigate(`/${locale}/${url}`);
    },
    [flightIds, locale, navigate],
  );

  // Selected date is always the first flight's date (primary leg).
  const selectedDate = flightIds[0]?.date ?? "";

  // `Купить билет` link — navigates to Aeroflot's booking flow in a new
  // tab. Mirrors Angular's hardcoded ru-ru SB URL. Returns null when we
  // can't assemble the query (missing legs).
  const buyUrlFor = useCallback(
    (flight: ISimpleFlight): string | null => {
      const legs = flight.routeType === "Direct" ? [flight.leg] : flight.legs;
      const firstLeg = legs[0];
      const lastLeg = legs[legs.length - 1];
      if (!firstLeg || !lastLeg) return null;
      const dep = firstLeg.departure.scheduled.airportCode;
      const arr = lastLeg.arrival.scheduled.airportCode;
      const date = formatAeroflotRouteDate(
        firstLeg.departure.times.scheduledDeparture.local ||
          firstLeg.departure.times.scheduledDeparture.utc,
      );
      return buildAeroflotSbSearchUrl({ departure: dep, arrival: arr, date });
    },
    [],
  );
  const backLink = (
    <Link
      to={scheduleHref}
      className="schedule-details__back"
      data-testid="schedule-details-back"
    >
      <span aria-hidden="true" className="schedule-details__back-arrow">
        {"\u2190"}
      </span>
      {t("SHARED.BACK-SCHEDULE")}
    </Link>
  );
  // TZ §4.1.16.6 Table 60 rules 1+5: connecting flights show BOTH flight numbers in the header.
  const titleFlightNumbers = flightIds.length > 1
    ? flightIds.map((f) => `${f.carrier} ${f.flightNumber}${f.suffix ?? ""}`).join(", ")
    : flightIds[0]
      ? `${flightIds[0].carrier} ${flightIds[0].flightNumber}${flightIds[0].suffix ?? ""}`
      : null;
  const title = titleFlightNumbers
    ? `${t("BOARD.FLIGHT-INFO")}: ${titleFlightNumbers}`
    : t("SCHEDULE.TITLE");

  if (loading) {
    return (
      <PageLayout
        headerLeft={backLink}
        title={<h1 className="text--white page-title">{title}</h1>}
        breadcrumbs={breadcrumbs}
      >
        <section className="frame">
          <FlightListSkeleton count={flightIds.length} />
        </section>
      </PageLayout>
    );
  }

  if (error) {
    return (
      <PageLayout
        headerLeft={backLink}
        title={<h1 className="text--white page-title">{title}</h1>}
        breadcrumbs={breadcrumbs}
      >
        <section className="frame">
          <div
            className="schedule-details schedule-details--error"
            data-testid="schedule-details-error"
            role="alert"
          >
            <p>{t("BOARD.LOAD-FAILED")}</p>
          </div>
        </section>
      </PageLayout>
    );
  }

  if (flights.length === 0) {
    return (
      <PageLayout
        headerLeft={backLink}
        title={<h1 className="text--white page-title">{title}</h1>}
        breadcrumbs={breadcrumbs}
      >
        <section className="frame">
          <div
            className="schedule-details schedule-details--not-found"
            data-testid="schedule-details-not-found"
            role="status"
          >
            <p>{t("BOARD.FLIGHT-NOT-FOUND")}</p>
          </div>
        </section>
      </PageLayout>
    );
  }

  // SEO
  const seoProps = buildScheduleDetailsSeo(t, flights, locale, canonicalOrigin, flightIds);

  // Single-flight chains render one schedule body. Multi-flight chains
  // (connecting itineraries assembled from the URL) render the rich
  // body for each, separated by a transit caption — close to Angular's
  // `schedule-flight-details-view` for the connecting case.
  const renderBody = (flight: typeof flights[number]) => {
    const buyUrl = buyUrlFor(flight as unknown as ISimpleFlight);
    return (
      <ScheduleFlightBody
        flight={flight as unknown as ISimpleFlight}
        {...(buyUrl ? { buyUrl } : {})}
      />
    );
  };

  return (
    <PageLayout
      headerLeft={
        // Angular's schedule-flight-details-view only renders
        // `<details-back>` in the `header-left` slot — no
        // Онлайн-Табло/Расписание/Карта полетов tab strip. The top
        // "Вернуться к Расписанию" link is the single navigation
        // affordance the details page offers.
        backLink
      }
      title={<h1 className="text--white page-title">{title}</h1>}
      breadcrumbs={breadcrumbs}
      contentLeft={
        miniListCurrentFlight ? (
          // Angular's `schedule-flights-mini-list` renders ONLY the
          // currently-open flight as a single card when
          // `shouldShowAccordion` is false (schedule.length <= 1).
          // Our connecting-itinerary flow never benefits from the
          // multi-day accordion — the day-±1 siblings are visually
          // identical to the open flight and read as duplicates —
          // so we skip siblings entirely and always render the one
          // synthesized MultiLeg card (SU 6188, SU 6341 combined).
          <ScheduleFlightsMiniList
            flights={[]}
            currentFlight={miniListCurrentFlight}
            lang={locale}
          />
        ) : undefined
      }
      stickyContent={
        // TZ §4.1.16.3 R22-R28: day tabs (Schedule window: [-1, +330] from today)
        <DayTabs
          selectedDate={selectedDate}
          availableDates={[]}
          daysBefore={scheduleSearchFrom}
          daysAfter={scheduleSearchTo}
          locale={locale}
          onNavigate={handleNavigateDate}
          mobileCaptionKey="SHARED.FLIGHT_DATE"
        />
      }
    >
      <SeoHead {...seoProps} />
      {/* Summary header — Angular's `<schedule-details-header>` renders
          one `<details-header-badge>` per flight (so a connecting
          itinerary shows BOTH SU 6188 and SU 6341 badges side-by-side),
          a shared share/buy row, and a `<last-update>` line. Followed
          by `<flight-details-full-route>` — the connecting timeline. */}
      {miniListCurrentFlight && (
        <section className="frame schedule-details__summary">
          <div className="schedule-details__summary-row">
            <div className="schedule-details__summary-badges">
              {(flights as unknown as ISimpleFlight[]).map((f) => (
                <DetailsHeaderBadge
                  key={f.id}
                  flight={f}
                  locale={locale}
                  round={miniListCurrentFlight.routeType !== "Direct"}
                />
              ))}
            </div>
            <div className="schedule-details__summary-actions">
              <FlightActions
                flight={miniListCurrentFlight}
                locale={locale}
                showStatus={miniListCurrentFlight.routeType === "Direct"}
                showRegister={false}
                forceBuy
              />
              <LastUpdate flight={miniListCurrentFlight} locale={locale} />
            </div>
          </div>
          {miniListCurrentFlight.routeType !== "Direct" && (
            <FullRouteTimeline
              legs={getLegs(miniListCurrentFlight) as IFlightLeg[]}
              viewType="Schedule"
            />
          )}
        </section>
      )}
      {/* Each flight renders in its OWN .frame section, and the
          Пересадка strip sits between them as a sibling — NOT inside
          a shared frame. That way the dark page background shows
          through the gaps set by `.schedule-details { gap }`, giving
          the three-distinct-blocks layout Angular uses for a
          connecting itinerary. A single shared frame would extend
          white behind the transfer and kill the separation. */}
      <div className="schedule-details" data-testid="schedule-details">
        {flights.map((flight, idx) => {
          const jsonLd = buildScheduleFlightJsonLd(flight);
          const flightNumber = `${flight.flightId.carrier} ${flight.flightId.flightNumber}`;
          const nextFlight = flights[idx + 1];

          const lastLegOfThis = (() => {
            const legs = getLegs(flight) as IFlightLeg[];
            return legs[legs.length - 1];
          })();
          const firstLegOfNext = nextFlight
            ? (() => {
                const legs = getLegs(nextFlight) as IFlightLeg[];
                return legs[0];
              })()
            : undefined;

          return (
            <Fragment key={flight.id}>
              <section
                className="frame schedule-details__flight"
                data-testid={`flight-${flight.id}`}
              >
                <JsonLdRenderer data={jsonLd} />

                <div className="schedule-details__header">
                  <h2 className="schedule-details__flight-number">
                    {flightNumber}
                  </h2>
                  <span className="schedule-details__status">
                    {t(`FLIGHT-STATUSES.${flight.status}`)}
                  </span>
                </div>

                <FlightCard flight={flight} direction="schedule" />
                <IFlyWarning flightNumber={flight.flightId.flightNumber} />
                {renderBody(flight)}
                <ScheduleLegDetails flight={flight as unknown as ISimpleFlight} />

                {/* Angular hides the weekly operating schedule on
                    multi-leg chains; keep it on direct flights. */}
                {flight.routeType === "Direct" && (
                  <FlightSchedule flight={flight as unknown as ISimpleFlight} />
                )}
              </section>

              {firstLegOfNext && lastLegOfThis && (
                <TransferBar
                  leg={lastLegOfThis}
                  nextLeg={firstLegOfNext}
                  viewType="Schedule"
                  isIntermediateLanding={false}
                />
              )}
            </Fragment>
          );
        })}
      </div>
    </PageLayout>
  );
};
