import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
  fireEvent,
  render,
  screen,
  waitFor,
} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import VehicleDetailsModal from "@/components/scooters/VehicleDetailsModal";
import type { ApiEnvelope } from "@/lib/api/types";

vi.mock("@/lib/api", async (importOriginal) => {
  const actual = await importOriginal<typeof import("@/lib/api")>();
  return {
    ...actual,
    apiFetch: vi.fn(),
  };
});

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

interface VehicleDetails {
  id: number | string;
  code: string;
  model: string;
  status: string;
  status_label: string;
  battery_percent: number;
  image: string | null;
  location: string;
  utilization_percent: number;
  currency: string;
  revenue: {
    today: number;
    week: number;
    month: number;
  };
  your_share: {
    today: number;
    month: number;
  };
  rides: {
    today: number;
    total: number;
    avg_duration_min: number;
  };
  recent_rides: Array<{
    amount: number;
    duration_minutes: number;
    ended_at: string;
  }>;
}

function getBackdrop(): HTMLElement {
  return screen.getByRole("button", { name: "إغلاق النافذة" });
}

function getCloseButton(): HTMLElement {
  const icon = document.querySelector(".ri-close-line");
  if (!icon?.parentElement) {
    throw new Error("Close button not found");
  }
  return icon.parentElement as HTMLElement;
}

function buildVehicleDetails(
  overrides: Partial<VehicleDetails> = {},
): VehicleDetails {
  return {
    id: 42,
    code: "SC-001",
    model: "Xiaomi Pro",
    status: "active",
    status_label: "نشطة",
    battery_percent: 75,
    image: "https://example.com/scooter.jpg",
    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: [
      {
        amount: 25,
        duration_minutes: 12,
        ended_at: "2026-01-15 10:00",
      },
    ],
    ...overrides,
  };
}

function mockVehicleSuccess(details: VehicleDetails) {
  vi.mocked(apiFetch).mockImplementation(async (_url, options) => {
    const envelope: ApiEnvelope<VehicleDetails> = {
      key: "success",
      data: details,
      msg: "",
    };
    options?.successCase?.(envelope);
    return envelope;
  });
}

