/**
 * E2E：账期账单部分收付 → partial_paid → 尾款结清 settled。
 */

import { test, expect } from '@playwright/test';
import {
  adminLoginViaBypass,
  adminCtxOf,
  fetchBillCreditLedgerRows,
  fetchOpenDrawNo,
  playerLoginViaBypass,
  openSettlementPeriod,
  setupCreditPlayer,
} from './_helper';
import { runWalletDrawSettlement } from './helpers/draw-settlement';

test('confirm 后分两笔收付 → partial_paid → settled', async () => {
  test.setTimeout(300_000);

  const credit = await setupCreditPlayer({
    credit_limit: 100_000,
    username: `e2e_partial_${Date.now()}`,
  });
  const drawNo = await fetchOpenDrawNo();

  const adminSession = await adminLoginViaBypass();
  const admin = await adminCtxOf(adminSession.accessToken);
  const periodId = await openSettlementPeriod(admin, credit.admin_site_id);

  const playerSession = await playerLoginViaBypass({
    site_code: credit.site_code,
    username: credit.username,
    password: credit.password,
  });

  await runWalletDrawSettlement({
    adminToken: adminSession.accessToken,
    playerToken: playerSession.access_token,
    drawNo,
    betNumber: '1234',
    publishWinNumber: '9999',
    betAmount: 10_000,
    expectWalletIncrease: false,
  });

  const close = await admin.post(`/api/v1/admin/settlement-periods/${periodId}/close`);
  expect(close.ok()).toBeTruthy();
  expect((await close.json()).code).toBe(0);

  const bills = await admin.get(
    `/api/v1/admin/settlement-bills?settlement_period_id=${periodId}&size=20`,
  );
  const billsBody = (await bills.json()) as any;
  const playerBill = billsBody.data.items.find(
    (b: any) => b.bill_type === 'player' && Number(b.owner_id) === credit.player_id,
  );
  expect(playerBill).toBeTruthy();
  expect(Number(playerBill.net_amount)).toBeGreaterThan(0);

  const confirm = await admin.post(`/api/v1/admin/settlement-bills/${playerBill.id}/confirm`);
  expect(confirm.ok()).toBeTruthy();
  const confirmBody = (await confirm.json()) as any;
  expect(confirmBody.code).toBe(0);
  expect(confirmBody.data.status).toBe('confirmed');

  const billShow = await admin.get(`/api/v1/admin/settlement-bills/${playerBill.id}`);
  const billDetail = (await billShow.json()) as any;
  const unpaid = Math.abs(Number(billDetail.data.bill.unpaid_amount));
  expect(unpaid).toBeGreaterThan(1);
  const firstPay = Math.floor(unpaid / 2);
  expect(firstPay).toBeGreaterThan(0);

  const pay1 = await admin.post(`/api/v1/admin/settlement-bills/${playerBill.id}/payments`, {
    data: { amount: firstPay, method: 'e2e_partial_1', remark: 'e2e first half' },
  });
  expect(pay1.ok()).toBeTruthy();
  const pay1Body = (await pay1.json()) as any;
  expect(pay1Body.code).toBe(0);
  expect(pay1Body.data.bill.status).toBe('partial_paid');

  const ledgerAfterPay1 = await fetchBillCreditLedgerRows(
    credit.player_id,
    Number(playerBill.id),
    'settlement_confirm',
  );
  expect(ledgerAfterPay1).toHaveLength(1);
  expect(Number(ledgerAfterPay1[0].amount)).toBe(firstPay);

  const midShow = await admin.get(`/api/v1/admin/settlement-bills/${playerBill.id}`);
  const midDetail = (await midShow.json()) as any;
  const secondPay = Math.abs(Number(midDetail.data.bill.unpaid_amount));
  expect(secondPay).toBeGreaterThan(0);

  const pay2 = await admin.post(`/api/v1/admin/settlement-bills/${playerBill.id}/payments`, {
    data: { amount: secondPay, method: 'e2e_partial_2', remark: 'e2e remainder' },
  });
  const pay2Text = await pay2.text();
  expect(pay2.ok(), `pay2 status=${pay2.status()} body=${pay2Text.slice(0, 400)}`).toBeTruthy();
  const pay2Body = JSON.parse(pay2Text) as any;
  expect(pay2Body.data.bill.status).toBe('settled');
  expect(Number(pay2Body.data.bill.unpaid_amount)).toBe(0);

  const ledgerAfterPay2 = await fetchBillCreditLedgerRows(
    credit.player_id,
    Number(playerBill.id),
    'settlement_confirm',
  );
  expect(ledgerAfterPay2).toHaveLength(1);
  expect(Number(ledgerAfterPay2[0].amount)).toBe(unpaid);

  const exceed = await admin.post(`/api/v1/admin/settlement-bills/${playerBill.id}/payments`, {
    data: { amount: 1, method: 'e2e_over', remark: 'should fail' },
  });
  const exceedBody = (await exceed.json()) as any;
  expect(exceedBody.code).not.toBe(0);

  await admin.dispose();
});

