import { TaxRate } from '../../src/store/models';

describe('TaxRate.findMatchingRate', () => {
  let mockFindAll: jest.SpyInstance;
  let mockSequelize: any;

  beforeEach(() => {
    mockSequelize = {
      fn: jest.fn(),
      col: jest.fn(),
      where: jest.fn((fn, value) => ({ fn, value })),
      literal: jest.fn((str) => str),
    };

    // Mock the sequelize instance
    Object.defineProperty(TaxRate, 'sequelize', {
      value: mockSequelize,
      writable: true,
      configurable: true,
    });

    // eslint-disable-next-line require-await
    mockFindAll = jest.spyOn(TaxRate, 'findAll').mockImplementation(async () => []);
  });

  afterEach(() => {
    jest.restoreAllMocks();
  });

  describe('Input validation', () => {
    it('should return null when country is empty', async () => {
      const result = await TaxRate.findMatchingRate({
        country: '',
        state: 'CA',
        postalCode: '90210',
        taxCode: 'digital',
      });

      expect(result).toBeNull();
      expect(mockFindAll).not.toHaveBeenCalled();
    });

    it('should return null when country is whitespace', async () => {
      const result = await TaxRate.findMatchingRate({
        country: '   ',
        state: 'CA',
        postalCode: '90210',
        taxCode: 'digital',
      });

      expect(result).toBeNull();
      expect(mockFindAll).not.toHaveBeenCalled();
    });

    it('should normalize country to uppercase', async () => {
      mockFindAll.mockResolvedValueOnce([]);

      await TaxRate.findMatchingRate({
        country: 'us',
        state: 'CA',
        postalCode: '90210',
        taxCode: 'digital',
      });

      const whereClause = mockFindAll.mock.calls[0][0].where;
      expect(whereClause).toBeDefined();
    });
  });

  describe('No candidates found', () => {
    it('should return null when no rates exist for country', async () => {
      mockFindAll.mockResolvedValueOnce([]);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        postalCode: '90210',
        taxCode: 'digital',
      });

      expect(result).toBeNull();
    });

    it('should return null when no active rates exist', async () => {
      mockFindAll.mockResolvedValueOnce([
        {
          id: 'txr_1',
          country: 'US',
          state: 'CA',
          postal_code: '90210',
          tax_code: 'digital',
          percentage: 8.5,
          active: false,
          livemode: true,
          display_name: 'Inactive Rate',
        },
      ]);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        postalCode: '90210',
        taxCode: 'digital',
      });

      expect(result).toBeNull();
    });

    it('should return null when no rates match livemode', async () => {
      mockFindAll.mockResolvedValueOnce([
        {
          id: 'txr_1',
          country: 'US',
          state: 'CA',
          postal_code: '90210',
          tax_code: 'digital',
          percentage: 8.5,
          active: true,
          livemode: false,
          display_name: 'Test Mode Rate',
        },
      ]);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        postalCode: '90210',
        taxCode: 'digital',
        livemode: true,
      });

      expect(result).toBeNull();
    });
  });

  describe('Postal code matching priority', () => {
    it('should prefer exact postal match over state-only match', async () => {
      const rateA = {
        id: 'txr_state',
        country: 'US',
        state: 'CA',
        postal_code: null,
        tax_code: 'digital',
        percentage: 7.5,
        active: true,
        livemode: true,
        display_name: 'CA Digital Tax',
        updated_at: new Date('2024-01-01'),
      };

      const rateB = {
        id: 'txr_postal',
        country: 'US',
        state: 'CA',
        postal_code: '90210',
        tax_code: 'digital',
        percentage: 9.5,
        active: true,
        livemode: true,
        display_name: 'CA 90210 Digital Tax',
        updated_at: new Date('2024-01-01'),
      };

      mockFindAll.mockResolvedValueOnce([rateA, rateB]);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        postalCode: '90210',
        taxCode: 'digital',
      });

      expect(result?.id).toBe('txr_postal');
    });

    it('should match wildcard postal codes', async () => {
      const rate = {
        id: 'txr_wildcard',
        country: 'US',
        state: 'CA',
        postal_code: '902*',
        tax_code: 'digital',
        percentage: 9.0,
        active: true,
        livemode: true,
        display_name: 'CA 902* Digital Tax',
        updated_at: new Date('2024-01-01'),
      };

      mockFindAll.mockResolvedValueOnce([rate]);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        postalCode: '90210',
        taxCode: 'digital',
      });

      expect(result?.id).toBe('txr_wildcard');
    });

    it('should prefer longer wildcard prefix', async () => {
      const rateA = {
        id: 'txr_short',
        country: 'US',
        state: 'CA',
        postal_code: '90*',
        tax_code: 'digital',
        percentage: 8.0,
        active: true,
        livemode: true,
        display_name: 'CA 90* Digital Tax',
        updated_at: new Date('2024-01-01'),
      };

      const rateB = {
        id: 'txr_long',
        country: 'US',
        state: 'CA',
        postal_code: '9021*',
        tax_code: 'digital',
        percentage: 9.0,
        active: true,
        livemode: true,
        display_name: 'CA 9021* Digital Tax',
        updated_at: new Date('2024-01-01'),
      };

      mockFindAll.mockResolvedValueOnce([rateA, rateB]);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        postalCode: '90210',
        taxCode: 'digital',
      });

      expect(result?.id).toBe('txr_long');
    });

    it('should reject wildcard that does not match', async () => {
      const rate = {
        id: 'txr_no_match',
        country: 'US',
        state: 'CA',
        postal_code: '91*',
        tax_code: 'digital',
        percentage: 9.0,
        active: true,
        livemode: true,
        display_name: 'CA 91* Digital Tax',
        updated_at: new Date('2024-01-01'),
      };

      mockFindAll.mockResolvedValueOnce([rate]);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        postalCode: '90210',
        taxCode: 'digital',
      });

      expect(result).toBeNull();
    });

    it('should reject when rate has postal but user does not provide', async () => {
      const rate = {
        id: 'txr_postal_required',
        country: 'US',
        state: 'CA',
        postal_code: '90210',
        tax_code: 'digital',
        percentage: 9.5,
        active: true,
        livemode: true,
        display_name: 'CA 90210 Digital Tax',
        updated_at: new Date('2024-01-01'),
      };

      mockFindAll.mockResolvedValueOnce([rate]);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        taxCode: 'digital',
      });

      expect(result).toBeNull();
    });
  });

  describe('State matching priority', () => {
    it('should prefer state match over country-only match', async () => {
      const rateA = {
        id: 'txr_country',
        country: 'US',
        state: null,
        postal_code: null,
        tax_code: 'digital',
        percentage: 5.0,
        active: true,
        livemode: true,
        display_name: 'US Digital Tax',
        updated_at: new Date('2024-01-01'),
      };

      const rateB = {
        id: 'txr_state',
        country: 'US',
        state: 'CA',
        postal_code: null,
        tax_code: 'digital',
        percentage: 7.5,
        active: true,
        livemode: true,
        display_name: 'CA Digital Tax',
        updated_at: new Date('2024-01-01'),
      };

      mockFindAll.mockResolvedValueOnce([rateA, rateB]);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        taxCode: 'digital',
      });

      expect(result?.id).toBe('txr_state');
    });

    it('should reject when rate has state but user does not provide', async () => {
      const rate = {
        id: 'txr_state_required',
        country: 'US',
        state: 'CA',
        postal_code: null,
        tax_code: 'digital',
        percentage: 7.5,
        active: true,
        livemode: true,
        display_name: 'CA Digital Tax',
        updated_at: new Date('2024-01-01'),
      };

      mockFindAll.mockResolvedValueOnce([rate]);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        taxCode: 'digital',
      });

      expect(result).toBeNull();
    });

    it('should normalize state to lowercase for comparison', async () => {
      const rate = {
        id: 'txr_state',
        country: 'US',
        state: 'ca',
        postal_code: null,
        tax_code: 'digital',
        percentage: 7.5,
        active: true,
        livemode: true,
        display_name: 'CA Digital Tax',
        updated_at: new Date('2024-01-01'),
      };

      mockFindAll.mockResolvedValueOnce([rate]);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        taxCode: 'digital',
      });

      expect(result?.id).toBe('txr_state');
    });
  });

  describe('Tax code matching priority', () => {
    it('should prefer exact tax code match', async () => {
      const rateA = {
        id: 'txr_general',
        country: 'US',
        state: 'CA',
        postal_code: null,
        tax_code: null,
        percentage: 7.5,
        active: true,
        livemode: true,
        display_name: 'CA General Tax',
        updated_at: new Date('2024-01-01'),
      };

      const rateB = {
        id: 'txr_digital',
        country: 'US',
        state: 'CA',
        postal_code: null,
        tax_code: 'digital',
        percentage: 9.5,
        active: true,
        livemode: true,
        display_name: 'CA Digital Tax',
        updated_at: new Date('2024-01-01'),
      };

      mockFindAll.mockResolvedValueOnce([rateA, rateB]);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        taxCode: 'digital',
      });

      expect(result?.id).toBe('txr_digital');
    });

    it('should fallback to general rate when no tax code match', async () => {
      const rate = {
        id: 'txr_general',
        country: 'US',
        state: 'CA',
        postal_code: null,
        tax_code: null,
        percentage: 7.5,
        active: true,
        livemode: true,
        display_name: 'CA General Tax',
        updated_at: new Date('2024-01-01'),
      };

      mockFindAll.mockResolvedValueOnce([rate]);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        taxCode: 'digital',
      });

      expect(result?.id).toBe('txr_general');
    });

    it('should reject when tax code does not match', async () => {
      const rate = {
        id: 'txr_physical',
        country: 'US',
        state: 'CA',
        postal_code: null,
        tax_code: 'physical',
        percentage: 8.5,
        active: true,
        livemode: true,
        display_name: 'CA Physical Goods Tax',
        updated_at: new Date('2024-01-01'),
      };

      mockFindAll.mockResolvedValueOnce([rate]);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        taxCode: 'digital',
      });

      expect(result).toBeNull();
    });
  });

  describe('Hierarchical priority', () => {
    it('should prioritize postal over state+tax combination', async () => {
      const rateA = {
        id: 'txr_state_tax',
        country: 'US',
        state: 'CA',
        postal_code: null,
        tax_code: 'digital',
        percentage: 7.5,
        active: true,
        livemode: true,
        display_name: 'CA Digital Tax',
        updated_at: new Date('2024-01-01'),
      };

      const rateB = {
        id: 'txr_postal',
        country: 'US',
        state: 'CA',
        postal_code: '90210',
        tax_code: null,
        percentage: 9.0,
        active: true,
        livemode: true,
        display_name: 'CA 90210 General Tax',
        updated_at: new Date('2024-01-01'),
      };

      mockFindAll.mockResolvedValueOnce([rateA, rateB]);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        postalCode: '90210',
        taxCode: 'digital',
      });

      expect(result?.id).toBe('txr_postal');
    });

    it('should use state as tiebreaker when postal scores are equal', async () => {
      const rateA = {
        id: 'txr_no_state',
        country: 'US',
        state: null,
        postal_code: null,
        tax_code: 'digital',
        percentage: 5.0,
        active: true,
        livemode: true,
        display_name: 'US Digital Tax',
        updated_at: new Date('2024-01-01'),
      };

      const rateB = {
        id: 'txr_with_state',
        country: 'US',
        state: 'CA',
        postal_code: null,
        tax_code: 'digital',
        percentage: 7.5,
        active: true,
        livemode: true,
        display_name: 'CA Digital Tax',
        updated_at: new Date('2024-01-01'),
      };

      mockFindAll.mockResolvedValueOnce([rateA, rateB]);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        taxCode: 'digital',
      });

      expect(result?.id).toBe('txr_with_state');
    });

    it('should use tax code as final tiebreaker', async () => {
      const rateA = {
        id: 'txr_general',
        country: 'US',
        state: 'CA',
        postal_code: null,
        tax_code: null,
        percentage: 7.5,
        active: true,
        livemode: true,
        display_name: 'CA General Tax',
        updated_at: new Date('2024-01-02'),
      };

      const rateB = {
        id: 'txr_digital',
        country: 'US',
        state: 'CA',
        postal_code: null,
        tax_code: 'digital',
        percentage: 9.5,
        active: true,
        livemode: true,
        display_name: 'CA Digital Tax',
        updated_at: new Date('2024-01-01'),
      };

      mockFindAll.mockResolvedValueOnce([rateA, rateB]);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        taxCode: 'digital',
      });

      expect(result?.id).toBe('txr_digital');
    });

    it('should prefer newer rate when all scores are equal', async () => {
      const rateA = {
        id: 'txr_old',
        country: 'US',
        state: 'CA',
        postal_code: null,
        tax_code: 'digital',
        percentage: 7.5,
        active: true,
        livemode: true,
        display_name: 'CA Digital Tax (Old)',
        updated_at: new Date('2024-01-01'),
      };

      const rateB = {
        id: 'txr_new',
        country: 'US',
        state: 'CA',
        postal_code: null,
        tax_code: 'digital',
        percentage: 8.5,
        active: true,
        livemode: true,
        display_name: 'CA Digital Tax (New)',
        updated_at: new Date('2024-02-01'),
      };

      mockFindAll.mockResolvedValueOnce([rateB, rateA]);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        taxCode: 'digital',
      });

      expect(result?.id).toBe('txr_new');
    });
  });

  describe('Real-world scenarios', () => {
    it('should handle country-level fallback', async () => {
      const rate = {
        id: 'txr_country',
        country: 'US',
        state: null,
        postal_code: null,
        tax_code: null,
        percentage: 5.0,
        active: true,
        livemode: true,
        display_name: 'US General Tax',
        updated_at: new Date('2024-01-01'),
      };

      mockFindAll.mockResolvedValueOnce([rate]);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'TX',
        postalCode: '75001',
        taxCode: 'services',
      });

      expect(result?.id).toBe('txr_country');
    });

    it('should handle complete match hierarchy', async () => {
      const rates = [
        {
          id: 'txr_exact',
          country: 'US',
          state: 'CA',
          postal_code: '90210',
          tax_code: 'digital',
          percentage: 10.0,
          active: true,
          livemode: true,
          display_name: 'Exact Match',
          updated_at: new Date('2024-01-01'),
        },
        {
          id: 'txr_postal',
          country: 'US',
          state: 'CA',
          postal_code: '90210',
          tax_code: null,
          percentage: 9.5,
          active: true,
          livemode: true,
          display_name: 'Postal Match',
          updated_at: new Date('2024-01-01'),
        },
        {
          id: 'txr_state',
          country: 'US',
          state: 'CA',
          postal_code: null,
          tax_code: 'digital',
          percentage: 8.5,
          active: true,
          livemode: true,
          display_name: 'State Match',
          updated_at: new Date('2024-01-01'),
        },
        {
          id: 'txr_country',
          country: 'US',
          state: null,
          postal_code: null,
          tax_code: null,
          percentage: 5.0,
          active: true,
          livemode: true,
          display_name: 'Country Match',
          updated_at: new Date('2024-01-01'),
        },
      ];

      mockFindAll.mockResolvedValueOnce(rates);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        postalCode: '90210',
        taxCode: 'digital',
      });

      expect(result?.id).toBe('txr_exact');
      expect(result?.percentage).toBe(10.0);
    });

    it('should handle missing postal in user input', async () => {
      const rate = {
        id: 'txr_state',
        country: 'US',
        state: 'CA',
        postal_code: null,
        tax_code: 'digital',
        percentage: 8.5,
        active: true,
        livemode: true,
        display_name: 'CA Digital Tax',
        updated_at: new Date('2024-01-01'),
      };

      mockFindAll.mockResolvedValueOnce([rate]);

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        taxCode: 'digital',
      });

      expect(result?.id).toBe('txr_state');
    });
  });

  describe('Error handling', () => {
    it('should handle database errors gracefully', async () => {
      mockFindAll.mockRejectedValueOnce(new Error('Database connection failed'));

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        postalCode: '90210',
        taxCode: 'digital',
      });

      expect(result).toBeNull();
    });

    it('should handle null sequelize instance', async () => {
      const originalSequelize = TaxRate.sequelize;
      // @ts-ignore
      TaxRate.sequelize = null;

      const result = await TaxRate.findMatchingRate({
        country: 'US',
        state: 'CA',
        postalCode: '90210',
        taxCode: 'digital',
      });

      expect(result).toBeNull();

      // @ts-ignore
      TaxRate.sequelize = originalSequelize;
    });
  });
});