describe("VehicleDetailsModal", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.mocked(apiFetch).mockImplementation(
      () => new Promise(() => undefined),
    );
  });

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

  it("renders nothing when the modal is closed", () => {
    const { container } = render(
      <VehicleDetailsModal isOpen={false} onClose={vi.fn()} vehicleId={42} />,
    );

    expect(container).toBeEmptyDOMElement();
  });

  it("closes on Escape key press on the backdrop", () => {
    const onClose = vi.fn();

    render(<VehicleDetailsModal isOpen onClose={onClose} vehicleId={42} />);

    fireEvent.keyDown(getBackdrop(), { key: "Escape" });

    expect(onClose).toHaveBeenCalledTimes(1);
  });

  it("does not close when a non-Escape key is pressed on the backdrop", () => {
    const onClose = vi.fn();

    render(<VehicleDetailsModal isOpen onClose={onClose} vehicleId={42} />);

    fireEvent.keyDown(getBackdrop(), { key: "Enter" });

    expect(onClose).not.toHaveBeenCalled();
  });

  it("closes when the backdrop itself is clicked", () => {
    const onClose = vi.fn();

    render(<VehicleDetailsModal isOpen onClose={onClose} vehicleId={42} />);

    fireEvent.click(getBackdrop());

    expect(onClose).toHaveBeenCalledTimes(1);
  });

  it("closes when the header close button is clicked", async () => {
    const user = userEvent.setup();
    const onClose = vi.fn();

    render(<VehicleDetailsModal isOpen onClose={onClose} vehicleId={42} />);

    await user.click(getCloseButton());

    expect(onClose).toHaveBeenCalledTimes(1);
  });

  it("does not close when clicking inside modal content", () => {
    const onClose = vi.fn();

    render(<VehicleDetailsModal isOpen onClose={onClose} vehicleId={42} />);

    fireEvent.click(screen.getByText("تفاصيل المركبة"));

    expect(onClose).not.toHaveBeenCalled();
  });

  it("shows a loading state while fetching vehicle details", () => {
    vi.mocked(apiFetch).mockImplementation(
      () => new Promise(() => undefined),
    );

    render(<VehicleDetailsModal isOpen onClose={vi.fn()} vehicleId={42} />);

    expect(document.querySelector(".ri-loader-4-line")).toBeInTheDocument();
  });

  it("loads and displays complete vehicle details", async () => {
    const details = buildVehicleDetails();
    mockVehicleSuccess(details);

    render(<VehicleDetailsModal isOpen onClose={vi.fn()} vehicleId={42} />);

    expect(await screen.findByText(details.code)).toBeInTheDocument();
    expect(screen.getByText(details.model)).toBeInTheDocument();
    expect(screen.getByText(`● ${details.status_label}`)).toBeInTheDocument();
    expect(screen.getByText(details.location)).toBeInTheDocument();
    expect(screen.getByText("75% ⚡")).toBeInTheDocument();
    expect(screen.getByText("65%")).toBeInTheDocument();
    expect(screen.getByText("أحدث الرحلات")).toBeInTheDocument();
    expect(screen.getByText("12 د")).toBeInTheDocument();

    expect(apiFetch).toHaveBeenCalledWith("/provider/vehicles/42", {
      method: "GET",
      successCase: expect.any(Function),
      errorCase: expect.any(Function),
    });
  });

  it("shows a fetch error when vehicle details fail to load", async () => {
    vi.mocked(apiFetch).mockImplementation(async (_url, options) => {
      const envelope: ApiEnvelope = { key: "fail", msg: "تعذر الجلب" };
      options?.errorCase?.(envelope);
      return envelope;
    });

    render(<VehicleDetailsModal isOpen onClose={vi.fn()} vehicleId={42} />);

    expect(await screen.findByText("تعذر الجلب")).toBeInTheDocument();
  });

  it("falls back to a default fetch error message", async () => {
    vi.mocked(apiFetch).mockImplementation(async (_url, options) => {
      const envelope: ApiEnvelope = { key: "fail", msg: "" };
      options?.errorCase?.(envelope);
      return envelope;
    });

    render(<VehicleDetailsModal isOpen onClose={vi.fn()} vehicleId={42} />);

    expect(await screen.findByText("خطأ في جلب تفاصيل المركبة")).toBeInTheDocument();
  });

  it("renders the icon fallback when no vehicle image is provided", async () => {
    mockVehicleSuccess(buildVehicleDetails({ image: null }));

    render(<VehicleDetailsModal isOpen onClose={vi.fn()} vehicleId={42} />);

    await screen.findByText("SC-001");
    expect(document.querySelector(".ri-e-bike-line")).toBeInTheDocument();
    expect(screen.queryByAltText("SC-001")).not.toBeInTheDocument();
  });

  it("shows the icon fallback when the vehicle image fails to load", async () => {
    mockVehicleSuccess(buildVehicleDetails());

    render(<VehicleDetailsModal isOpen onClose={vi.fn()} vehicleId={42} />);

    const image = await screen.findByAltText("SC-001");
    fireEvent.error(image);

    expect(image.style.display).toBe("none");
    const fallback = image.nextElementSibling as HTMLElement;
    expect(fallback.style.display).toBe("flex");
  });

  it("hides the image without throwing when the fallback sibling is missing", async () => {
    mockVehicleSuccess(buildVehicleDetails());

    render(<VehicleDetailsModal isOpen onClose={vi.fn()} vehicleId={42} />);

    const image = await screen.findByAltText("SC-001");
    Object.defineProperty(image, "nextElementSibling", { value: null });
    fireEvent.error(image);

    expect(image.style.display).toBe("none");
    expect(screen.getByText("SC-001")).toBeInTheDocument();
  });

  it.each([
    ["maintenance", "صيانة"],
    ["in_ride", "في رحلة"],
    ["offline", "غير متصلة"],
  ] as const)("renders the %s status label", async (status, statusLabel) => {
    mockVehicleSuccess(
      buildVehicleDetails({ status, status_label: statusLabel }),
    );

    render(<VehicleDetailsModal isOpen onClose={vi.fn()} vehicleId={42} />);

    expect(await screen.findByText(`● ${statusLabel}`)).toBeInTheDocument();
  });

  it.each([
    [60, "bg-[#FCD704]"],
    [30, "bg-yellow-400"],
    [10, "bg-red-500"],
  ] as const)(
    "renders the battery bar for %s percent charge",
    async (batteryPercent, expectedClass) => {
      mockVehicleSuccess(
        buildVehicleDetails({ battery_percent: batteryPercent }),
      );

      render(<VehicleDetailsModal isOpen onClose={vi.fn()} vehicleId={42} />);

      await screen.findByText(`${batteryPercent}% ⚡`);
      const batterySection = screen.getByText("البطارية").closest(".bg-white\\/5");
      const batteryBar = batterySection?.querySelector(".h-full.rounded-full");
      expect(batteryBar?.className).toContain(expectedClass);
    },
  );

  it("omits the recent rides section when there are no rides", async () => {
    mockVehicleSuccess(buildVehicleDetails({ recent_rides: [] }));

    render(<VehicleDetailsModal isOpen onClose={vi.fn()} vehicleId={42} />);

    await screen.findByText("SC-001");
    expect(screen.queryByText("أحدث الرحلات")).not.toBeInTheDocument();
  });

  it("does not fetch details when vehicleId is null", () => {
    render(<VehicleDetailsModal isOpen onClose={vi.fn()} vehicleId={null} />);

    expect(apiFetch).not.toHaveBeenCalled();
    expect(screen.getByText("تفاصيل المركبة")).toBeInTheDocument();
  });

  it("renders the default status styling for unknown vehicle statuses", async () => {
    mockVehicleSuccess(
      buildVehicleDetails({ status: "offline", status_label: "غير متصلة" }),
    );

    render(<VehicleDetailsModal isOpen onClose={vi.fn()} vehicleId={42} />);

    const badge = await screen.findByText("● غير متصلة");
    expect(badge.className).toContain("text-gray-400");
  });

  it("refetches details when the modal reopens with a different vehicle id", async () => {
    mockVehicleSuccess(buildVehicleDetails({ id: 1, code: "SC-001" }));

    const { rerender } = render(
      <VehicleDetailsModal isOpen onClose={vi.fn()} vehicleId={1} />,
    );

    await screen.findByText("SC-001");

    mockVehicleSuccess(buildVehicleDetails({ id: 2, code: "SC-002" }));
    rerender(<VehicleDetailsModal isOpen onClose={vi.fn()} vehicleId={2} />);

    expect(await screen.findByText("SC-002")).toBeInTheDocument();
    expect(apiFetch).toHaveBeenCalledTimes(2);
  });

  it("ignores a success response without data", async () => {
    vi.mocked(apiFetch).mockImplementation(async (_url, options) => {
      const envelope = { key: "success", msg: "" } satisfies ApiEnvelope;
      options?.successCase?.(envelope);
      return envelope;
    });

    render(<VehicleDetailsModal isOpen onClose={vi.fn()} vehicleId={42} />);

    await waitFor(() => {
      expect(apiFetch).toHaveBeenCalled();
    });
    expect(screen.queryByText("SC-001")).not.toBeInTheDocument();
  });

  it("ignores stale fetch callbacks after the modal unmounts", async () => {
    let resolveFetch!: (value: ApiEnvelope<VehicleDetails>) => void;

    vi.mocked(apiFetch).mockImplementation((_url, options) => {
      return new Promise<ApiEnvelope<VehicleDetails>>((resolve) => {
        resolveFetch = (envelope) => {
          if (envelope.key === "fail") {
            options?.errorCase?.(envelope);
          } else {
            options?.successCase?.(envelope);
          }
          resolve(envelope);
        };
      });
    });

    const { unmount } = render(
      <VehicleDetailsModal isOpen onClose={vi.fn()} vehicleId={42} />,
    );

    await waitFor(() => expect(apiFetch).toHaveBeenCalled());
    unmount();
    resolveFetch({ key: "fail", msg: "متأخر" });

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