import { BN } from '@ocap/util';
import crypto from 'crypto';

describe('QuoteService', () => {
  describe('Idempotency key generation', () => {
    it('should generate consistent idempotency key for same params', () => {
      const params = {
        contextId: 'session_123',
        priceId: 'price_456',
        currencyId: 'currency_789',
        quantity: 1,
        baseAmount: '10.00',
      };

      const key1 = crypto
        .createHash('sha256')
        .update(`${params.contextId}:${params.priceId}:${params.currencyId}:${params.quantity}:${params.baseAmount}`)
        .digest('hex')
        .substring(0, 64);

      const key2 = crypto
        .createHash('sha256')
        .update(`${params.contextId}:${params.priceId}:${params.currencyId}:${params.quantity}:${params.baseAmount}`)
        .digest('hex')
        .substring(0, 64);

      expect(key1).toBe(key2);
    });

    it('should generate different keys for different quantities', () => {
      const params1 = {
        contextId: 'session_123',
        priceId: 'price_456',
        currencyId: 'currency_789',
        quantity: 1,
        baseAmount: '10.00',
      };

      const params2 = {
        ...params1,
        quantity: 2,
        baseAmount: '20.00',
      };

      const key1 = crypto
        .createHash('sha256')
        .update(
          `${params1.contextId}:${params1.priceId}:${params1.currencyId}:${params1.quantity}:${params1.baseAmount}`
        )
        .digest('hex')
        .substring(0, 64);

      const key2 = crypto
        .createHash('sha256')
        .update(
          `${params2.contextId}:${params2.priceId}:${params2.currencyId}:${params2.quantity}:${params2.baseAmount}`
        )
        .digest('hex')
        .substring(0, 64);

      expect(key1).not.toBe(key2);
    });
  });

  describe('Quote amount calculation', () => {
    it('should correctly calculate quoted amount with ceiling', () => {
      const baseAmount = 10; // $10 USD
      const rate = 0.1; // 1 token = $0.1 USD
      const decimals = 18;

      // token_amount = base_usd / rate = 10 / 0.1 = 100 tokens
      const tokenAmount = baseAmount / rate;
      expect(tokenAmount).toBe(100);

      // Using the same logic as QuoteService
      const PRECISION_SCALE = 1e8;
      const scaledNumerator = Math.floor(baseAmount * PRECISION_SCALE);
      const scaledDenominator = Math.floor(rate * PRECISION_SCALE);
      const numerator = new BN(scaledNumerator).mul(new BN(10).pow(new BN(decimals)));
      const denominator = new BN(scaledDenominator);
      const quotedAmount = numerator.add(denominator).sub(new BN(1)).div(denominator);

      expect(quotedAmount.toString()).toBe('100000000000000000000');
    });

    it('should round up fractional token amounts', () => {
      const baseAmount = 10.5; // $10.5 USD
      const rate = 0.15; // 1 token = $0.15 USD
      const decimals = 18;

      // token_amount = 10.5 / 0.15 = 70
      const tokenAmount = baseAmount / rate;
      expect(tokenAmount).toBe(70);

      const PRECISION_SCALE = 1e8;
      const scaledNumerator = Math.floor(baseAmount * PRECISION_SCALE);
      const scaledDenominator = Math.floor(rate * PRECISION_SCALE);
      const numerator = new BN(scaledNumerator).mul(new BN(10).pow(new BN(decimals)));
      const denominator = new BN(scaledDenominator);
      const quotedAmount = numerator.add(denominator).sub(new BN(1)).div(denominator);

      expect(quotedAmount.toString()).toBe('70000000000000000000');
    });

    it('should handle precision correctly with many decimals', () => {
      const baseAmount = 100;
      const rate = 0.123456789;
      const decimals = 18;

      const PRECISION_SCALE = 1e8;
      const scaledNumerator = Math.floor(baseAmount * PRECISION_SCALE);
      const scaledDenominator = Math.floor(rate * PRECISION_SCALE);
      const numerator = new BN(scaledNumerator).mul(new BN(10).pow(new BN(decimals)));
      const denominator = new BN(scaledDenominator);
      const quotedAmount = numerator.add(denominator).sub(new BN(1)).div(denominator);

      // Should ceil to ensure user pays enough
      expect(quotedAmount.gt(new BN(0))).toBe(true);
    });
  });

  describe('Quote expiration', () => {
    it('should calculate correct expiration time', () => {
      const now = Math.floor(Date.now() / 1000);
      const lockDuration = 300; // 5 minutes
      const expiresAt = now + lockDuration;

      expect(expiresAt).toBeGreaterThan(now);
      expect(expiresAt - now).toBe(lockDuration);
    });

    it('should use default lock duration of 300 seconds', () => {
      const defaultLockDuration = 300;
      const now = Math.floor(Date.now() / 1000);
      const expiresAt = now + defaultLockDuration;

      expect(expiresAt - now).toBe(300);
    });
  });

  describe('Quote metadata structure', () => {
    it('should include calculation details in metadata', () => {
      const tokenAmount = '100.123456789';
      const unitAmount = '100123456789000000000';

      const metadata = {
        calculation: {
          token_amount_raw: tokenAmount,
          unit_amount_raw: unitAmount,
        },
        rounding: {
          mode: 'ceil',
          token_decimals: 18,
        },
      };

      expect(metadata.calculation.token_amount_raw).toBe(tokenAmount);
      expect(metadata.calculation.unit_amount_raw).toBe(unitAmount);
      expect(metadata.rounding.mode).toBe('ceil');
    });

    it('should include risk information in metadata', () => {
      const metadata = {
        risk: {
          anomaly_detected: false,
          degraded: false,
          degraded_reason: null,
        },
      };

      expect(metadata.risk.anomaly_detected).toBe(false);
      expect(metadata.risk.degraded).toBe(false);
    });
  });

  describe('Validation logic', () => {
    it('should require either session_id or invoice_id', () => {
      const params = {
        price_id: 'price_123',
        target_currency_id: 'currency_456',
        quantity: 1,
      };

      // Neither session_id nor invoice_id provided
      expect(params).not.toHaveProperty('session_id');
      expect(params).not.toHaveProperty('invoice_id');
    });

    it('should validate dynamic pricing type', () => {
      const price = {
        pricing_type: 'fixed',
        base_currency: 'USD',
        base_amount: '10',
      };

      expect(price.pricing_type).not.toBe('dynamic');
    });
  });
});
