import {
  settleSelection,
  calculatePayout,
  isQuarterHandicapOrTotal,
} from './domains/settlement/domain/settlement-calculator';
import { canSelectForParlay } from '@thebet365/shared';

/**
 * Agent credit & wallet integration scenarios (A001-A007)
 * These tests validate business rules without DB dependency.
 */
describe('Agent Credit Rules', () => {
  it('A001: deposit increases player balance and reduces agent available credit', () => {
    const creditLimit = 10000;
    const usedCredit = 1000;
    const depositAmount = 500;
    const newUsed = usedCredit + depositAmount;
    expect(creditLimit - newUsed).toBe(8500);
  });

  it('A002: bet freeze does not change total balance or agent credit', () => {
    const available = 1000;
    const frozen = 0;
    const stake = 100;
    const totalBefore = available + frozen;
    const totalAfter = (available - stake) + (frozen + stake);
    expect(totalAfter).toBe(totalBefore);
  });

  it('A003: player lose releases agent credit', () => {
    const usedBefore = 1000;
    const stake = 100;
    const usedAfter = usedBefore - stake;
    expect(usedAfter).toBe(900);
  });

  it('A004: player win increases agent credit usage', () => {
    const usedBefore = 1000;
    const payout = 185;
    const stake = 100;
    const netGain = payout - stake;
    const usedAfter = usedBefore + netGain;
    expect(usedAfter).toBe(1085);
  });

  it('A005: negative credit blocks further deposit', () => {
    const creditLimit = 1000;
    const usedCredit = 1200;
    const available = creditLimit - usedCredit;
    expect(available).toBeLessThan(0);
    const canDeposit = available > 0;
    expect(canDeposit).toBe(false);
  });

  it('A006: level 1 allocating credit to level 2 reduces available', () => {
    const available = 10000;
    const allocate = 3000;
    expect(available - allocate).toBe(7000);
  });

  it('A007: non-direct player deposit should be rejected', () => {
    const agentId = BigInt(1);
    const playerParentId = BigInt(2);
    const canDeposit = agentId === playerParentId;
    expect(canDeposit).toBe(false);
  });

  it('A008: admin deposit cannot exceed parent agent available credit', () => {
    const creditLimit = 5000;
    const usedCredit = 4800;
    const depositAmount = 300;
    const available = creditLimit - usedCredit;
    expect(depositAmount).toBeGreaterThan(available);
    const allowed = depositAmount <= available;
    expect(allowed).toBe(false);
  });
});

describe('Bet Validation Rules (B001-B010)', () => {
  it('B003: odds version mismatch should reject', () => {
    const submitted = BigInt(1);
    const current = BigInt(2);
    expect(submitted === current).toBe(false);
  });

  it('B007: same match legs are allowed in parlay', () => {
    const first = canSelectForParlay({ marketType: 'FT_1X2', allowParlay: true });
    const second = canSelectForParlay({ marketType: 'FT_1X2', allowParlay: true });
    expect(first.ok).toBe(true);
    expect(second.ok).toBe(true);
  });

  it('B008: quarter line in parlay rejected', () => {
    expect(isQuarterHandicapOrTotal(-0.25)).toBe(true);
    expect(isQuarterHandicapOrTotal(-0.5)).toBe(false);
  });

  it('B009: more than 5 legs rejected', () => {
    const legs = 6;
    expect(legs > 5).toBe(true);
  });
});

describe('Settlement payout accuracy', () => {
  it('half win payout formula', () => {
    const payout = calculatePayout(100, 1.85, 'HALF_WIN');
    expect(payout.toNumber()).toBe(142.5);
  });

  it('half lose payout formula', () => {
    const payout = calculatePayout(100, 1.85, 'HALF_LOSE');
    expect(payout.toNumber()).toBe(50);
  });

  it('S004: 0-0 odd/even is even', () => {
    const result = settleSelection({
      marketType: 'FT_ODD_EVEN',
      selectionCode: 'EVEN',
      score: { htHome: 0, htAway: 0, ftHome: 0, ftAway: 0 },
    });
    expect(result).toBe('WIN');
  });
});
