import { createServer } from 'node:net';
import { expect, tap } from '@git.zone/tstest/tapbundle';
import { KwbClient, KwbIntegration, type IKwbSnapshot } from '../../ts/integrations/kwb/index.js';

tap.test('reads KWB Easyfire TCP byte-stream packets into sensor snapshots', async () => {
  const payload = Buffer.concat([sensePacket([21.5, 20.1, 64.4, 250.2, 48, 47.5, -3.2, 120.5, 12.3, 0, 0, 0, 55.5]), controlPacket()]);
  const server = createServer((socketArg) => socketArg.end(payload));
  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 KwbClient({ host: '127.0.0.1', port, timeoutMs: 1000 });
    const snapshot = await client.getSnapshot(true);

    expect(snapshot.online).toBeTrue();
    expect(snapshot.source).toEqual('tcp');
    expect(snapshot.entities.find((entityArg) => entityArg.id === 'supply')?.state).toEqual(21.5);
    expect(snapshot.entities.find((entityArg) => entityArg.id === 'return_mixer')?.state).toEqual(1);

    const runtime = await new KwbIntegration().setup({ host: '127.0.0.1', port, timeoutMs: 1000 }, {});
    const status = await runtime.callService!({ domain: 'kwb', service: 'status', target: {} });
    expect(status.success).toBeTrue();
    expect((status.data as IKwbSnapshot).entities.find((entityArg) => entityArg.id === 'furnace')?.state).toEqual(250.2);
    await runtime.destroy();
  } finally {
    await new Promise<void>((resolve, reject) => server.close((errorArg) => errorArg ? reject(errorArg) : resolve()));
  }
});

const sensePacket = (temperaturesArg: number[]): Buffer => {
  const tempBytes = Buffer.concat(temperaturesArg.map((valueArg) => {
    const encoded = Math.round(valueArg * 10);
    const buffer = Buffer.alloc(2);
    buffer.writeInt16BE(encoded, 0);
    return buffer;
  }));
  const unescapedData = Buffer.concat([Buffer.alloc(4), tempBytes, Buffer.alloc(6)]);
  const packet = Buffer.concat([Buffer.from([0]), unescapedData]);
  return Buffer.concat([Buffer.from([2, 2, packet.length, 1, 1]), packet, Buffer.from([0])]);
};

const controlPacket = (): Buffer => {
  const packet = Buffer.alloc(16);
  packet[2] = 0b00000010;
  packet[3] = 0b00000010;
  return Buffer.concat([Buffer.from([2, 1, 1, 1]), packet, Buffer.from([0])]);
};

export default tap.start();
