import { describe, expect, it } from 'vitest';
import {
  normalizeDocumentAmount,
} from '../helpers/document-amount.helper.js';

import { DocumentType } from '../../../shared/enums.js';

describe('Document Amount Normalization', () => {
  describe('INVOICE - Business is Debtor (User is Creditor)', () => {
    it('should return positive amount when business is debtor', () => {
      // Business owes user (user is creditor, business is debtor)
      const result = normalizeDocumentAmount(100, false, DocumentType.Invoice);
      expect(result).toBe(100);
    });

    it('should handle decimal amounts', () => {
      const result = normalizeDocumentAmount(150.75, false, DocumentType.Invoice);
      expect(result).toBe(150.75);
    });

    it('should handle large amounts', () => {
      const result = normalizeDocumentAmount(1000000, false, DocumentType.Invoice);
      expect(result).toBe(1000000);
    });
  });

  describe('INVOICE - Business is Creditor (User is Debtor)', () => {
    it('should return negative amount when business is creditor', () => {
      // User owes business (user is debtor, business is creditor)
      const result = normalizeDocumentAmount(100, true, DocumentType.Invoice);
      expect(result).toBe(-100);
    });

    it('should handle decimal amounts', () => {
      const result = normalizeDocumentAmount(250.50, true, DocumentType.Invoice);
      expect(result).toBe(-250.50);
    });

    it('should handle large amounts', () => {
      const result = normalizeDocumentAmount(5000000, true, DocumentType.Invoice);
      expect(result).toBe(-5000000);
    });
  });

  describe('CREDIT_INVOICE - Business is Debtor (User is Creditor)', () => {
    it('should return negative amount for credit invoice with business debtor', () => {
      // Credit invoice: user owes refund to business (opposite of regular invoice)
      const result = normalizeDocumentAmount(100, false, DocumentType.CreditInvoice);
      expect(result).toBe(-100);
    });

    it('should handle decimal amounts', () => {
      const result = normalizeDocumentAmount(75.25, false, DocumentType.CreditInvoice);
      expect(result).toBe(-75.25);
    });

    it('should handle large amounts', () => {
      const result = normalizeDocumentAmount(2000000, false, DocumentType.CreditInvoice);
      expect(result).toBe(-2000000);
    });
  });

  describe('CREDIT_INVOICE - Business is Creditor (User is Debtor)', () => {
    it('should return positive amount for credit invoice with business creditor (double negation)', () => {
      // Credit invoice: business owes refund to user (double negation: -1 * -1 = 1)
      const result = normalizeDocumentAmount(100, true, DocumentType.CreditInvoice);
      expect(result).toBe(100);
    });

    it('should handle decimal amounts', () => {
      const result = normalizeDocumentAmount(125.99, true, DocumentType.CreditInvoice);
      expect(result).toBe(125.99);
    });

    it('should handle large amounts', () => {
      const result = normalizeDocumentAmount(3000000, true, DocumentType.CreditInvoice);
      expect(result).toBe(3000000);
    });
  });

  describe('RECEIPT - Business is Debtor (User is Creditor)', () => {
    it('should return positive amount when business is debtor', () => {
      const result = normalizeDocumentAmount(100, false, DocumentType.Receipt);
      expect(result).toBe(100);
    });

    it('should handle typical receipt amounts', () => {
      const result = normalizeDocumentAmount(49.99, false, DocumentType.Receipt);
      expect(result).toBe(49.99);
    });
  });

  describe('RECEIPT - Business is Creditor (User is Debtor)', () => {
    it('should return negative amount when business is creditor', () => {
      const result = normalizeDocumentAmount(100, true, DocumentType.Receipt);
      expect(result).toBe(-100);
    });

    it('should handle typical receipt amounts', () => {
      const result = normalizeDocumentAmount(89.50, true, DocumentType.Receipt);
      expect(result).toBe(-89.50);
    });
  });

  describe('INVOICE_RECEIPT - Business is Debtor (User is Creditor)', () => {
    it('should return positive amount when business is debtor', () => {
      const result = normalizeDocumentAmount(100, false, DocumentType.InvoiceReceipt);
      expect(result).toBe(100);
    });

    it('should handle combined invoice-receipt amounts', () => {
      const result = normalizeDocumentAmount(350.00, false, DocumentType.InvoiceReceipt);
      expect(result).toBe(350.00);
    });
  });

  describe('INVOICE_RECEIPT - Business is Creditor (User is Debtor)', () => {
    it('should return negative amount when business is creditor', () => {
      const result = normalizeDocumentAmount(100, true, DocumentType.InvoiceReceipt);
      expect(result).toBe(-100);
    });

    it('should handle combined invoice-receipt amounts', () => {
      const result = normalizeDocumentAmount(425.75, true, DocumentType.InvoiceReceipt);
      expect(result).toBe(-425.75);
    });
  });

  describe('OTHER Document Type', () => {
    it('should handle OTHER type with business debtor', () => {
      const result = normalizeDocumentAmount(100, false, DocumentType.Other);
      expect(result).toBe(100);
    });

    it('should handle OTHER type with business creditor', () => {
      const result = normalizeDocumentAmount(100, true, DocumentType.Other);
      expect(result).toBe(-100);
    });
  });

  describe('PROFORMA Document Type', () => {
    it('should handle PROFORMA type with business debtor', () => {
      const result = normalizeDocumentAmount(100, false, DocumentType.Proforma);
      expect(result).toBe(100);
    });

    it('should handle PROFORMA type with business creditor', () => {
      const result = normalizeDocumentAmount(100, true, DocumentType.Proforma);
      expect(result).toBe(-100);
    });
  });

  describe('UNPROCESSED Document Type', () => {
    it('should handle UNPROCESSED type with business debtor', () => {
      const result = normalizeDocumentAmount(100, false, DocumentType.Unprocessed);
      expect(result).toBe(100);
    });

    it('should handle UNPROCESSED type with business creditor', () => {
      const result = normalizeDocumentAmount(100, true, DocumentType.Unprocessed);
      expect(result).toBe(-100);
    });
  });

  describe('Negative Input Amounts (Absolute Value First)', () => {
    it('should convert negative input to positive for INVOICE with business debtor', () => {
      // Input is -100, should become |−100| = 100
      const result = normalizeDocumentAmount(-100, false, DocumentType.Invoice);
      expect(result).toBe(100);
    });

    it('should convert negative input to negative for INVOICE with business creditor', () => {
      // Input is -100, should become |−100| * -1 = -100
      const result = normalizeDocumentAmount(-100, true, DocumentType.Invoice);
      expect(result).toBe(-100);
    });

    it('should convert negative input for CREDIT_INVOICE with business debtor', () => {
      // Input is -100, should become |−100| * -1 = -100
      const result = normalizeDocumentAmount(-100, false, DocumentType.CreditInvoice);
      expect(result).toBe(-100);
    });

    it('should convert negative input for CREDIT_INVOICE with business creditor', () => {
      // Input is -100, should become |−100| * -1 * -1 = 100
      const result = normalizeDocumentAmount(-100, true, DocumentType.CreditInvoice);
      expect(result).toBe(100);
    });

    it('should handle negative decimal amounts', () => {
      const result = normalizeDocumentAmount(-250.50, false, DocumentType.Invoice);
      expect(result).toBe(250.50);
    });

    it('should handle large negative amounts', () => {
      const result = normalizeDocumentAmount(-1000000, true, DocumentType.Receipt);
      expect(result).toBe(-1000000);
    });
  });

  describe('Zero Amounts', () => {
    it('should handle zero amount with business debtor', () => {
      const result = normalizeDocumentAmount(0, false, DocumentType.Invoice);
      expect(result).toBe(0);
    });

    it('should handle zero amount with business creditor', () => {
      const result = normalizeDocumentAmount(0, true, DocumentType.Invoice);
      expect(result).toBe(-0); // JavaScript has -0, but 0 === -0 is true
    });

    it('should handle zero for CREDIT_INVOICE', () => {
      const result = normalizeDocumentAmount(0, false, DocumentType.CreditInvoice);
      expect(result).toBe(-0);
    });

    it('should verify zero equality regardless of sign', () => {
      const result1 = normalizeDocumentAmount(0, false, DocumentType.Invoice);
      const result2 = normalizeDocumentAmount(0, true, DocumentType.Invoice);
      expect(result1).toBe(0);
      // In JavaScript, -0 and 0 are loosely equal but Object.is distinguishes them
      // For our purposes, they are functionally equivalent
      expect(result2 == 0).toBe(true); // Using == instead of === for -0 case
    });
  });

  describe('Very Large Amounts', () => {
    it('should handle very large positive amounts', () => {
      const largeAmount = 999999999.99;
      const result = normalizeDocumentAmount(largeAmount, false, DocumentType.Invoice);
      expect(result).toBe(largeAmount);
    });

    it('should handle very large amounts with creditor negation', () => {
      const largeAmount = 999999999.99;
      const result = normalizeDocumentAmount(largeAmount, true, DocumentType.Invoice);
      expect(result).toBe(-largeAmount);
    });

    it('should handle very large amounts with CREDIT_INVOICE', () => {
      const largeAmount = 888888888.88;
      const result = normalizeDocumentAmount(largeAmount, true, DocumentType.CreditInvoice);
      expect(result).toBe(largeAmount);
    });

    it('should handle maximum safe integer', () => {
      const maxSafe = Number.MAX_SAFE_INTEGER;
      const result = normalizeDocumentAmount(maxSafe, false, DocumentType.Invoice);
      expect(result).toBe(maxSafe);
    });
  });

  describe('Small Decimal Amounts', () => {
    it('should handle very small decimal amounts', () => {
      const result = normalizeDocumentAmount(0.01, false, DocumentType.Invoice);
      expect(result).toBe(0.01);
    });

    it('should handle fractional cents', () => {
      const result = normalizeDocumentAmount(0.001, true, DocumentType.Receipt);
      expect(result).toBe(-0.001);
    });

    it('should preserve precision for small decimals', () => {
      const result = normalizeDocumentAmount(1.23456789, false, DocumentType.Invoice);
      expect(result).toBe(1.23456789);
    });
  });

  describe('All Document Types Summary Table', () => {
    // This test documents the expected behavior for all combinations
    it('should correctly normalize all document type and business role combinations', () => {
      const testCases: Array<{
        type: DocumentType;
        isCreditor: boolean;
        input: number;
        expected: number;
        description: string;
      }> = [
        // INVOICE
        { type: DocumentType.Invoice, isCreditor: false, input: 100, expected: 100, description: 'INVOICE, business debtor' },
        { type: DocumentType.Invoice, isCreditor: true, input: 100, expected: -100, description: 'INVOICE, business creditor' },
        
        // CREDIT_INVOICE
        { type: DocumentType.CreditInvoice, isCreditor: false, input: 100, expected: -100, description: 'CREDIT_INVOICE, business debtor' },
        { type: DocumentType.CreditInvoice, isCreditor: true, input: 100, expected: 100, description: 'CREDIT_INVOICE, business creditor (double neg)' },
        
        // RECEIPT
        { type: DocumentType.Receipt, isCreditor: false, input: 100, expected: 100, description: 'RECEIPT, business debtor' },
        { type: DocumentType.Receipt, isCreditor: true, input: 100, expected: -100, description: 'RECEIPT, business creditor' },
        
        // INVOICE_RECEIPT
        { type: DocumentType.InvoiceReceipt, isCreditor: false, input: 100, expected: 100, description: 'INVOICE_RECEIPT, business debtor' },
        { type: DocumentType.InvoiceReceipt, isCreditor: true, input: 100, expected: -100, description: 'INVOICE_RECEIPT, business creditor' },
        
        // OTHER
        { type: DocumentType.Other, isCreditor: false, input: 100, expected: 100, description: 'OTHER, business debtor' },
        { type: DocumentType.Other, isCreditor: true, input: 100, expected: -100, description: 'OTHER, business creditor' },
        
        // PROFORMA
        { type: DocumentType.Proforma, isCreditor: false, input: 100, expected: 100, description: 'PROFORMA, business debtor' },
        { type: DocumentType.Proforma, isCreditor: true, input: 100, expected: -100, description: 'PROFORMA, business creditor' },
        
        // UNPROCESSED
        { type: DocumentType.Unprocessed, isCreditor: false, input: 100, expected: 100, description: 'UNPROCESSED, business debtor' },
        { type: DocumentType.Unprocessed, isCreditor: true, input: 100, expected: -100, description: 'UNPROCESSED, business creditor' },
      ];

      testCases.forEach(({ type, isCreditor, input, expected, description }) => {
        const result = normalizeDocumentAmount(input, isCreditor, type);
        expect(result).toBe(expected);
      });
    });
  });

  describe('Real-World Scenarios', () => {
    it('should normalize customer invoice (business sends invoice to customer)', () => {
      // Business is creditor (customer owes business)
      const result = normalizeDocumentAmount(1500, true, DocumentType.Invoice);
      expect(result).toBe(-1500); // Negative because user owes
    });

    it('should normalize vendor invoice (vendor sends invoice to business)', () => {
      // Business is debtor (business owes vendor)
      const result = normalizeDocumentAmount(750, false, DocumentType.Invoice);
      expect(result).toBe(750); // Positive because vendor owes user
    });

    it('should normalize customer refund (credit invoice to customer)', () => {
      // Business owes customer a refund
      const result = normalizeDocumentAmount(200, true, DocumentType.CreditInvoice);
      expect(result).toBe(200); // Positive because business owes refund
    });

    it('should normalize vendor credit note (credit invoice from vendor)', () => {
      // Vendor gives credit note to business
      const result = normalizeDocumentAmount(150, false, DocumentType.CreditInvoice);
      expect(result).toBe(-150); // Negative because business owes vendor less now
    });

    it('should normalize payment receipt from customer', () => {
      // Customer pays business
      const result = normalizeDocumentAmount(500, true, DocumentType.Receipt);
      expect(result).toBe(-500); // Negative because customer paid
    });

    it('should normalize payment receipt to vendor', () => {
      // Business pays vendor
      const result = normalizeDocumentAmount(300, false, DocumentType.Receipt);
      expect(result).toBe(300); // Positive because business paid
    });
  });
});
