/**
 * @vitest-environment jsdom
 */
import { cleanup, render, screen, waitFor } from "@testing-library/react";
import { getAdminInviteList } from "@workspace/ui/services/admin/invite";
import type { AxiosResponse } from "axios";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import InviteManagement from ".";

vi.mock("react-i18next", () => ({
  useTranslation: () => ({
    t: (
      _key: string,
      fallback: string,
      options?: { count?: number }
    ): string => {
      if (typeof options?.count === "number") {
        return `${options.count} days`;
      }
      return fallback;
    },
  }),
}));

vi.mock("@/stores/global", () => ({
  useGlobalStore: () => ({
    common: {
      currency: {
        currency_symbol: "$",
      },
    },
  }),
}));

vi.mock("@/utils/common", () => ({
  formatDate: (timestamp: number) => `date-${timestamp}`,
}));

vi.mock("@workspace/ui/services/admin/invite", () => ({
  getAdminInviteList: vi.fn(),
}));

const mockedGetAdminInviteList = vi.mocked(getAdminInviteList);

function createInviteListResponse(
  data: API.GetAdminInviteListResponse
): AxiosResponse<API.Response & { data?: API.GetAdminInviteListResponse }> {
  return {
    data: {
      code: 200,
      data,
    },
    status: 200,
    statusText: "OK",
    headers: {},
    config: {
      headers: {} as AxiosResponse["config"]["headers"],
    },
  };
}

afterEach(() => {
  cleanup();
  vi.clearAllMocks();
});

beforeEach(() => {
  localStorage.setItem("timezone", "UTC");
});

describe("InviteManagement", () => {
  it("renders invite rows with status, purchase state, commission, and gift days", async () => {
    mockedGetAdminInviteList.mockResolvedValue(
      createInviteListResponse({
        total: 1,
        list: [
          {
            inviter_id: 10,
            inviter_identifier: "agent@example.com",
            invitee_id: 123,
            invitee_identifier: "user@example.com",
            invitee_avatar: "",
            invitee_enable: true,
            invited_at: 1_716_800_000,
            order_count: 3,
            has_purchased: true,
            inviter_commission: 1500,
            inviter_gift_days: 30,
            invitee_gift_days: 7,
          },
        ],
      })
    );

    render(<InviteManagement />);

    await waitFor(() =>
      expect(mockedGetAdminInviteList).toHaveBeenCalledWith({
        page: 1,
        size: 200,
        search: undefined,
        inviter_id: undefined,
        invitee_id: undefined,
      })
    );

    expect(await screen.findByText("agent@example.com")).not.toBeNull();
    expect(screen.getByText("user@example.com")).not.toBeNull();
    expect(screen.getByText("Enabled")).not.toBeNull();
    expect(screen.getByText("Yes")).not.toBeNull();
    expect(screen.getByText("$15.00")).not.toBeNull();
    expect(screen.getByText("30 days")).not.toBeNull();
    expect(screen.getByText("7 days")).not.toBeNull();
    expect(screen.getByText("date-1716800000")).not.toBeNull();
  });

  it("shows the empty state when the API returns no rows", async () => {
    mockedGetAdminInviteList.mockResolvedValue(
      createInviteListResponse({
        total: 0,
        list: [],
      })
    );

    render(<InviteManagement />);

    expect(await screen.findByText("No invite records")).not.toBeNull();
  });

  it("shows a loading state while invites are being fetched", () => {
    let resolveRequest:
      | ((
          value: AxiosResponse<
            API.Response & { data?: API.GetAdminInviteListResponse }
          >
        ) => void)
      | undefined;
    mockedGetAdminInviteList.mockReturnValue(
      new Promise((resolve) => {
        resolveRequest = resolve;
      })
    );

    render(<InviteManagement />);

    expect(screen.getByRole("status", { name: "Loading data" })).not.toBeNull();
    expect(resolveRequest).toBeTypeOf("function");
  });

  it("shows an error state when the request fails", async () => {
    mockedGetAdminInviteList.mockRejectedValue(new Error("network failed"));

    render(<InviteManagement />);

    await waitFor(() => expect(mockedGetAdminInviteList).toHaveBeenCalled());
    expect(await screen.findByText("Failed to load invites")).not.toBeNull();
    expect(screen.queryByText("No invite records")).toBeNull();
  });
});
