import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { afterEach, beforeEach, expect, test, vi } from "vitest";
import { ToastProvider } from "@/shared/ui/ToastProvider.jsx";
import { FinanceApp } from "./FinanceApp.jsx";
import {
  approveOperationsWithdrawalApplication,
  fetchFinanceOptions,
  fetchFinanceRechargeApps,
  fetchFinanceRechargeOverview,
  fetchFinanceRechargeSummaries,
  fetchFinanceSession,
  fetchFinanceWithdrawalOptions,
  listFinanceCoinSellerRechargeOrders,
  listFinanceRechargeDetails,
  listFinanceRechargeRegions,
  listFinanceWithdrawalApplications,
  listOperationsWithdrawalApplications,
  rejectOperationsWithdrawalApplication,
} from "./api.js";
import { EMPTY_RECHARGE_OVERVIEW, EMPTY_RECHARGE_SUMMARY } from "./format.js";

vi.mock("./api.js", () => ({
  approveFinanceWithdrawalApplication: vi.fn(),
  approveOperationsWithdrawalApplication: vi.fn(),
  createFinanceCoinSellerRechargeOrder: vi.fn(),
  exportFinanceRechargeBills: vi.fn(),
  fetchFinanceOptions: vi.fn(),
  fetchFinanceRechargeApps: vi.fn(),
  fetchFinanceRechargeOverview: vi.fn(),
  fetchFinanceRechargeSummaries: vi.fn(),
  fetchFinanceSession: vi.fn(),
  fetchFinanceWithdrawalOptions: vi.fn(),
  grantFinanceCoinSellerRechargeOrder: vi.fn(),
  getFinanceCoinSellerRechargeExchangeRate: vi.fn(),
  getFinanceUSDTAddresses: vi.fn(),
  listFinanceCoinSellerRechargeOrders: vi.fn(),
  listFinanceRechargeDetails: vi.fn(),
  listFinanceRechargeRegions: vi.fn(),
  listFinanceWithdrawalApplications: vi.fn(),
  listOperationsWithdrawalApplications: vi.fn(),
  rejectFinanceWithdrawalApplication: vi.fn(),
  rejectOperationsWithdrawalApplication: vi.fn(),
  refreshFinanceGoogleRechargePaid: vi.fn(),
  replaceFinanceCoinSellerRechargeExchangeRate: vi.fn(),
  upsertFinanceUSDTAddress: vi.fn(),
  quoteFinanceCoinSellerRechargeOrder: vi.fn(),
  verifyFinanceCoinSellerRechargeOrder: vi.fn(),
  verifyFinanceCoinSellerRechargeReceipt: vi.fn()
}));

const defaultSession = {
  canAccessFinance: true,
  canAuditOperationsWithdrawalApplications: false,
  canAuditWithdrawalApplications: false,
  canConfigureCoinSellerRechargeExchangeRate: false,
  canCreateCoinSellerRechargeOrder: false,
  canGrantCoinSellerRechargeOrder: false,
  canUpdateUSDTAddress: false,
  canVerifyCoinSellerRechargeOrder: false,
  canViewAppRechargeDetails: false,
  canViewCoinSellerRechargeOrders: false,
  canViewOperationsWithdrawalApplications: false,
  canViewWithdrawalApplications: true,
  permissions: ["finance-withdrawal:view"],
  user: { account: "admin", name: "admin" }
};

beforeEach(() => {
  vi.mocked(fetchFinanceOptions).mockResolvedValue({ apps: [{ appCode: "lalu", appName: "lalu" }] });
  vi.mocked(fetchFinanceRechargeApps).mockResolvedValue([{ appCode: "lalu", appName: "Lalu" }]);
  vi.mocked(fetchFinanceRechargeOverview).mockResolvedValue(EMPTY_RECHARGE_OVERVIEW);
  vi.mocked(fetchFinanceRechargeSummaries).mockResolvedValue({ merged: EMPTY_RECHARGE_SUMMARY, perApp: [] });
  vi.mocked(fetchFinanceSession).mockResolvedValue(defaultSession);
  vi.mocked(fetchFinanceWithdrawalOptions).mockResolvedValue({ apps: [{ appCode: "lalu", appName: "lalu" }] });
  vi.mocked(listFinanceCoinSellerRechargeOrders).mockResolvedValue({ items: [], page: 1, pageSize: 50, total: 0 });
  vi.mocked(listFinanceRechargeDetails).mockResolvedValue({ items: [], page: 1, pageSize: 50, total: 0 });
  vi.mocked(listFinanceRechargeRegions).mockResolvedValue([]);
  vi.mocked(listFinanceWithdrawalApplications).mockResolvedValue({ items: [], page: 1, pageSize: 50, total: 0 });
  vi.mocked(listOperationsWithdrawalApplications).mockResolvedValue({ items: [], page: 1, pageSize: 50, total: 0 });
});

