import { describe, expect, test } from 'vitest';
import { calculateSnatchShortfall, isPositionDelinquent } from '../staking.js';

describe('calculateSnatchShortfall', () => {
  test('returns zero when within cap', () => {
    const outstanding = 100n;
    const desired = 50n;
    const total = 1000n;

    const result = calculateSnatchShortfall(outstanding, desired, total, 2n, 10n);
    expect(result).toBe(0n);
  });

  test('returns positive remainder when exceeding cap', () => {
    const outstanding = 200n;
    const desired = 200n;
    const total = 1000n;

    const result = calculateSnatchShortfall(outstanding, desired, total, 2n, 10n);
    expect(result).toBe(200n);
  });

  test('returns exact overage when required equals cap + 1', () => {
    // cap = (1000 * 2) / 10 = 200; required = 100 + 101 = 201; delta = 1
    expect(calculateSnatchShortfall(100n, 101n, 1000n, 2n, 10n)).toBe(1n);
  });

  test('returns zero when required equals cap exactly', () => {
    // cap = (1000 * 2) / 10 = 200; required = 100 + 100 = 200; delta = 0
    expect(calculateSnatchShortfall(100n, 100n, 1000n, 2n, 10n)).toBe(0n);
  });

  test('uses default cap numerator/denominator when not provided', () => {
    // defaults: capNumerator=2n, capDenominator=10n
    // cap = (1000 * 2) / 10 = 200; outstanding=100, desired=50 -> required=150 <= 200 -> 0
    expect(calculateSnatchShortfall(100n, 50n, 1000n)).toBe(0n);
  });

  test('throws when capDenominator is zero', () => {
    expect(() => calculateSnatchShortfall(100n, 50n, 1000n, 2n, 0n)).toThrow('capDenominator must be greater than zero');
  });
});

describe('isPositionDelinquent', () => {
  test('respects tax rate windows', () => {
    const now = 1_000_000;
    const taxRate = 0.5; // 50%
    const windowSeconds = (365 * 24 * 60 * 60) / taxRate;

    expect(isPositionDelinquent(now - windowSeconds + 1, taxRate, now)).toBe(false);
    expect(isPositionDelinquent(now - windowSeconds - 10, taxRate, now)).toBe(true);
    expect(isPositionDelinquent(now, 0, now)).toBe(false);
  });

  test('returns false for zero tax rate', () => {
    expect(isPositionDelinquent(0, 0, 1_000_000)).toBe(false);
  });

  test('returns false for negative tax rate', () => {
    expect(isPositionDelinquent(0, -0.1, 1_000_000)).toBe(false);
  });

  test('returns true when far past the allowance window', () => {
    // taxRate=1.0 -> allowance = 31_536_000s (1 year); use now > 31_536_000
    const now = 100_000_000;
    const lastTax = 0;
    const taxRate = 1.0; // 100% per year = 1 year window
    expect(isPositionDelinquent(lastTax, taxRate, now)).toBe(true);
  });

  test('uses current time as default when referenceTimestamp not provided', () => {
    // taxRate=1.0 -> allowance = 1 year; 2 years ago is definitely delinquent
    const twoYearsAgoSeconds = Math.floor(Date.now() / 1000) - 2 * 365 * 24 * 60 * 60;
    expect(isPositionDelinquent(twoYearsAgoSeconds, 1.0)).toBe(true);
  });
});
