import { beforeEach, describe, expect, it, vi } from "vitest";
import { render, screen, waitFor, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import ScootersPage, { type VehiclesData } from "@/app/scooters/page";
import type { Scooter } from "@/components/scooters/ScooterCard";
import type { ApiEnvelope } from "@/lib/api/types";

vi.mock("@/components/DashboardLayout", () => ({
  default: ({
    children,
    title,
  }: {
    children: React.ReactNode;
    title: string;
  }) => (
    <div>
      <h1>{title}</h1>
      {children}
    </div>
  ),
}));

vi.mock("@/lib/api", () => ({
  apiFetch: vi.fn(),
  formatApiMsg: vi.fn((msg: string) => msg),
}));

import { apiFetch, formatApiMsg } from "@/lib/api";

function buildScooter(overrides: Partial<Scooter> = {}): Scooter {
  return {
    id: 1,
    code: "SC-001",
    model: "Xiaomi Pro",
    status: "active",
    status_label: "نشطة",
    battery_percent: 75,
    image: null,
    location: "الرياض",
    rides_today: 5,
    revenue_today: 200,
    your_share_today: 50,
    currency: "SAR",
    ...overrides,
  };
}

function buildVehiclesData(
  overrides: Partial<VehiclesData> = {},
): VehiclesData {
  return {
    summary: {
      total: 2,
      active: 1,
      maintenance: 0,
      in_ride: 1,
      inactive: 0,
    },
    vehicles: [
      buildScooter({ id: 1, code: "SC-001", status: "active", status_label: "نشطة" }),
      buildScooter({
        id: 2,
        code: "SC-002",
        status: "in_ride",
        status_label: "في رحلة",
      }),
    ],
    ...overrides,
  };
}

function mockVehiclesResponses({
  data = buildVehiclesData(),
  error,
}: {
  data?: VehiclesData;
  error?: string;
} = {}) {
  vi.mocked(apiFetch).mockImplementation(async (url, options) => {
    if (
      typeof url === "string" &&
      url.match(/\/provider\/vehicles\/[^/?]+$/)
    ) {
      const vehicleId = url.split("/").pop();
      const envelope = {
        key: "success",
        data: {
          id: vehicleId ?? 1,
          code: "SC-001",
          model: "Xiaomi Pro",
          status: "active",
          status_label: "نشطة",
          battery_percent: 75,
          image: null,
          location: "الرياض",
          utilization_percent: 65,
          currency: "SAR",
          revenue: { today: 120, week: 840, month: 3200 },
          your_share: { today: 22, month: 576 },
          rides: { today: 5, total: 120, avg_duration_min: 15 },
          recent_rides: [],
        },
        msg: "",
      } satisfies ApiEnvelope;
      options?.successCase?.(envelope);
      return envelope;
    }

    if (typeof url === "string" && url.includes("/provider/vehicles")) {
      if (error) {
        const envelope = {
          key: "fail",
          msg: error,
        } satisfies ApiEnvelope;
        options?.errorCase?.(envelope);
        return envelope;
      }

      const envelope = {
        key: "success",
        data,
        msg: "",
      } satisfies ApiEnvelope<VehiclesData>;
      options?.successCase?.(envelope);
      return envelope;
    }

    return { key: "success", msg: "" };
  });
}

describe("ScootersPage", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("renders the page title and loading skeletons on initial fetch", () => {
    vi.mocked(apiFetch).mockImplementation(() => new Promise(() => {}));

    const { container } = render(<ScootersPage />);

    expect(
      screen.getByRole("heading", { name: "مركباتي" }),
    ).toBeInTheDocument();
    expect(container.querySelectorAll(".animate-pulse").length).toBeGreaterThan(0);
    expect(apiFetch).toHaveBeenCalledWith("/provider/vehicles", {
      method: "GET",
      successCase: expect.any(Function),
      errorCase: expect.any(Function),
    });
  });

  it("renders scooter cards and summary counts on success", async () => {
    mockVehiclesResponses();

    render(<ScootersPage />);

    expect(
      await screen.findByRole("heading", { name: "مركباتي (2)" }),
    ).toBeInTheDocument();
    expect(screen.getByText("SC-001")).toBeInTheDocument();
    expect(screen.getByText("SC-002")).toBeInTheDocument();
    const filterBar = screen.getByRole("heading", { name: "مركباتي (2)" })
      .parentElement!;
    const filterButtons = within(filterBar).getAllByRole("button");
    expect(filterButtons[0]).toHaveTextContent("الكل2");
    expect(filterButtons[1]).toHaveTextContent("متاح1");
    expect(filterButtons[2]).toHaveTextContent("في رحلة1");
  });

  it("shows the error state when the vehicles request fails", async () => {
    mockVehiclesResponses({ error: "تعذّر تحميل المركبات" });

    render(<ScootersPage />);

    expect(
      await screen.findByText("تعذّر تحميل المركبات"),
    ).toBeInTheDocument();
  });

  it("uses the default error when the API message cannot be formatted", async () => {
    vi.mocked(formatApiMsg).mockReturnValue("");
    mockVehiclesResponses({ error: "رسالة خلفية" });

    render(<ScootersPage />);

    expect(
      await screen.findByText("تعذّر تحميل المركبات، حاول مرة أخرى"),
    ).toBeInTheDocument();
  });

  it("shows the empty state when no vehicles match the filter", async () => {
    mockVehiclesResponses({
      data: buildVehiclesData({
        summary: {
          total: 0,
          active: 0,
          maintenance: 0,
          in_ride: 0,
          inactive: 0,
        },
        vehicles: [],
      }),
    });

    render(<ScootersPage />);

    expect(
      await screen.findByText("لا توجد مركبات تطابق هذا الفلتر"),
    ).toBeInTheDocument();
  });

  it("refetches vehicles when a status filter tab is selected", async () => {
    const user = userEvent.setup();
    mockVehiclesResponses();

    render(<ScootersPage />);
    await screen.findByText("SC-001");

    mockVehiclesResponses({
      data: buildVehiclesData({
        vehicles: [buildScooter({ id: 3, code: "SC-003", status: "active" })],
        summary: {
          total: 1,
          active: 1,
          maintenance: 0,
          in_ride: 0,
          inactive: 0,
        },
      }),
    });

    await user.click(screen.getByRole("button", { name: /متاح/ }));

    await waitFor(() => {
      expect(apiFetch).toHaveBeenLastCalledWith(
        "/provider/vehicles?status=active",
        expect.objectContaining({ method: "GET" }),
      );
    });
    expect(await screen.findByText("SC-003")).toBeInTheDocument();
  });

  it("opens the vehicle details modal when a scooter card is clicked", async () => {
    const user = userEvent.setup();
    mockVehiclesResponses();

    render(<ScootersPage />);
    await screen.findByText("SC-001");

    const scooterButtons = screen.getAllByRole("button");
    const scooterCard = scooterButtons.find((button) =>
      button.textContent?.includes("SC-001"),
    );
    expect(scooterCard).toBeDefined();
    await user.click(scooterCard!);

    expect(await screen.findByText("تفاصيل المركبة")).toBeInTheDocument();
    await waitFor(() => {
      expect(apiFetch).toHaveBeenCalledWith("/provider/vehicles/1", {
        method: "GET",
        successCase: expect.any(Function),
        errorCase: expect.any(Function),
      });
    });
  });

  it("clears the selected vehicle when its details modal closes", async () => {
    const user = userEvent.setup();
    mockVehiclesResponses();
    render(<ScootersPage />);
    await screen.findByText("SC-001");

    const scooterCard = screen
      .getAllByRole("button")
      .find((button) => button.textContent?.includes("SC-001"));
    await user.click(scooterCard!);
    expect(await screen.findByText("تفاصيل المركبة")).toBeInTheDocument();

    await user.click(screen.getByLabelText("إغلاق النافذة"));
    expect(screen.queryByText("تفاصيل المركبة")).not.toBeInTheDocument();
  });

  it("renders all vehicles when pagination metadata is present but no controls exist", async () => {
    mockVehiclesResponses({
      data: buildVehiclesData({
        pagination: {
          current_page: 1,
          last_page: 3,
          per_page: 2,
          total: 6,
        },
      }),
    });

    render(<ScootersPage />);

    expect(await screen.findByText("SC-001")).toBeInTheDocument();
    expect(screen.getByText("SC-002")).toBeInTheDocument();
    expect(screen.queryByRole("button", { name: "2" })).not.toBeInTheDocument();
    expect(screen.queryByRole("button", { name: "3" })).not.toBeInTheDocument();
  });

  it("ignores stale vehicle callbacks after unmount", async () => {
    let resolveFetch!: (envelope: ApiEnvelope<VehiclesData>) => void;
    let successCase!: (envelope: ApiEnvelope<VehiclesData>) => void;
    let errorCase!: (envelope: ApiEnvelope) => void;
    vi.mocked(apiFetch).mockImplementation((_url, options) =>
      new Promise((resolve) => {
        resolveFetch = resolve;
        successCase = options?.successCase as (envelope: ApiEnvelope<VehiclesData>) => void;
        errorCase = options?.errorCase as (envelope: ApiEnvelope) => void;
      }),
    );

    const { unmount } = render(<ScootersPage />);
    await waitFor(() => expect(successCase).toBeDefined());
    unmount();
    successCase({ key: "success", data: buildVehiclesData() });
    errorCase({ key: "fail", msg: "خطأ متأخر" });
    resolveFetch({ key: "success" });

    expect(screen.queryByText("خطأ متأخر")).not.toBeInTheDocument();
  });
});
