import { expect, tap } from '@git.zone/tstest/tapbundle';
import { DaikinConfigFlow, DaikinMapper, createDaikinDiscoveryDescriptor, type IDaikinRawData } from '../../ts/integrations/daikin/index.js';

tap.test('matches Daikin zeroconf records and creates config flow output', async () => {
  const descriptor = createDaikinDiscoveryDescriptor();
  const matcher = descriptor.getMatchers().find((matcherArg) => matcherArg.id === 'daikin-zeroconf-match');
  const result = await matcher!.matches({ type: '_dkapi._tcp.local.', name: 'Living Daikin._dkapi._tcp.local.', host: 'daikin.local', port: 80, properties: { mac: 'AABBCCDDEEFF', name: 'Living Daikin' } }, {});

  expect(result.matched).toBeTrue();
  expect(result.candidate?.integrationDomain).toEqual('daikin');
  expect(result.candidate?.host).toEqual('daikin.local');
  expect(result.candidate?.macAddress).toEqual('aa:bb:cc:dd:ee:ff');

  const done = await (await new DaikinConfigFlow().start(result.candidate!, {})).submit!({});
  expect(done.kind).toEqual('done');
  expect(done.config?.host).toEqual('daikin.local');
  expect(done.config?.port).toEqual(80);
  expect(done.config?.macAddress).toEqual('aa:bb:cc:dd:ee:ff');
});

tap.test('matches UDP discovery payloads from pydaikin basic_info', async () => {
  const descriptor = createDaikinDiscoveryDescriptor();
  const matcher = descriptor.getMatchers().find((matcherArg) => matcherArg.id === 'daikin-udp-match');
  const result = await matcher!.matches({ host: '192.0.2.10', port: 30050, data: 'ret=OK,type=aircon,name=Office%20Daikin,mac=112233445566,ver=1_0_0' }, {});

  expect(result.matched).toBeTrue();
  expect(result.normalizedDeviceId).toEqual('11:22:33:44:55:66');
  expect(result.candidate?.host).toEqual('192.0.2.10');
  expect(result.candidate?.port).toEqual(80);
  expect(result.candidate?.metadata?.discoveryProtocol).toEqual('udp');
});

tap.test('matches manual snapshots without requiring live HTTP credentials', async () => {
  const rawData: Partial<IDaikinRawData> = {
    basicInfo: { name: 'Manual%20Daikin', mac: 'AABBCCDDEEFF' },
    controlInfo: { pow: '0', mode: '3', stemp: '22' },
    sensorInfo: { htemp: '20' },
  };
  const snapshot = DaikinMapper.toSnapshot({ config: { name: 'Manual Daikin' }, rawData, online: true, source: 'manual' });
  const descriptor = createDaikinDiscoveryDescriptor();
  const manualMatcher = descriptor.getMatchers().find((matcherArg) => matcherArg.id === 'daikin-manual-match');
  const match = await manualMatcher!.matches({ name: 'Manual Daikin', snapshot }, {});

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

  const done = await (await new DaikinConfigFlow().start(match.candidate!, {})).submit!({});
  expect(done.kind).toEqual('done');
  expect(done.config?.snapshot?.climate.hvacMode).toEqual('off');
});

tap.test('rejects mixed API key and password authentication', async () => {
  const done = await (await new DaikinConfigFlow().start({ source: 'manual', integrationDomain: 'daikin', host: 'daikin.local' }, {})).submit!({ apiKey: 'key', password: 'pass' });
  expect(done.kind).toEqual('error');
  expect(done.error).toContain('either API key or password');
});

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

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

export default tap.start();
