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 EarningsPage from "@/app/earnings/page";
import type { ApiEnvelope } from "@/lib/api/types";
import type { EarningTransactionsData } from "@/lib/transactions";
import type { EarningsData } from "@/app/earnings/page";

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

vi.mock("@/components/earnings/MonthlyCompareChart", () => ({
  default: ({ data }: { data: { labels: string[] } }) => (
    <div data-testid="compare-chart">{data.labels.join(",")}</div>
  ),
}));

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

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

function buildEarningsData(
  overrides: Partial<EarningsData> = {},
): EarningsData {
  return {
    currency: "SAR",
    profit_share_percent: 25,
    kpis: {
      profit_share_percent: 25,
      avg_daily_profit_30d: 120,
      profit_this_month: {
        value: 3000,
        label: "ربح هذا الشهر",
        change: {
          formatted: "+12%",
          trend: "up",
          label: "عن الشهر الماضي",
        },
      },
      total_profit: 12000,
    },
    yearly_comparison: {
      labels: ["يناير", "فبراير"],
      current_year: [1000, 1500],
      previous_year: [900, 1100],
    },
    monthly_earnings_chart: [
      {
        month: "2026-01",
        label: "يناير 2026",
        revenue: 4000,
        profit: 1000,
        is_current: true,
      },
      {
        month: "2025-12",
        label: "ديسمبر 2025",
        revenue: 3000,
        profit: 500,
        is_current: false,
      },
    ],
    ...overrides,
  };
}

function buildTransactionsData(): EarningTransactionsData {
  return {
    currency: "SAR",
    profit_share_percent: 25,
    total_monthly_share: 750,
    vehicles: [
      {
        scooter_id: 1,
        code: "SC-001",
        status: "active",
        status_label: "نشطة",
        area: "الرياض",
        daily_revenue: 100,
        monthly_revenue: 2000,
        monthly_share: 500,
        rides_today: 4,
        utilization_percent: 65,
        your_share_percent: 25,
        your_share_revenue: 500,
      },
    ],
    pagination: {
      current_page: 1,
      last_page: 1,
      per_page: 15,
      total: 1,
    },
  };
}

