import { describe, expect, it, beforeEach, vi, afterEach } from "vitest";
import {
  setBoardFilter, getBoardFilter,
  setScheduleFilter, getScheduleFilter,
  setMapFilter, getMapFilter,
  projectBoardToSchedule, projectScheduleToBoard,
  resetCrossSectionStore,
  type BoardFilterSnapshot, type ScheduleFilterSnapshot, type MapFilterSnapshot,
} from "./crossSectionNavigation.js";

beforeEach(() => {
  resetCrossSectionStore();
  vi.useFakeTimers();
  vi.setSystemTime(new Date(2026, 4, 15, 12, 0, 0)); // Fri 2026-05-15
});
afterEach(() => vi.useRealTimers());

describe("4.1.8: store + retrieval", () => {
  it("stores and retrieves board filter", () => {
    const snap: BoardFilterSnapshot = {
      mode: "route", departure: "MOW", arrival: "LED",
      date: "20260515", timeFrom: "0000", timeTo: "2400",
      searchExecuted: true,
    };
    setBoardFilter(snap);
    expect(getBoardFilter()).toEqual(snap);
  });

  it("stores and retrieves schedule filter", () => {
    const snap: ScheduleFilterSnapshot = {
      mode: "route", departure: "MOW", arrival: "LED",
      dateFrom: "20260511", dateTo: "20260517",
      timeFrom: "0600", timeTo: "2200",
      onlyDirect: false, showReturn: false,
      searchExecuted: true,
    };
    setScheduleFilter(snap);
    expect(getScheduleFilter()).toEqual(snap);
  });

  it("stores and retrieves map filter", () => {
    const snap: MapFilterSnapshot = {
      departure: "MOW", arrival: "LED", date: "20260515",
      showInternal: true, showInternational: true, showTransfers: false,
    };
    setMapFilter(snap);
    expect(getMapFilter()).toEqual(snap);
  });

  it("getBoardFilter returns null before any set", () => {
    expect(getBoardFilter()).toBeNull();
  });

  it("getScheduleFilter returns null before any set", () => {
    expect(getScheduleFilter()).toBeNull();
  });

  it("getMapFilter returns null before any set", () => {
    expect(getMapFilter()).toBeNull();
  });

  it("resetCrossSectionStore clears all three", () => {
    setBoardFilter({
      mode: "route", departure: "MOW", arrival: "LED",
      date: "20260515", timeFrom: "0000", timeTo: "2400",
      searchExecuted: true,
    });
    setMapFilter({
      departure: "MOW", arrival: null, date: "20260515",
      showInternal: true, showInternational: true, showTransfers: false,
    });
    setScheduleFilter({
      mode: "route", departure: "MOW", arrival: "LED",
      dateFrom: "20260511", dateTo: "20260517",
      timeFrom: "0000", timeTo: "2400",
      onlyDirect: false, showReturn: false,
      searchExecuted: false,
    });
    resetCrossSectionStore();
    expect(getBoardFilter()).toBeNull();
    expect(getScheduleFilter()).toBeNull();
    expect(getMapFilter()).toBeNull();
  });

  it("overwrites previous board filter with new value", () => {
    const snap1: BoardFilterSnapshot = {
      mode: "route", departure: "MOW", arrival: "LED",
      date: "20260515", timeFrom: "0000", timeTo: "2400",
      searchExecuted: false,
    };
    const snap2: BoardFilterSnapshot = {
      mode: "flight-number", flightNumber: "1234",
      date: "20260516", timeFrom: "0600", timeTo: "2200",
      searchExecuted: true,
    };
    setBoardFilter(snap1);
    setBoardFilter(snap2);
    expect(getBoardFilter()).toEqual(snap2);
  });
});

