import * as http from 'node:http';
import { expect, tap } from '@git.zone/tstest/tapbundle';
import { LoqedClient, LoqedIntegration } from '../../ts/integrations/loqed/index.js';

interface ILoqedTestServer {
  url: string;
  commands: string[];
  close(): Promise<void>;
}

const statusPayload = {
  battery_percentage: 87,
  battery_type: 'AA',
  battery_voltage: 5.8,
  bolt_state: 'night_lock',
  bridge_mac_wifi: 'AA:BB:CC:DD:EE:FF',
  bridge_mac_ble: '11:22:33:44:55:66',
  lock_online: 1,
  webhooks_number: 1,
  ip_address: '192.168.1.40',
  up_timestamp: 12345,
  wifi_strength: -55,
  ble_strength: -60,
};

const startLoqedServer = async (): Promise<ILoqedTestServer> => {
  const commands: string[] = [];
  const server = http.createServer((requestArg, responseArg) => {
    const url = new URL(requestArg.url || '/', 'http://127.0.0.1');
    if (requestArg.method === 'GET' && url.pathname === '/status') {
      responseArg.setHeader('content-type', 'application/json');
      responseArg.end(JSON.stringify(statusPayload));
      return;
    }
    if (requestArg.method === 'GET' && url.pathname === '/to_lock') {
      const command = url.searchParams.get('command_signed_base64') || '';
      commands.push(command);
      responseArg.end('OK');
      return;
    }
    responseArg.statusCode = 404;
    responseArg.end('not found');
  });
  await listen(server);
  const address = server.address();
  const port = typeof address === 'object' && address ? address.port : 0;
  return {
    url: `http://127.0.0.1:${port}`,
    commands,
    close: async () => close(server),
  };
};

tap.test('reads LOQED bridge status and sends signed local commands', async () => {
  const server = await startLoqedServer();
  const secret = 'U2VjcmV0U2VjcmV0U2VjcmV0U2VjcmV0U2VjcmV0U2VjcmU=';
  try {
    const client = new LoqedClient({ url: server.url, timeoutMs: 1000, name: 'Front Door', lockKeySecret: secret, lockKeyLocalId: 1 });
    const snapshot = await client.getSnapshot(true);
    const runtime = await new LoqedIntegration().setup({ url: server.url, timeoutMs: 1000, name: 'Front Door', lockKeySecret: secret, lockKeyLocalId: 1 }, {});
    const unlock = await runtime.callService?.({ domain: 'lock', service: 'unlock', target: { entityId: 'switch.front_door_lock' } });

    expect(snapshot.online).toBeTrue();
    expect(snapshot.source).toEqual('http');
    expect(snapshot.entities.find((entityArg) => entityArg.id === 'lock')?.state).toBeTrue();
    expect(snapshot.entities.find((entityArg) => entityArg.id === 'battery')?.state).toEqual(87);
    expect(unlock?.success).toBeTrue();
    expect(server.commands.length).toEqual(1);
    expect(Buffer.from(server.commands[0], 'base64').length).toEqual(53);
    await runtime.destroy();
  } finally {
    await server.close();
  }
});

tap.test('does not fake LOQED control without local key material', async () => {
  const server = await startLoqedServer();
  try {
    const runtime = await new LoqedIntegration().setup({ url: server.url, timeoutMs: 1000, name: 'Front Door' }, {});
    const result = await runtime.callService?.({ domain: 'lock', service: 'lock', target: {} });

    expect(result?.success).toBeFalse();
    expect(result?.error || '').toContain('lockKeySecret');
    expect(server.commands.length).toEqual(0);
    await runtime.destroy();
  } finally {
    await server.close();
  }
});

const listen = async (serverArg: http.Server): Promise<void> => {
  await new Promise<void>((resolve, reject) => {
    serverArg.once('error', reject);
    serverArg.listen(0, '127.0.0.1', () => {
      serverArg.off('error', reject);
      resolve();
    });
  });
};

const close = async (serverArg: http.Server): Promise<void> => {
  await new Promise<void>((resolve, reject) => {
    serverArg.close((errorArg) => errorArg ? reject(errorArg) : resolve());
  });
};

export default tap.start();