test('玩家赢单 confirm 后分两笔收付 → settlement_payout 累计', async () => {
  test.setTimeout(300_000);

  const winNumber = `6${String(Date.now()).slice(-3)}`;
  const credit = await setupCreditPlayer({
    credit_limit: 100_000,
    username: `e2e_partial_win_${Date.now()}`,
  });
  const drawNo = await fetchOpenDrawNo();

  const adminSession = await adminLoginViaBypass();
  const admin = await adminCtxOf(adminSession.accessToken);
  const periodId = await openSettlementPeriod(admin, credit.admin_site_id);

  const playerSession = await playerLoginViaBypass({
    site_code: credit.site_code,
    username: credit.username,
    password: credit.password,
  });

  await runWalletDrawSettlement({
    adminToken: adminSession.accessToken,
    playerToken: playerSession.access_token,
    drawNo,
    betNumber: winNumber,
    betAmount: 10_000,
    expectWalletIncrease: false,
  });

  const close = await admin.post(`/api/v1/admin/settlement-periods/${periodId}/close`);
  expect(close.ok()).toBeTruthy();

  const bills = await admin.get(
    `/api/v1/admin/settlement-bills?settlement_period_id=${periodId}&size=20`,
  );
  const billsBody = (await bills.json()) as any;
  const playerBill = billsBody.data.items.find(
    (b: any) => b.bill_type === 'player' && Number(b.owner_id) === credit.player_id,
  );
  expect(playerBill).toBeTruthy();
  expect(Number(playerBill.net_amount)).toBeLessThan(0);

  await admin.post(`/api/v1/admin/settlement-bills/${playerBill.id}/confirm`);

  const billDetail = (await (await admin.get(`/api/v1/admin/settlement-bills/${playerBill.id}`)).json()) as any;
  const unpaid = Math.abs(Number(billDetail.data.bill.unpaid_amount));
  expect(unpaid).toBeGreaterThan(1);
  const firstPay = Math.floor(unpaid / 2);
  expect(firstPay).toBeGreaterThan(0);

  const pay1 = await admin.post(`/api/v1/admin/settlement-bills/${playerBill.id}/payments`, {
    data: { amount: firstPay, method: 'e2e_win_partial_1', remark: 'win first half' },
  });
  expect(pay1.ok()).toBeTruthy();
  expect((await pay1.json()).data.bill.status).toBe('partial_paid');

  const ledgerAfterPay1 = await fetchBillCreditLedgerRows(
    credit.player_id,
    Number(playerBill.id),
    'settlement_payout',
  );
  expect(ledgerAfterPay1).toHaveLength(1);
  expect(Number(ledgerAfterPay1[0].amount)).toBe(firstPay);

  const midDetail = (await (await admin.get(`/api/v1/admin/settlement-bills/${playerBill.id}`)).json()) as any;
  const secondPay = Math.abs(Number(midDetail.data.bill.unpaid_amount));

  const pay2 = await admin.post(`/api/v1/admin/settlement-bills/${playerBill.id}/payments`, {
    data: { amount: secondPay, method: 'e2e_win_partial_2', remark: 'win remainder' },
  });
  expect(pay2.ok()).toBeTruthy();
  expect((await pay2.json()).data.bill.status).toBe('settled');

  const ledgerAfterPay2 = await fetchBillCreditLedgerRows(
    credit.player_id,
    Number(playerBill.id),
    'settlement_payout',
  );
  expect(ledgerAfterPay2).toHaveLength(1);
  expect(Number(ledgerAfterPay2[0].amount)).toBe(unpaid);

  await admin.dispose();
});
