import { describe, expect, it } from 'vitest';
import { base91ToNumber, numberToBase91 } from './base91';

describe('Base91 encoding/decoding', () => {
  describe('base91ToNumber', () => {
    it('should decode single Base91 character', () => {
      expect(base91ToNumber('!')).toBe(0);
      expect(base91ToNumber('"')).toBe(1);
      expect(base91ToNumber('#')).toBe(2);
    });

    it('should decode multiple Base91 characters', () => {
      // "!!" = 0 * 91 + 0 = 0
      expect(base91ToNumber('!!')).toBe(0);

      // "!#" = 0 * 91 + 2 = 2
      expect(base91ToNumber('!#')).toBe(2);

      // "#!" = 2 * 91 + 0 = 182
      expect(base91ToNumber('#!')).toBe(182);

      // "##" = 2 * 91 + 2 = 184
      expect(base91ToNumber('##')).toBe(184);
    });

    it('should decode 4-character Base91 strings (used in APRS)', () => {
      // Test with printable ASCII Base91 characters (33-123)
      const testValue = base91ToNumber('!#%\'');
      expect(testValue).toBeGreaterThan(0);
      expect(testValue).toBeLessThan(91 * 91 * 91 * 91);
    });

    it('should decode maximum valid Base91 value', () => {
      // Maximum is '{' (ASCII 123, digit 90) repeated
      const maxValue = base91ToNumber('{{{{');
      const expected = 90 * 91 * 91 * 91 + 90 * 91 * 91 + 90 * 91 + 90;
      expect(maxValue).toBe(expected);
    });

    it('should throw error for invalid Base91 characters', () => {
      expect(() => base91ToNumber(' ')).toThrow('Invalid Base91 character');
      expect(() => base91ToNumber('}')).toThrow('Invalid Base91 character');
      expect(() => base91ToNumber('~')).toThrow('Invalid Base91 character');
    });

    it('should handle APRS compressed position example', () => {
      // Using actual characters from APRS test vector
      const latStr = '/:*E';
      const lonStr = 'qZ=O';

      const latValue = base91ToNumber(latStr);
      const lonValue = base91ToNumber(lonStr);

      // Just verify they decode without error and produce valid numbers
      expect(typeof latValue).toBe('number');
      expect(typeof lonValue).toBe('number');
      expect(latValue).toBeGreaterThanOrEqual(0);
      expect(lonValue).toBeGreaterThanOrEqual(0);
    });
  });

  describe('numberToBase91', () => {
    it('should encode zero', () => {
      expect(numberToBase91(0)).toBe('!');
      expect(numberToBase91(0, 1)).toBe('!');
      expect(numberToBase91(0, 4)).toBe('!!!!');
    });

    it('should encode small numbers', () => {
      expect(numberToBase91(5, 1)).toBe('&');
      expect(numberToBase91(1)).toBe('"');
      expect(numberToBase91(90)).toBe('{');
    });

    it('should encode multi-digit Base91 values', () => {
      // 91 = "!" (1 char to next position) but we get "#!" (2 chars) because it's 1*91
      const encoded = numberToBase91(91);
      expect(encoded.length).toBeGreaterThan(1);
    });

    it('should pad with leading ! when length specified', () => {
      const encoded1 = numberToBase91(5, 1);
      expect(encoded1).toBe('&'); // ASCII 33 + 5 = 38 (&)
      expect(encoded1.length).toBe(1);

      const encoded4 = numberToBase91(5, 4);
      expect(encoded4.length).toBe(4);
      expect(encoded4.endsWith('&')).toBe(true);
      expect(encoded4.startsWith('!')).toBe(true);
    });

    it('should encode and decode consistently', () => {
      const testValues = [0, 1, 42, 91, 182, 1000, 10000, 100000];

      for (const value of testValues) {
        const encoded = numberToBase91(value, 4);
        const decoded = base91ToNumber(encoded);
        expect(decoded).toBe(value);
      }
    });

    it('should handle maximum Base91 4-byte value', () => {
      const maxValue = 91 * 91 * 91 * 91 - 1;
      const encoded = numberToBase91(maxValue, 4);
      expect(encoded.length).toBe(4);

      const decoded = base91ToNumber(encoded);
      expect(decoded).toBe(maxValue);
    });
  });

  describe('Round-trip encoding/decoding', () => {
    it('should preserve values through encode-decode cycle', () => {
      const testCases = [
        { value: 12345, length: 4 },
        { value: 0, length: 4 },
        { value: 65535, length: 4 },
        { value: 1000000, length: 4 },
      ];

      for (const { value, length } of testCases) {
        const encoded = numberToBase91(value, length);
        const decoded = base91ToNumber(encoded);
        expect(decoded).toBe(value);
      }
    });

    it('should work with APRS latitude/longitude encoding pattern', () => {
      // APRS uses 4-character Base91 strings for latitude and longitude
      // Latitude range: 0 to 380926 (representing 90 to -90 degrees)
      const latValues = [0, 190463, 380926]; // ~90N, ~0, ~90S

      for (const value of latValues) {
        const encoded = numberToBase91(value, 4);
        expect(encoded.length).toBe(4);

        const decoded = base91ToNumber(encoded);
        expect(decoded).toBe(value);
      }
    });
  });
});
