import { createServer } from 'node:http';
import { expect, tap } from '@git.zone/tstest/tapbundle';
import { DaikinClient, DaikinIntegration, DaikinMapper } from '../../ts/integrations/daikin/index.js';

tap.test('reads Daikin BRP069 data over native local HTTP and writes set_control_info', async () => {
  const requests: Array<{ url: string; method?: string }> = [];
  const server = createServer((requestArg, responseArg) => {
    requests.push({ url: requestArg.url || '', method: requestArg.method });
    responseArg.setHeader('content-type', 'text/plain');
    const url = new URL(requestArg.url || '/', 'http://127.0.0.1');

    if (url.pathname === '/common/basic_info') {
      responseArg.end('ret=OK,type=aircon,name=Living%20Daikin,mac=AABBCCDDEEFF,ver=1_2_3');
      return;
    }
    if (url.pathname === '/aircon/get_sensor_info') {
      responseArg.end('ret=OK,htemp=21.5,otemp=8,hhum=44,cmpfreq=20');
      return;
    }
    if (url.pathname === '/aircon/get_model_info') {
      responseArg.end('ret=OK,model=FTXM35R');
      return;
    }
    if (url.pathname === '/aircon/get_control_info' || url.pathname === '/aircon/get_target') {
      responseArg.end('ret=OK,pow=1,mode=3,stemp=22,shum=--,f_rate=A,f_dir=0,dt3=22,dh3=--,dfr3=A');
      return;
    }
    if (url.pathname === '/common/get_holiday') {
      responseArg.end('ret=OK,en_hol=0');
      return;
    }
    if (url.pathname === '/common/get_notify') {
      responseArg.end('ret=OK,adv=');
      return;
    }
    if (url.pathname === '/aircon/get_day_power_ex') {
      responseArg.end('ret=OK,curr_day_cool=1/2,curr_day_heat=3/4');
      return;
    }
    if (url.pathname === '/aircon/get_week_power') {
      responseArg.end('ret=OK,datas=0/100/200');
      return;
    }
    if (url.pathname === '/aircon/get_year_power') {
      responseArg.end('ret=OK,this_year=1/2,previous_year=0/0');
      return;
    }
    if (url.pathname === '/aircon/set_control_info') {
      expect(url.searchParams.get('pow')).toEqual('1');
      expect(url.searchParams.get('mode')).toEqual('3');
      expect(url.searchParams.get('stemp')).toEqual('23.5');
      responseArg.end('ret=OK');
      return;
    }
    responseArg.statusCode = 404;
    responseArg.end('ret=PARAM NG');
  });

  await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', resolve));
  try {
    const address = server.address();
    const port = typeof address === 'object' && address ? address.port : 0;
    const client = new DaikinClient({ host: '127.0.0.1', port, timeoutMs: 1000 });
    const snapshot = await client.getSnapshot();
    const command = DaikinMapper.commandForService(snapshot, { domain: 'climate', service: 'set_temperature', target: { entityId: 'climate.living_daikin' }, data: { temperature: 23.5 } });
    await client.execute(command!);

    expect(snapshot.online).toBeTrue();
    expect(snapshot.source).toEqual('http');
    expect(snapshot.device.name).toEqual('Living Daikin');
    expect(snapshot.device.macAddress).toEqual('aa:bb:cc:dd:ee:ff');
    expect(snapshot.climate.hvacMode).toEqual('cool');
    expect(snapshot.climate.insideTemperature).toEqual(21.5);
    expect(snapshot.energy.energyToday).toEqual(1);
    expect(requests.some((requestArg) => requestArg.url.startsWith('/common/basic_info'))).toBeTrue();
    expect(requests.some((requestArg) => requestArg.url.startsWith('/aircon/set_control_info'))).toBeTrue();
  } finally {
    await new Promise<void>((resolve, reject) => server.close((errorArg) => errorArg ? reject(errorArg) : resolve()));
  }
});

tap.test('does not report live command success without HTTP transport or executor', async () => {
  const snapshot = DaikinMapper.toSnapshot({
    config: { name: 'Static Daikin' },
    rawData: {
      basicInfo: { name: 'Static%20Daikin', mac: 'AABBCCDDEEFF' },
      controlInfo: { pow: '1', mode: '3', stemp: '22' },
      sensorInfo: { htemp: '21' },
    },
    online: true,
    source: 'manual',
  });
  const runtime = await new DaikinIntegration().setup({ snapshot }, {});
  const result = await runtime.callService?.({ domain: 'climate', service: 'turn_off', target: { entityId: 'climate.static_daikin' }, data: {} });

  expect(result?.success).toBeFalse();
  expect(result?.error).toContain('Static snapshots/manual data are read-only');
  await runtime.destroy();
});

tap.test('delegates Daikin commands to an injected executor', async () => {
  const commands: unknown[] = [];
  const snapshot = DaikinMapper.toSnapshot({
    config: { name: 'Executor Daikin' },
    rawData: {
      basicInfo: { name: 'Executor%20Daikin', mac: 'AABBCCDDEEFF' },
      controlInfo: { pow: '1', mode: '3', stemp: '22' },
      sensorInfo: { htemp: '21' },
    },
    online: true,
    source: 'manual',
  });
  const runtime = await new DaikinIntegration().setup({
    snapshot,
    commandExecutor: {
      execute: async (requestArg) => {
        commands.push(requestArg);
        return { accepted: true };
      },
    },
  }, {});

  const result = await runtime.callService?.({ domain: 'climate', service: 'set_hvac_mode', target: {}, data: { hvac_mode: 'heat' } });

  expect(result?.success).toBeTrue();
  expect((commands[0] as { action?: string }).action).toEqual('set_control');
  expect(((commands[0] as { settings?: { mode?: string } }).settings)?.mode).toEqual('hot');
  await runtime.destroy();
});

export default tap.start();