function mockEarningsResponses({
  earnings = buildEarningsData(),
  transactions = buildTransactionsData(),
  earningsError,
  transactionsError,
}: {
  earnings?: EarningsData;
  transactions?: EarningTransactionsData;
  earningsError?: string;
  transactionsError?: string;
} = {}) {
  vi.mocked(apiFetch).mockImplementation(async (url, options) => {
    if (typeof url === "string" && url.includes("/provider/earnings")) {
      if (earningsError) {
        const envelope = {
          key: "fail",
          msg: earningsError,
        } satisfies ApiEnvelope;
        options?.errorCase?.(envelope);
        return envelope;
      }
      const envelope = {
        key: "success",
        data: earnings,
        msg: "",
      } satisfies ApiEnvelope<EarningsData>;
      options?.successCase?.(envelope);
      return envelope;
    }

    if (typeof url === "string" && url.includes("/provider/earning-transactions")) {
      if (transactionsError) {
        const envelope = {
          key: "fail",
          msg: transactionsError,
        } satisfies ApiEnvelope;
        options?.errorCase?.(envelope);
        return envelope;
      }
      const envelope = {
        key: "success",
        data: transactions,
        msg: "",
      } satisfies ApiEnvelope<EarningTransactionsData>;
      options?.successCase?.(envelope);
      return envelope;
    }

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

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

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

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

    expect(screen.getByRole("heading", { name: "الأرباح" })).toBeInTheDocument();
    expect(container.querySelectorAll(".animate-pulse").length).toBeGreaterThan(0);
  });

  it("renders KPIs, compare chart, distribution bars, and vehicle table on success", async () => {
    mockEarningsResponses();

    render(<EarningsPage />);

    expect(await screen.findByText("إجمالي الأرباح")).toBeInTheDocument();
    expect(screen.getByTestId("compare-chart")).toHaveTextContent("يناير,فبراير");
    expect(screen.getByText("الشهر الحالي")).toBeInTheDocument();
    expect(screen.getByText("SC-001")).toBeInTheDocument();
    expect(screen.getByText("توزيع الأرباح")).toBeInTheDocument();
  });

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

    render(<EarningsPage />);

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

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

    render(<EarningsPage />);

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

  it("uses default errors when API messages cannot be formatted", async () => {
    vi.mocked(formatApiMsg).mockReturnValue("");
    mockEarningsResponses({
      earningsError: "رسالة خلفية",
      transactionsError: "رسالة خلفية",
    });

    render(<EarningsPage />);

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

  it("shows the empty distribution state when monthly chart data is absent", async () => {
    mockEarningsResponses({
      earnings: buildEarningsData({ monthly_earnings_chart: [] }),
    });

    render(<EarningsPage />);

    expect(await screen.findByText("لا توجد بيانات")).toBeInTheDocument();
  });

  it("refetches earning-transactions with sort when a table sort button is clicked", async () => {
    const user = userEvent.setup();
    mockEarningsResponses();

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

    await user.click(screen.getByRole("button", { name: "نصيبك" }));

    await waitFor(() => {
      expect(apiFetch).toHaveBeenCalledWith(
        "/provider/earning-transactions?page=1&per_page=15&sort=your_share_revenue",
        expect.objectContaining({ method: "GET" }),
      );
    });
  });

  it("refetches earnings when the months range changes", async () => {
    const user = userEvent.setup();
    mockEarningsResponses();

    render(<EarningsPage />);
    await screen.findByText("إجمالي الأرباح");

    await user.click(screen.getByRole("button", { name: "12 أشهر" }));

    await waitFor(() => {
      expect(apiFetch).toHaveBeenCalledWith(
        "/provider/earnings?months=12",
        expect.objectContaining({ method: "GET" }),
      );
    });
  });

  it("renders the distribution total for the selected months range", async () => {
    mockEarningsResponses();

    render(<EarningsPage />);

    const distributionPanel = await screen.findByText("توزيع الأرباح");
    const panel = distributionPanel.parentElement!;
    expect(within(panel).getByText("إجمالي 6 أشهر")).toBeInTheDocument();
    expect(within(panel).getByText("1,500 ريال")).toBeInTheDocument();
  });

  it("renders no KPI section when earnings succeed without data", async () => {
    vi.mocked(apiFetch).mockImplementation(async (url, options) => {
      if (typeof url === "string" && url.includes("/provider/earnings")) {
        const envelope = {
          key: "success",
          msg: "",
        } satisfies ApiEnvelope<EarningsData>;
        options?.successCase?.(envelope);
        return envelope;
      }

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

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

    await waitFor(() => {
      expect(apiFetch).toHaveBeenCalled();
    });

    expect(screen.queryByText("إجمالي الأرباح")).not.toBeInTheDocument();
    expect(screen.queryByText("تعذّر تحميل الأرباح")).not.toBeInTheDocument();
    expect(container.querySelector(".animate-pulse")).not.toBeInTheDocument();
  });

  it("ignores a stale earnings error callback after unmount", async () => {
    let resolveEarnings!: () => void;
    vi.mocked(apiFetch).mockImplementation((url, options) => {
      if (typeof url === "string" && url.includes("/provider/earnings")) {
        return new Promise<ApiEnvelope<EarningsData>>((resolve) => {
          resolveEarnings = () => {
            const envelope = { key: "fail", msg: "خطأ متأخر" } satisfies ApiEnvelope;
            options?.errorCase?.(envelope);
            resolve(envelope);
          };
        });
      }
      const envelope = {
        key: "success",
        data: buildTransactionsData(),
        msg: "",
      } satisfies ApiEnvelope<EarningTransactionsData>;
      options?.successCase?.(envelope);
      return Promise.resolve(envelope);
    });

    const { unmount } = render(<EarningsPage />);
    await waitFor(() => expect(apiFetch).toHaveBeenCalled());
    unmount();
    resolveEarnings();

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

  it("ignores every stale earnings and vehicle callback after unmount", async () => {
    const callbacks: Array<{
      success?: (envelope: ApiEnvelope<unknown>) => void;
      error?: (envelope: ApiEnvelope) => void;
      resolve: (envelope: ApiEnvelope<unknown>) => void;
    }> = [];
    vi.mocked(apiFetch).mockImplementation((_url, options) =>
      new Promise((resolve) => {
        callbacks.push({
          success: options?.successCase as (envelope: ApiEnvelope<unknown>) => void,
          error: options?.errorCase,
          resolve,
        });
      }),
    );

    const { unmount } = render(<EarningsPage />);
    await waitFor(() => expect(callbacks).toHaveLength(2));
    unmount();

    callbacks.forEach(({ success, error, resolve }, index) => {
      success?.({
        key: "success",
        data: index ? buildTransactionsData() : buildEarningsData(),
      });
      error?.({ key: "fail", msg: "خطأ متأخر" });
      resolve({ key: "success" });
    });

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