afterEach(() => {
  vi.restoreAllMocks();
  vi.clearAllMocks();
  window.history.pushState(null, "", "/");
});

test("preserves the finance entry when the expired session asks the user to log in again", async () => {
  vi.mocked(fetchFinanceSession).mockRejectedValueOnce(new Error("登录已失效"));
  window.history.pushState(null, "", "/finance/?view=rechargeDetails");

  render(
    <ToastProvider>
      <FinanceApp />
    </ToastProvider>
  );

  expect(await screen.findByRole("link", { name: "重新登录" })).toHaveAttribute(
    "href",
    "/login?redirect=%2Ffinance%2F%3Fview%3DrechargeDetails"
  );
});

test("defaults recharge finance views to yesterday in China timezone", async () => {
  vi.spyOn(Date, "now").mockReturnValue(Date.UTC(2026, 6, 8, 4));
  vi.mocked(fetchFinanceSession).mockResolvedValue({
    ...defaultSession,
    canViewAppRechargeDetails: true,
    permissions: ["finance-recharge-detail:view"]
  });

  render(
    <ToastProvider>
      <FinanceApp />
    </ToastProvider>
  );

  const yesterdayButton = await screen.findByRole("button", { name: "昨天" });
  expect(yesterdayButton).toHaveClass("is-on");
  expect(screen.getByRole("button", { name: "今天" })).toBeInTheDocument();

  await waitFor(() => expect(listFinanceRechargeDetails).toHaveBeenCalled());
  const [query] = vi.mocked(listFinanceRechargeDetails).mock.calls.at(-1);
  expect(query.start_at_ms).toBe(Date.UTC(2026, 6, 6, 16));
  expect(query.end_at_ms).toBe(Date.UTC(2026, 6, 7, 16));
});

test("opens the permitted operations withdrawal deep link and synchronizes menu changes", async () => {
  vi.mocked(fetchFinanceSession).mockResolvedValue({
    ...defaultSession,
    canAuditOperationsWithdrawalApplications: true,
    canViewOperationsWithdrawalApplications: true,
    permissions: ["finance-withdrawal:view", "operations-withdrawal:audit"]
  });
  window.history.pushState(null, "", "/finance/?view=withdrawalOperations");

  render(
    <ToastProvider>
      <FinanceApp />
    </ToastProvider>
  );

  expect(await screen.findByRole("heading", { name: "用户提现运营审核" })).toBeInTheDocument();
  await waitFor(() => expect(listOperationsWithdrawalApplications).toHaveBeenCalled());
  fireEvent.click(screen.getByRole("button", { name: "用户提现财务审核" }));
  expect(window.location.search).toBe("?view=withdrawalFinance");
  expect(screen.getByRole("heading", { name: "用户提现财务审核" })).toBeInTheDocument();
});

test("falls back from an unauthorized operations deep link to the finance review queue", async () => {
  window.history.pushState(null, "", "/finance/?view=withdrawalOperations");

  render(
    <ToastProvider>
      <FinanceApp />
    </ToastProvider>
  );

  expect(await screen.findByRole("heading", { name: "用户提现财务审核" })).toBeInTheDocument();
  expect(window.location.search).toBe("?view=withdrawalFinance");
  expect(screen.queryByRole("button", { name: "用户提现运营审核" })).not.toBeInTheDocument();
});

