import { expect, tap } from '@git.zone/tstest/tapbundle';
import { AirosConfigFlow, createAirosDiscoveryDescriptor, parseAirosDiscoveryPacket, type IAirosSnapshot } from '../../ts/integrations/airos/index.js';

const snapshot: IAirosSnapshot = {
  connected: true,
  source: 'snapshot',
  device: {
    id: 'aa:bb:cc:dd:ee:ff',
    name: 'Roof Bridge',
    host: '192.168.1.20',
    macAddress: 'aa:bb:cc:dd:ee:ff',
    manufacturer: 'Ubiquiti',
    modelName: 'LiteBeam 5AC',
    firmwareVersion: 'v8.7.19',
    firmwareMajor: 8,
  },
  host: { hostname: 'Roof Bridge', uptime: 123, cpuLoad: 3.5, netRole: 'bridge' },
  services: { dhcpClient: true, dhcpServer: false, pppoe: false, portForwarding: false },
  wireless: { essid: 'Backhaul', frequency: 5805, distance: 1200, role: 'access_point', mode: 'point_to_point' },
  derived: { mac: 'aa:bb:cc:dd:ee:ff', fwMajor: 8, role: 'access_point', mode: 'point_to_point' },
  events: [],
};

tap.test('parses native airOS UDP discovery packets and matches candidates', async () => {
  const packet = Buffer.concat([
    Buffer.from([0x01, 0x06, 0x00, 0x00, 0x00, 0x00]),
    Buffer.from([0x06, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]),
    tlv(0x0b, 'Roof Bridge'),
    tlv(0x03, 'v8.7.19'),
    tlv(0x0c, 'LBE-5AC'),
    tlv(0x14, 'LiteBeam 5AC'),
    Buffer.from([0x0a, 0x00, 0x04, 0x00, 0x00, 0x04, 0xd2]),
    Buffer.from([0x18, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00]),
  ]);
  const parsed = parseAirosDiscoveryPacket(packet, '192.168.1.20');
  const descriptor = createAirosDiscoveryDescriptor();
  const matcher = descriptor.getMatchers().find((matcherArg) => matcherArg.id === 'airos-udp-broadcast-match');
  const result = await matcher?.matches(parsed, {});

  expect(parsed.mac_address).toEqual('aa:bb:cc:dd:ee:ff');
  expect(parsed.hostname).toEqual('Roof Bridge');
  expect(parsed.firmware_version).toEqual('v8.7.19');
  expect(parsed.uptime_seconds).toEqual(1234);
  expect(result?.matched).toBeTrue();
  expect(result?.confidence).toEqual('certain');
  expect(result?.candidate?.integrationDomain).toEqual('airos');
  expect(result?.candidate?.host).toEqual('192.168.1.20');
  expect(result?.candidate?.metadata?.discoveryProtocol).toEqual('airos-udp-broadcast');
});

tap.test('matches DHCP registered-device and manual snapshot candidates', async () => {
  const descriptor = createAirosDiscoveryDescriptor();
  const dhcpMatcher = descriptor.getMatchers().find((matcherArg) => matcherArg.id === 'airos-dhcp-registered-device-match');
  const manualMatcher = descriptor.getMatchers().find((matcherArg) => matcherArg.id === 'airos-manual-match');
  const dhcp = await dhcpMatcher?.matches({
    integrationDomain: 'airos',
    ipAddress: '192.168.1.20',
    macaddress: 'AA-BB-CC-DD-EE-FF',
    hostname: 'Roof Bridge',
    registeredDevice: true,
  }, {});
  const manual = await manualMatcher?.matches({ snapshot }, {});

  expect(dhcp?.matched).toBeTrue();
  expect(dhcp?.confidence).toEqual('certain');
  expect(dhcp?.candidate?.macAddress).toEqual('aa:bb:cc:dd:ee:ff');
  expect(dhcp?.candidate?.metadata?.discoveryProtocol).toEqual('dhcp-registered-device');
  expect(manual?.matched).toBeTrue();
  expect(manual?.confidence).toEqual('certain');
  expect(manual?.candidate?.metadata?.snapshot).toEqual(snapshot);
});

tap.test('builds config flow output and validates local airOS inputs', async () => {
  const flow = new AirosConfigFlow();
  const step = await flow.start({
    source: 'custom',
    integrationDomain: 'airos',
    id: 'aa:bb:cc:dd:ee:ff',
    host: '192.168.1.20',
    name: 'Roof Bridge',
    metadata: { firmwareVersion: 'v8.7.19' },
  }, {});

  const done = await step.submit?.({ password: 'ubnt-password', ssl: true, verifySsl: false, timeoutMs: '2500' });
  expect(done?.kind).toEqual('done');
  expect(done?.config?.host).toEqual('192.168.1.20');
  expect(done?.config?.username).toEqual('ubnt');
  expect(done?.config?.password).toEqual('ubnt-password');
  expect(done?.config?.ssl).toBeTrue();
  expect(done?.config?.verifySsl).toBeFalse();
  expect(done?.config?.timeoutMs).toEqual(2500);

  const missingPassword = await step.submit?.({});
  expect(missingPassword?.kind).toEqual('error');
  expect(missingPassword?.error || '').toContain('password');

  const snapshotDone = await step.submit?.({ snapshotJson: JSON.stringify(snapshot) });
  expect(snapshotDone?.kind).toEqual('done');
  expect(snapshotDone?.config?.snapshot?.device.modelName).toEqual('LiteBeam 5AC');

  const invalidSnapshot = await step.submit?.({ password: 'ubnt-password', snapshotJson: '{"connected":true}' });
  expect(invalidSnapshot?.kind).toEqual('error');
});

const tlv = (typeArg: number, valueArg: string): Buffer => {
  const value = Buffer.from(valueArg, 'utf8');
  const header = Buffer.alloc(3);
  header[0] = typeArg;
  header.writeUInt16BE(value.length, 1);
  return Buffer.concat([header, value]);
};

export default tap.start();
