import { expect, tap } from '@git.zone/tstest/tapbundle';
import { HyperionConfigFlow, HyperionMapper, createHyperionDiscoveryDescriptor, type IHyperionRawData } from '../../ts/integrations/hyperion/index.js';

const rawData: IHyperionRawData = {
  sysInfo: { hyperion: { id: 'hyperion-uuid', version: '2.0.17' } },
  serverInfo: {
    hostname: 'hyperion-host',
    instance: [{ instance: 0, friendly_name: 'Living Hyperion', running: true }],
    components: [{ name: 'ALL', enabled: true }, { name: 'LEDDEVICE', enabled: true }],
    effects: [{ name: 'Rainbow swirl' }],
    adjustment: [{ id: 'default', brightness: 80 }],
    priorities: [{ priority: 128, componentId: 'COLOR', origin: 'smarthome.exchange', visible: true, value: { RGB: [1, 2, 3] } }],
  },
};

tap.test('matches Hyperion SSDP records and creates TCP JSON config', async () => {
  const descriptor = createHyperionDiscoveryDescriptor();
  const matcher = descriptor.getMatchers().find((matcherArg) => matcherArg.id === 'hyperion-ssdp-match');
  const result = await matcher!.matches({
    ssdp_location: 'http://192.0.2.20:8090/description.xml',
    ssdp_st: 'urn:hyperion-project.org:device:basic:1',
    manufacturer: 'Hyperion Open Source Ambient Lighting',
    serialNumber: 'f9aab089-f85a-55cf-b7c1-222a72faebe9',
    ports: { jsonServer: '19444', sslServer: '8092' },
    friendlyName: 'Hyperion (192.0.2.20)',
  }, {});

  expect(result.matched).toBeTrue();
  expect(result.normalizedDeviceId).toEqual('f9aab089-f85a-55cf-b7c1-222a72faebe9');
  expect(result.candidate?.host).toEqual('192.0.2.20');
  expect(result.candidate?.port).toEqual(19444);
  expect(result.candidate?.metadata?.webPort).toEqual(8090);

  const done = await (await new HyperionConfigFlow().start(result.candidate!, {})).submit!({});
  expect(done.kind).toEqual('done');
  expect(done.config?.transport).toEqual('tcp');
  expect(done.config?.host).toEqual('192.0.2.20');
  expect(done.config?.jsonPort).toEqual(19444);
  expect(done.config?.webPort).toEqual(8090);
});

tap.test('matches manual Hyperion snapshots without requiring live transport', async () => {
  const snapshot = HyperionMapper.toSnapshot({ config: { name: 'Manual Hyperion' }, rawData, online: true, source: 'manual' });
  const descriptor = createHyperionDiscoveryDescriptor();
  const matcher = descriptor.getMatchers().find((matcherArg) => matcherArg.id === 'hyperion-manual-match');
  const result = await matcher!.matches({ name: 'Manual Hyperion', snapshot }, {});

  expect(result.matched).toBeTrue();
  expect(result.candidate?.metadata?.snapshot).toEqual(snapshot);
  const validation = await descriptor.getValidators()[0].validate(result.candidate!, {});
  expect(validation.matched).toBeTrue();

  const done = await (await new HyperionConfigFlow().start(result.candidate!, {})).submit!({});
  expect(done.kind).toEqual('done');
  expect(done.config?.snapshot?.device.id).toEqual('hyperion-uuid');
});

tap.test('rejects candidates without Hyperion hints or usable data', async () => {
  const descriptor = createHyperionDiscoveryDescriptor();
  const matcher = descriptor.getMatchers().find((matcherArg) => matcherArg.id === 'hyperion-manual-match');
  const result = await matcher!.matches({ name: 'Generic JSON service' }, {});
  expect(result.matched).toBeFalse();

  const validation = await descriptor.getValidators()[0].validate({ source: 'manual', name: 'Hyperion without endpoint' }, {});
  expect(validation.matched).toBeFalse();
});

export default tap.start();