test("withdrawal app filter uses only the MoneyAccess scope catalog", async () => {
  vi.mocked(fetchFinanceOptions).mockResolvedValue({
    apps: [
      { appCode: "lalu", appName: "Lalu" },
      { appCode: "aslan", appName: "Aslan" }
    ]
  });
  vi.mocked(fetchFinanceWithdrawalOptions).mockResolvedValue({
    apps: [{ appCode: "lalu", appName: "Lalu" }]
  });

  render(
    <ToastProvider>
      <FinanceApp />
    </ToastProvider>
  );

  const appFilter = await screen.findByRole("combobox", { name: "APP" });
  fireEvent.mouseDown(appFilter);

  expect(await screen.findByRole("option", { name: "全部 APP" })).toBeInTheDocument();
  expect(screen.getByRole("option", { name: "Lalu" })).toBeInTheDocument();
  expect(screen.queryByRole("option", { name: "Aslan" })).not.toBeInTheDocument();
});

test("submits operations review with the withdrawal record app instead of the global app", async () => {
  vi.mocked(fetchFinanceSession).mockResolvedValue({
    ...defaultSession,
    canAuditOperationsWithdrawalApplications: true,
    canViewOperationsWithdrawalApplications: true,
    canViewWithdrawalApplications: false,
    permissions: ["operations-withdrawal:audit"]
  });
  vi.mocked(listOperationsWithdrawalApplications).mockResolvedValue({
    items: [
      {
        appCode: "aslan",
        createdAtMs: 1760000000000,
        id: 16,
        operationsStatus: "pending",
        status: "pending",
        userId: "10002",
        withdrawAddress: "TRX-operations",
        withdrawAmount: 20,
        withdrawMethod: "USDT-TRC20"
      }
    ],
    page: 1,
    pageSize: 50,
    total: 1
  });
  vi.mocked(approveOperationsWithdrawalApplication).mockResolvedValue({ id: 16 });
  window.history.pushState(null, "", "/finance/?view=withdrawalOperations");

  render(
    <ToastProvider>
      <FinanceApp />
    </ToastProvider>
  );

  fireEvent.click(await screen.findByRole("button", { name: "通过" }));
  fireEvent.click(await screen.findByRole("button", { name: "确认" }));

  await waitFor(() => {
    expect(approveOperationsWithdrawalApplication).toHaveBeenCalledWith(16, "aslan", {});
  });
});

test("retries a claimed operations rejection with its saved remark", async () => {
  vi.mocked(fetchFinanceSession).mockResolvedValue({
    ...defaultSession,
    canAuditOperationsWithdrawalApplications: true,
    canViewOperationsWithdrawalApplications: true,
    canViewWithdrawalApplications: false,
    permissions: ["operations-withdrawal:audit"]
  });
  vi.mocked(listOperationsWithdrawalApplications).mockResolvedValue({
    items: [
      {
        appCode: "aslan",
        createdAtMs: 1760000000000,
        id: 17,
        operationsAuditRemark: "命中运营风控",
        operationsStatus: "rejecting",
        status: "pending",
        userId: "10003",
        withdrawAddress: "TRX-rejecting",
        withdrawAmount: 30,
        withdrawMethod: "USDT-TRC20"
      }
    ],
    page: 1,
    pageSize: 50,
    total: 1
  });
  vi.mocked(rejectOperationsWithdrawalApplication).mockResolvedValue({ id: 17 });
  window.history.pushState(null, "", "/finance/?view=withdrawalOperations");

  render(
    <ToastProvider>
      <FinanceApp />
    </ToastProvider>
  );

  expect(await screen.findByText("拒绝处理中")).toBeInTheDocument();
  expect(screen.queryByRole("button", { name: "通过" })).not.toBeInTheDocument();
  fireEvent.click(screen.getByRole("button", { name: "重试拒绝" }));
  const savedRemark = await screen.findByRole("textbox", { name: "审核备注" });
  expect(savedRemark).toHaveValue("命中运营风控");
  expect(savedRemark).toBeDisabled();
  fireEvent.click(screen.getByRole("button", { name: "确认" }));

  await waitFor(() => {
    expect(rejectOperationsWithdrawalApplication).toHaveBeenCalledWith(17, "aslan", {
      auditRemark: "命中运营风控"
    });
  });
  expect(approveOperationsWithdrawalApplication).not.toHaveBeenCalled();
});
