import { afterEach, expect, test, vi } from "vitest";
import { setAccessToken } from "@/shared/api/request";
import {
  approveFinanceApplication,
  approveFinanceWithdrawalApplication,
  createFinanceApplication,
  fetchFinanceApplicationOptions,
  filterOperationsByPermissions,
  listFinanceApplications,
  listFinanceRechargeDetails,
  listMyFinanceApplications,
  listFinanceWithdrawalApplications,
  rejectFinanceApplication,
  rejectFinanceWithdrawalApplication
} from "./api.js";
import { FINANCE_OPERATIONS } from "./constants.js";

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

test("finance application APIs use generated endpoint paths", async () => {
  vi.stubGlobal(
    "fetch",
    vi.fn(async (url) => {
      if (String(url).includes("/admin/apps")) {
        return new Response(JSON.stringify({ code: 0, data: { items: [{ appCode: "lalu", appName: "lalu" }], total: 1 } }));
      }
      if (String(url).includes("/applications?")) {
        return new Response(JSON.stringify({ code: 0, data: { items: [], page: 1, pageSize: 50, total: 0 } }));
      }
      if (String(url).includes("/applications/mine?")) {
        return new Response(JSON.stringify({ code: 0, data: { items: [], page: 1, pageSize: 50, total: 0 } }));
      }
      if (String(url).includes("/withdrawal-applications?")) {
        return new Response(JSON.stringify({ code: 0, data: { items: [], page: 1, pageSize: 50, total: 0 } }));
      }
      return new Response(JSON.stringify({ code: 0, data: { id: 12 } }));
    })
  );

  const options = await fetchFinanceApplicationOptions();
  await listFinanceApplications({ page: 1, page_size: 50, status: "pending" });
  await listMyFinanceApplications({ page: 1, page_size: 50 });
  await listFinanceWithdrawalApplications({ page: 1, page_size: 50 });
  await createFinanceApplication({
    appCode: "lalu",
    coinAmount: 100,
    operation: "coin_seller_coin_credit",
    rechargeAmount: 10,
    targetUserId: "10001"
  });
  await approveFinanceApplication(12, { auditRemark: "ok" });
  await rejectFinanceApplication(13, { auditRemark: "bad" });
  await approveFinanceWithdrawalApplication(14, { auditImageUrl: "https://cdn.example.com/pay.png", auditRemark: "ok" });
  await rejectFinanceWithdrawalApplication(15, { auditRemark: "bad" });

  const calls = vi.mocked(fetch).mock.calls;
  expect(options.apps).toEqual([{ appCode: "lalu", appName: "lalu" }]);
  expect(options.operations.map((item) => item.label)).toEqual([
    "增加用户金币",
    "减少用户金币",
    "用户钱包扣减",
    "用户钱包增加",
    "增加币商金币",
    "减少币商金币"
  ]);
  expect(String(calls[0][0])).toContain("/api/v1/admin/apps");
  expect(calls[0][1]?.method).toBe("GET");
  expect(String(calls[1][0])).toContain("/api/v1/admin/finance/applications?");
  expect(String(calls[1][0])).toContain("status=pending");
  expect(calls[1][1]?.method).toBe("GET");
  expect(String(calls[2][0])).toContain("/api/v1/admin/finance/applications/mine?");
  expect(calls[2][1]?.method).toBe("GET");
  expect(String(calls[3][0])).toContain("/api/v1/admin/finance/withdrawal-applications?");
  expect(calls[3][1]?.method).toBe("GET");
  expect(String(calls[4][0])).toContain("/api/v1/admin/finance/applications");
  expect(calls[4][1]?.method).toBe("POST");
  expect(JSON.parse(String(calls[4][1]?.body))).toMatchObject({ appCode: "lalu", operation: "coin_seller_coin_credit" });
  expect(String(calls[5][0])).toContain("/api/v1/admin/finance/applications/12/approve");
  expect(calls[5][1]?.method).toBe("POST");
  expect(JSON.parse(String(calls[5][1]?.body))).toEqual({ auditRemark: "ok" });
  expect(String(calls[6][0])).toContain("/api/v1/admin/finance/applications/13/reject");
  expect(calls[6][1]?.method).toBe("POST");
  expect(JSON.parse(String(calls[6][1]?.body))).toEqual({ auditRemark: "bad" });
  expect(String(calls[7][0])).toContain("/api/v1/admin/finance/withdrawal-applications/14/approve");
  expect(calls[7][1]?.method).toBe("POST");
  expect(JSON.parse(String(calls[7][1]?.body))).toEqual({ auditImageUrl: "https://cdn.example.com/pay.png", auditRemark: "ok" });
  expect(String(calls[8][0])).toContain("/api/v1/admin/finance/withdrawal-applications/15/reject");
  expect(calls[8][1]?.method).toBe("POST");
  expect(JSON.parse(String(calls[8][1]?.body))).toEqual({ auditRemark: "bad" });
});

test("filters operation options by backend-configured permissions", () => {
  const allowed = filterOperationsByPermissions(FINANCE_OPERATIONS, [
    "finance-operation:user-coin-credit",
    "finance-operation:coin-seller-coin-debit"
  ]);

  expect(allowed.map((item) => item.value)).toEqual(["user_coin_credit", "coin_seller_coin_debit"]);
});

test("finance recharge details query every app with app header and merge by recharge time", async () => {
  vi.stubGlobal(
    "fetch",
    vi.fn(async (url, init) => {
      const appCode = init?.headers?.["X-App-Code"];
      return new Response(
        JSON.stringify({
          code: 0,
          data: {
            items: [
              {
                appCode,
                coinAmount: appCode === "aslan" ? 2000 : 1000,
                createdAtMs: appCode === "aslan" ? 1760003600000 : 1760000000000,
                exchangeCoinAmount: appCode === "aslan" ? 2000 : 1000,
                exchangeUsdMinorAmount: appCode === "aslan" ? 200 : 100,
                rechargeType: "google_play_recharge",
                status: "succeeded",
                transactionId: `${appCode}-tx`,
                usdMinorAmount: appCode === "aslan" ? 200 : 100,
                userId: "10001"
              }
            ],
            page: 1,
            pageSize: 2,
            total: 1
          }
        })
      );
    })
  );

  const page = await listFinanceRechargeDetails(
    { page: 1, page_size: 2, start_at_ms: 1760000000000, status: "succeeded" },
    [
      { appCode: "lalu", appName: "lalu" },
      { appCode: "aslan", appName: "aslan" }
    ]
  );

  const calls = vi.mocked(fetch).mock.calls;
  expect(page.total).toBe(2);
  expect(page.items.map((item) => item.transactionId)).toEqual(["aslan-tx", "lalu-tx"]);
  expect(calls).toHaveLength(2);
  expect(calls.map(([, init]) => init?.headers?.["X-App-Code"])).toEqual(["lalu", "aslan"]);
  expect(String(calls[0][0])).toContain("/api/v1/admin/payment/recharge-bills?");
  expect(String(calls[0][0])).toContain("page_size=2");
  expect(String(calls[0][0])).toContain("status=succeeded");
  expect(String(calls[0][0])).toContain("start_at_ms=1760000000000");
});
