import { describe, it, expect } from 'vitest';
import { Address } from '../src/address';

describe('Address.constructor', () => {
  it('truncates callsign to 6 chars and uppercases; accepts valid SSID', () => {
    const a = new Address('abcdefg', 12);
    expect(a.callsign).toBe('ABCDEF');
    expect(a.ssid).toBe(12);
  });

  describe('input validation', () => {
    it('rejects non-string callsign', () => {
      expect(() => new Address(123 as any, 0)).toThrow(TypeError);
    });

    it('rejects empty callsign', () => {
      expect(() => new Address('   ', 0)).toThrow();
    });

    it('rejects non-integer or out-of-range SSID', () => {
      // non-integer
      expect(() => new Address('ABC', 1.5 as any)).toThrow(TypeError);
      // out of range
      expect(() => new Address('ABC', -1)).toThrow(RangeError);
      expect(() => new Address('ABC', 16)).toThrow(RangeError);
    });
  });
});

describe('Address.fromBytes', () => {
  it('roundtrips to/from bytes and string', () => {
    const a = new Address('NOCALL', 3);
    const b = Address.fromBytes(a.toBytes(true));
    expect(b.callsign).toBe('NOCALL');
    expect(b.ssid).toBe(3);
    expect(a.toString()).toBe('NOCALL-3');
  });

  it('throws on short buffer', () => {
    expect(() => Address.fromBytes(new Uint8Array([1, 2, 3]))).toThrow();
  });
});

describe('Address.fromString', () => {
  it('parses string addresses', () => {
    const a = Address.fromString('FOO-1');
    expect(a.callsign).toBe('FOO');
    expect(a.ssid).toBe(1);
  });

  describe('input validation', () => {
    it('rejects invalid fromString inputs', () => {
      // non-string
      expect(() => Address.fromString(123 as any)).toThrow(TypeError);
      // empty
      expect(() => Address.fromString('-1')).toThrow();
      // non-numeric SSID
      expect(() => Address.fromString('FOO-A')).toThrow();
    });
  });
});

describe('Address.toBytes', () => {
  it('encodes callsign bytes and SSID correctly with and without last bit', () => {
    const a = new Address('AB', 5);
    const b = a.toBytes(false);
    expect(b[0]).toBe('A'.charCodeAt(0) << 1);
    expect(b[1]).toBe('B'.charCodeAt(0) << 1);
    // padding spaces are 0x20 << 1 == 0x40
    expect(b[2]).toBe(0x40);
    expect(b[6]).toBe(0x60 | ((5 << 1) & 0xfe));

    const last = a.toBytes(true);
    expect((last[6] & 0x01)).toBe(1);
  });
});

describe('Address.toString', () => {
  it('omits SSID when showSsid is false and supports custom separator', () => {
    const a = new Address('call', 2);
    expect(a.toString({ showSsid: false })).toBe('CALL');
    expect(a.toString({ sep: ':' })).toBe('CALL:2');
  });
});