describe("4.1.8-R1/R2: projectBoardToSchedule", () => {
  it("carries departure + arrival", () => {
    const board: BoardFilterSnapshot = {
      mode: "route", departure: "MOW", arrival: "LED",
      date: "20260515", timeFrom: "0600", timeTo: "2200",
      searchExecuted: true,
    };
    const p = projectBoardToSchedule(board);
    expect(p.departure).toBe("MOW");
    expect(p.arrival).toBe("LED");
  });

  it("sets dateFrom/dateTo to current week (Mon-Sun of board.date's week)", () => {
    const board: BoardFilterSnapshot = {
      mode: "route", departure: "MOW", arrival: "LED",
      date: "20260515", // Friday
      timeFrom: "0600", timeTo: "2200", searchExecuted: true,
    };
    const p = projectBoardToSchedule(board);
    // Week containing 2026-05-15 (Fri) = 2026-05-11 (Mon) to 2026-05-17 (Sun)
    expect(p.dateFrom).toBe("20260511");
    expect(p.dateTo).toBe("20260517");
  });

  it("clears time/toggles to Schedule defaults", () => {
    const p = projectBoardToSchedule({
      mode: "route", departure: "MOW", arrival: "LED",
      date: "20260515", timeFrom: "0600", timeTo: "2200",
      searchExecuted: true,
    });
    expect(p.timeFrom).toBe("0000");
    expect(p.timeTo).toBe("2400");
    expect(p.onlyDirect).toBe(false);
    expect(p.showReturn).toBe(false);
    expect(p.searchExecuted).toBe(false);
  });

  it("uses current date for week bounds when board.date is malformed", () => {
    const p = projectBoardToSchedule({
      mode: "route", departure: "MOW", arrival: "LED",
      date: "", timeFrom: "0000", timeTo: "2400", searchExecuted: false,
    });
    // Clock frozen at 2026-05-15 (Fri), so week = 2026-05-11 to 2026-05-17
    expect(p.dateFrom).toBe("20260511");
    expect(p.dateTo).toBe("20260517");
  });

  it("does not carry optional cities when board has only departure", () => {
    const p = projectBoardToSchedule({
      mode: "departure", departure: "MOW",
      date: "20260515", timeFrom: "0000", timeTo: "2400", searchExecuted: false,
    });
    expect(p.departure).toBe("MOW");
    expect(p.arrival).toBeUndefined();
  });

  it("does not carry optional cities when board has only arrival", () => {
    const p = projectBoardToSchedule({
      mode: "arrival", arrival: "LED",
      date: "20260515", timeFrom: "0000", timeTo: "2400", searchExecuted: false,
    });
    expect(p.departure).toBeUndefined();
    expect(p.arrival).toBe("LED");
  });

  it("week bounds for a Monday give Mon=Mon+6=Sun", () => {
    // 2026-05-11 is a Monday
    const p = projectBoardToSchedule({
      mode: "route", departure: "MOW", arrival: "LED",
      date: "20260511", timeFrom: "0000", timeTo: "2400", searchExecuted: false,
    });
    expect(p.dateFrom).toBe("20260511");
    expect(p.dateTo).toBe("20260517");
  });

  it("week bounds for a Sunday give Mon-6..Sun", () => {
    // 2026-05-17 is a Sunday
    const p = projectBoardToSchedule({
      mode: "route", departure: "MOW", arrival: "LED",
      date: "20260517", timeFrom: "0000", timeTo: "2400", searchExecuted: false,
    });
    expect(p.dateFrom).toBe("20260511");
    expect(p.dateTo).toBe("20260517");
  });
});

