import { createServer } from 'node:http';
import { expect, tap } from '@git.zone/tstest/tapbundle';
import { ElgatoClient, ElgatoIntegration, ElgatoMapper, type IElgatoCommandRequest } from '../../ts/integrations/elgato/index.js';

tap.test('reads Elgato snapshots and executes light commands over native local HTTP', async () => {
  const requests: Array<{ url: string; method?: string; body: string }> = [];
  const server = createServer((requestArg, responseArg) => {
    const chunks: Buffer[] = [];
    requestArg.on('data', (chunkArg) => chunks.push(Buffer.from(chunkArg)));
    requestArg.on('end', () => {
      const body = Buffer.concat(chunks).toString('utf8');
      requests.push({ url: requestArg.url || '', method: requestArg.method, body });
      responseArg.setHeader('content-type', 'application/json');

      if (requestArg.url === '/elgato/accessory-info' && requestArg.method === 'GET') {
        responseArg.end(JSON.stringify({ productName: 'Elgato Key Light', serialNumber: 'ELG123', displayName: 'Desk Key Light', firmwareVersion: '1.0.3', firmwareBuildNumber: 42, hardwareBoardType: 7, macAddress: 'AABBCCDDEEFF', features: ['lights'] }));
        return;
      }
      if (requestArg.url === '/elgato/lights/settings' && requestArg.method === 'GET') {
        responseArg.end(JSON.stringify({ colorChangeDurationMs: 100, powerOnBehavior: 1, powerOnBrightness: 40, switchOffDurationMs: 300, switchOnDurationMs: 300 }));
        return;
      }
      if (requestArg.url === '/elgato/lights' && requestArg.method === 'GET') {
        responseArg.end(JSON.stringify({ numberOfLights: 1, lights: [{ on: 1, brightness: 35, temperature: 200 }] }));
        return;
      }
      if (requestArg.url === '/elgato/lights' && requestArg.method === 'PUT') {
        responseArg.end(JSON.stringify({ ok: true }));
        return;
      }
      if (requestArg.url === '/elgato/identify' && requestArg.method === 'POST') {
        responseArg.end(JSON.stringify({ ok: true }));
        return;
      }
      responseArg.statusCode = 404;
      responseArg.end(JSON.stringify({ error: 'not found' }));
    });
  });

  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 ElgatoClient({ host: '127.0.0.1', port, timeoutMs: 1000 });
    const snapshot = await client.getSnapshot();
    const light = await client.setLight({ on: true, brightness: 50, colorTemperatureKelvin: 5000 });
    const identify = await client.identify();

    expect(snapshot.online).toBeTrue();
    expect(snapshot.source).toEqual('http');
    expect(snapshot.device.serialNumber).toEqual('ELG123');
    expect(snapshot.device.macAddress).toEqual('aa:bb:cc:dd:ee:ff');
    expect(snapshot.light.colorTemperatureKelvin).toEqual(5000);
    expect(light.ok).toBeTrue();
    expect(identify.ok).toBeTrue();
    const putBody = JSON.parse(requests.find((requestArg) => requestArg.url === '/elgato/lights' && requestArg.method === 'PUT')!.body) as { lights: Array<Record<string, unknown>> };
    expect(putBody.lights[0].on).toEqual(1);
    expect(putBody.lights[0].brightness).toEqual(50);
    expect(putBody.lights[0].temperature).toEqual(200);
    expect(requests.some((requestArg) => requestArg.url === '/elgato/identify' && requestArg.method === 'POST')).toBeTrue();
    await client.destroy();
  } finally {
    await new Promise<void>((resolve, reject) => server.close((errorArg) => errorArg ? reject(errorArg) : resolve()));
  }
});

tap.test('does not report live light command success without HTTP transport or executor', async () => {
  const snapshot = ElgatoMapper.toSnapshot({
    config: { name: 'Static Key Light' },
    rawData: {
      info: { productName: 'Elgato Key Light', serialNumber: 'ELGSTATIC', displayName: 'Static Key Light' },
      settings: { colorChangeDurationMs: 100, powerOnBehavior: 1, powerOnBrightness: 40, switchOffDurationMs: 300, switchOnDurationMs: 300 },
      state: { on: 1, brightness: 40, temperature: 200 },
    },
    online: true,
    source: 'manual',
  });
  const runtime = await new ElgatoIntegration().setup({ snapshot }, {});
  const result = await runtime.callService?.({ domain: 'light', service: 'turn_off', target: { entityId: 'light.static_key_light' }, data: {} });

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

tap.test('models Elgato commands through an injected executor', async () => {
  const commands: IElgatoCommandRequest[] = [];
  const snapshot = ElgatoMapper.toSnapshot({
    config: { name: 'Executor Key Light' },
    rawData: {
      info: { productName: 'Elgato Key Light', serialNumber: 'ELGEXEC', displayName: 'Executor Key Light' },
      settings: { colorChangeDurationMs: 100, powerOnBehavior: 1, powerOnBrightness: 40, switchOffDurationMs: 300, switchOnDurationMs: 300 },
      state: { on: 1, brightness: 40, temperature: 200 },
    },
    online: true,
    source: 'manual',
  });
  const runtime = await new ElgatoIntegration().setup({
    snapshot,
    commandExecutor: {
      execute: async (requestArg) => {
        commands.push(requestArg);
        return { ok: true, accepted: requestArg.action };
      },
    },
  }, {});

  const result = await runtime.callService?.({ domain: 'button', service: 'press', target: { entityId: 'button.executor_key_light_identify' } });

  expect(result?.success).toBeTrue();
  expect(commands[0].action).toEqual('identify');
  await runtime.destroy();
});

export default tap.start();
