// @vitest-environment jsdom
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { render, screen, fireEvent } from "@testing-library/react";
import { DayTabs } from "./DayTabs.js";

describe("DayTabs", () => {
  const FIXED_TODAY = new Date(2026, 3, 16); // Apr 16 2026

  beforeEach(() => {
    vi.useFakeTimers();
    vi.setSystemTime(FIXED_TODAY);
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  function allDatesBetween(from: Date, to: Date): string[] {
    const out: string[] = [];
    const cur = new Date(from);
    while (cur <= to) {
      const y = cur.getFullYear().toString();
      const m = (cur.getMonth() + 1).toString().padStart(2, "0");
      const d = cur.getDate().toString().padStart(2, "0");
      out.push(`${y}${m}${d}`);
      cur.setDate(cur.getDate() + 1);
    }
    return out;
  }

  it("renders container with data-testid", () => {
    render(
      <DayTabs
        selectedDate="20260416"
        availableDates={["20260416"]}
        daysBefore={2}
        daysAfter={14}
        locale="en"
        onNavigate={() => {}}
      />,
    );
    expect(screen.getByTestId("day-tabs")).toBeTruthy();
  });

  it("renders 7 tabs on first page", () => {
    render(
      <DayTabs
        selectedDate="20260414"
        availableDates={allDatesBetween(new Date(2026, 3, 14), new Date(2026, 3, 30))}
        daysBefore={2}
        daysAfter={14}
        locale="en"
        onNavigate={() => {}}
      />,
    );
    const tabs = screen.getAllByTestId(/^day-tab-\d{8}$/);
    expect(tabs).toHaveLength(7);
  });

  it("disables tabs not in availableDates", () => {
    render(
      <DayTabs
        selectedDate="20260414"
        availableDates={["20260415"]}
        daysBefore={2}
        daysAfter={4}
        locale="en"
        onNavigate={() => {}}
      />,
    );
    const unavailable = screen.getByTestId("day-tab-20260414") as HTMLButtonElement;
    const available = screen.getByTestId("day-tab-20260415") as HTMLButtonElement;
    expect(unavailable.disabled).toBe(true);
    expect(available.disabled).toBe(false);
  });

  it("prev arrow disabled on first page", () => {
    render(
      <DayTabs
        selectedDate="20260414"
        availableDates={["20260414"]}
        daysBefore={2}
        daysAfter={14}
        locale="en"
        onNavigate={() => {}}
      />,
    );
    const prev = screen.getByTestId("day-tabs-prev") as HTMLButtonElement;
    expect(prev.disabled).toBe(true);
  });

  it("next arrow changes page", () => {
    render(
      <DayTabs
        selectedDate="20260414"
        availableDates={allDatesBetween(new Date(2026, 3, 14), new Date(2026, 3, 30))}
        daysBefore={2}
        daysAfter={14}
        locale="en"
        onNavigate={() => {}}
      />,
    );
    expect(screen.queryByTestId("day-tab-20260421")).toBeNull();

    fireEvent.click(screen.getByTestId("day-tabs-next"));

    expect(screen.getByTestId("day-tab-20260421")).toBeTruthy();
    expect(screen.queryByTestId("day-tab-20260414")).toBeNull();
  });

  it("clicking an available tab calls onNavigate", () => {
    const onNavigate = vi.fn();
    render(
      <DayTabs
        selectedDate="20260416"
        availableDates={["20260415", "20260416", "20260417"]}
        daysBefore={2}
        daysAfter={4}
        locale="en"
        onNavigate={onNavigate}
      />,
    );
    fireEvent.click(screen.getByTestId("day-tab-20260417"));
    expect(onNavigate).toHaveBeenCalledWith("20260417");
  });

  it("initializes currentPage to the page containing selectedDate", () => {
    const available = allDatesBetween(new Date(2026, 3, 14), new Date(2026, 3, 30));
    render(
      <DayTabs
        selectedDate="20260425"
        availableDates={available}
        daysBefore={2}
        daysAfter={14}
        locale="en"
        onNavigate={() => {}}
      />,
    );
    // 20260425 is on page 1 (0-indexed) of pages [14..20][21..27][28..30]
    expect(screen.getByTestId("day-tab-20260425")).toBeTruthy();
    expect(screen.queryByTestId("day-tab-20260414")).toBeNull();
  });

  it("renders DaySelect for mobile", () => {
    render(
      <DayTabs
        selectedDate="20260416"
        availableDates={["20260416"]}
        daysBefore={2}
        daysAfter={14}
        locale="en"
        onNavigate={() => {}}
      />,
    );
    expect(screen.getByTestId("day-select")).toBeTruthy();
  });

  // ── TZ §4.1.13.1 compliance tests ──────────────────────────────────────

  it("4.1.13.1: active range today-1 to today+14 yields 16 total dates", () => {
    // daysBefore=1, daysAfter=14 → 1+1+14 = 16 dates total (3 pages: 7+7+2)
    render(
      <DayTabs
        selectedDate="20260415"
        availableDates={allDatesBetween(new Date(2026, 3, 15), new Date(2026, 3, 30))}
        daysBefore={1}
        daysAfter={14}
        locale="en"
        onNavigate={() => {}}
      />,
    );
    // First page shows today-1 (20260415) through 20260421
    expect(screen.getByTestId("day-tab-20260415")).toBeTruthy();
    expect(screen.getByTestId("day-tab-20260421")).toBeTruthy();
    // today-2 (20260414) is not in the range
    expect(screen.queryByTestId("day-tab-20260414")).toBeNull();
  });

  it("4.1.13.1: next arrow enables and disables correctly across pages", () => {
    render(
      <DayTabs
        selectedDate="20260415"
        availableDates={allDatesBetween(new Date(2026, 3, 15), new Date(2026, 3, 30))}
        daysBefore={1}
        daysAfter={14}
        locale="en"
        onNavigate={() => {}}
      />,
    );
    const next = screen.getByTestId("day-tabs-next") as HTMLButtonElement;
    expect(next.disabled).toBe(false); // page 0 of 3

    fireEvent.click(next); // go to page 1
    expect(next.disabled).toBe(false); // still more pages

    fireEvent.click(next); // go to page 2 (last, with padding)
    expect(next.disabled).toBe(true); // on last page → disabled
  });

  it("4.1.13.1: last page is padded to 7 slots with disabled out-of-range tabs", () => {
    // daysBefore=1, daysAfter=14 → 16 in-range dates → 3 pages (7 + 7 + 2).
    // Last page has 2 in-range tabs + 5 out-of-range tabs (greyed-out so the
    // user can see where the +N boundary lies, matches Angular).
    render(
      <DayTabs
        selectedDate="20260415"
        availableDates={allDatesBetween(new Date(2026, 3, 15), new Date(2026, 3, 30))}
        daysBefore={1}
        daysAfter={14}
        locale="en"
        onNavigate={() => {}}
      />,
    );
    const next = screen.getByTestId("day-tabs-next") as HTMLButtonElement;
    fireEvent.click(next); // page 1
    fireEvent.click(next); // page 2 (last)

    const allTabs = screen.getAllByTestId(/^day-tab-\d{8}$/) as HTMLButtonElement[];
    expect(allTabs).toHaveLength(7);
    const enabled = allTabs.filter((t) => !t.disabled);
    const disabled = allTabs.filter((t) => t.disabled);
    expect(enabled).toHaveLength(2);
    expect(disabled).toHaveLength(5);
  });

  it("4.1.13.1: right-arrow disabled on last page even when padded", () => {
    render(
      <DayTabs
        selectedDate="20260415"
        availableDates={allDatesBetween(new Date(2026, 3, 15), new Date(2026, 3, 30))}
        daysBefore={1}
        daysAfter={14}
        locale="en"
        onNavigate={() => {}}
      />,
    );
    const next = screen.getByTestId("day-tabs-next") as HTMLButtonElement;
    fireEvent.click(next);
    fireEvent.click(next); // now on last (padded) page
    expect(next.disabled).toBe(true);
  });

  it("4.1.13.1: full last page (no short tail) has exactly 7 in-range tabs", () => {
    // daysBefore=0, daysAfter=6 → 7 dates → 1 page, no padding needed
    render(
      <DayTabs
        selectedDate="20260416"
        availableDates={["20260416"]}
        daysBefore={0}
        daysAfter={6}
        locale="en"
        onNavigate={() => {}}
      />,
    );
    const tabs = screen.getAllByTestId(/^day-tab-\d{8}$/);
    expect(tabs).toHaveLength(7);
  });

  it("4.1.13.1: selected tab has aria-current=date for highlight", () => {
    render(
      <DayTabs
        selectedDate="20260416"
        availableDates={["20260415", "20260416", "20260417"]}
        daysBefore={1}
        daysAfter={5}
        locale="en"
        onNavigate={() => {}}
      />,
    );
    const activeTab = screen.getByTestId("day-tab-20260416");
    expect(activeTab.getAttribute("aria-current")).toBe("date");

    const inactiveTab = screen.getByTestId("day-tab-20260415");
    expect(inactiveTab.getAttribute("aria-current")).toBeNull();
  });

  it("4.1.13.1: selected tab does NOT have aria-current when not active", () => {
    render(
      <DayTabs
        selectedDate="20260416"
        availableDates={["20260415", "20260416", "20260417"]}
        daysBefore={1}
        daysAfter={5}
        locale="en"
        onNavigate={() => {}}
      />,
    );
    // Sibling tabs should not have aria-current
    expect(screen.getByTestId("day-tab-20260415").getAttribute("aria-current")).toBeNull();
    expect(screen.getByTestId("day-tab-20260417").getAttribute("aria-current")).toBeNull();
  });

  it("4.1.13.1: out-of-range padding tabs are disabled and not navigable", () => {
    const onNavigate = vi.fn();
    render(
      <DayTabs
        selectedDate="20260415"
        availableDates={allDatesBetween(new Date(2026, 3, 15), new Date(2026, 3, 30))}
        daysBefore={1}
        daysAfter={14}
        locale="en"
        onNavigate={onNavigate}
      />,
    );
    const next = screen.getByTestId("day-tabs-next") as HTMLButtonElement;
    fireEvent.click(next);
    fireEvent.click(next); // last page

    // First out-of-range tab follows the last in-range date (20260430).
    const pad = screen.getByTestId("day-tab-20260501") as HTMLButtonElement;
    expect(pad.disabled).toBe(true);
    fireEvent.click(pad);
    expect(onNavigate).not.toHaveBeenCalled();
  });
});