describe("4.1.8-R1/R2: projectScheduleToBoard", () => {
  it("carries departure + arrival, preserves in-window dateFrom, clears time", () => {
    // Clock frozen at 2026-05-15. dateFrom = 2026-05-14 (yesterday, within Board window [-1,+14])
    const sched: ScheduleFilterSnapshot = {
      mode: "route", departure: "MOW", arrival: "LED",
      dateFrom: "20260514", dateTo: "20260520",
      timeFrom: "0600", timeTo: "2200",
      onlyDirect: true, showReturn: false,
      searchExecuted: true,
    };
    const p = projectScheduleToBoard(sched);
    expect(p.departure).toBe("MOW");
    expect(p.arrival).toBe("LED");
    expect(p.date).toBe("20260514"); // in-window → preserved
    expect(p.timeFrom).toBe("0000");
    expect(p.timeTo).toBe("2400");
    expect(p.searchExecuted).toBe(false);
  });

  it("4.1.8-R2: preserves dateFrom when schedule dateFrom is today (in-window)", () => {
    // Clock frozen at 2026-05-15. dateFrom = today is definitely in window.
    const sched: ScheduleFilterSnapshot = {
      mode: "route", departure: "MOW", arrival: "LED",
      dateFrom: "20260515", dateTo: "20260521",
      timeFrom: "0000", timeTo: "2400",
      onlyDirect: false, showReturn: false,
      searchExecuted: true,
    };
    const p = projectScheduleToBoard(sched);
    expect(p.date).toBe("20260515"); // in-window → preserved (also equals today)
  });

  it("4.1.8-R2: preserves dateFrom when schedule dateFrom is within +14 days", () => {
    // Clock frozen at 2026-05-15. +7 days = 2026-05-22, within the +14 window.
    const sched: ScheduleFilterSnapshot = {
      mode: "route", departure: "MOW", arrival: "LED",
      dateFrom: "20260522", dateTo: "20260528",
      timeFrom: "0000", timeTo: "2400",
      onlyDirect: false, showReturn: false,
      searchExecuted: true,
    };
    const p = projectScheduleToBoard(sched);
    expect(p.date).toBe("20260522"); // in-window → preserved
  });

  it("does not carry optional departure when schedule has only arrival", () => {
    const p = projectScheduleToBoard({
      mode: "route", arrival: "LED",
      dateFrom: "20260511", dateTo: "20260517",
      timeFrom: "0000", timeTo: "2400",
      onlyDirect: false, showReturn: false,
      searchExecuted: false,
    });
    expect(p.departure).toBeUndefined();
    expect(p.arrival).toBe("LED");
  });

  it("sets mode to route regardless of schedule mode", () => {
    const p = projectScheduleToBoard({
      mode: "route", departure: "MOW", arrival: "LED",
      dateFrom: "20260511", dateTo: "20260517",
      timeFrom: "0000", timeTo: "2400",
      onlyDirect: false, showReturn: false,
      searchExecuted: false,
    });
    expect(p.mode).toBe("route");
  });

  it("4.1.8-R2: projectScheduleToBoard clamps to today when schedule dateFrom is outside board window (future)", () => {
    // Clock frozen at 2026-05-15. Schedule dateFrom = 2026-07-01 (47 days forward, outside board's +14 window)
    const schedule: ScheduleFilterSnapshot = {
      mode: "route",
      departure: "MOW",
      arrival: "LED",
      dateFrom: "20260701",
      dateTo: "20260707",
      timeFrom: "0000",
      timeTo: "2400",
      onlyDirect: false,
      showReturn: false,
      searchExecuted: true,
    };
    const projected = projectScheduleToBoard(schedule);
    expect(projected.date).toBe("20260515"); // clamps to today
  });

  it("4.1.8-R2: projectScheduleToBoard clamps to today when schedule dateFrom is outside board window (past)", () => {
    // Schedule dateFrom in the distant past, outside the -1 day board window
    const schedule: ScheduleFilterSnapshot = {
      mode: "route",
      departure: "MOW",
      arrival: "LED",
      dateFrom: "20250101",
      dateTo: "20250107",
      timeFrom: "0000",
      timeTo: "2400",
      onlyDirect: false,
      showReturn: false,
      searchExecuted: true,
    };
    const projected = projectScheduleToBoard(schedule);
    expect(projected.date).toBe("20260515"); // clamps to today
  });
});

describe("4.1.1-R26 / 4.1.8-R3: map filter is independent of board/schedule", () => {
  it("setBoardFilter does not affect map", () => {
    const mapSnap: MapFilterSnapshot = {
      departure: "MOW", arrival: null, date: "20260515",
      showInternal: true, showInternational: true, showTransfers: false,
    };
    setMapFilter(mapSnap);
    setBoardFilter({
      mode: "route", departure: "LED", arrival: "KGD",
      date: "20260601", timeFrom: "0000", timeTo: "2400",
      searchExecuted: false,
    });
    expect(getMapFilter()).toEqual(mapSnap);
  });

  it("setScheduleFilter does not affect map", () => {
    const mapSnap: MapFilterSnapshot = {
      departure: "SVO", arrival: "LED", date: "20260601",
      showInternal: false, showInternational: true, showTransfers: true,
    };
    setMapFilter(mapSnap);
    setScheduleFilter({
      mode: "route", departure: "KZN", arrival: "AER",
      dateFrom: "20260601", dateTo: "20260607",
      timeFrom: "0000", timeTo: "2400",
      onlyDirect: false, showReturn: false,
      searchExecuted: true,
    });
    expect(getMapFilter()).toEqual(mapSnap);
  });

  it("setMapFilter does not affect board", () => {
    const boardSnap: BoardFilterSnapshot = {
      mode: "route", departure: "MOW", arrival: "LED",
      date: "20260515", timeFrom: "0000", timeTo: "2400",
      searchExecuted: false,
    };
    setBoardFilter(boardSnap);
    setMapFilter({
      departure: "SVO", arrival: null, date: null,
      showInternal: false, showInternational: false, showTransfers: false,
    });
    expect(getBoardFilter()).toEqual(boardSnap);
  });

  it("setMapFilter does not affect schedule", () => {
    const schedSnap: ScheduleFilterSnapshot = {
      mode: "route", departure: "MOW", arrival: "LED",
      dateFrom: "20260511", dateTo: "20260517",
      timeFrom: "0000", timeTo: "2400",
      onlyDirect: true, showReturn: false,
      searchExecuted: false,
    };
    setScheduleFilter(schedSnap);
    setMapFilter({
      departure: "SVO", arrival: null, date: null,
      showInternal: false, showInternational: false, showTransfers: false,
    });
    expect(getScheduleFilter()).toEqual(schedSnap);
  });
});
