import * as http from 'node:http';
import { expect, tap } from '@git.zone/tstest/tapbundle';
import { DdwrtClient, DdwrtIntegration, DdwrtMapper, type IDdwrtCommand } from '../../ts/integrations/ddwrt/index.js';

const wirelessResponse = "{active_wireless::'AA:BB:CC:DD:EE:FF','ath0'}";
const lanResponse = "{arp_table::'192.168.1.10','AA:BB:CC:DD:EE:FF','br0'}{dhcp_leases::'phone','192.168.1.10','AA:BB:CC:DD:EE:FF','hostid','3600'}";
const rawStatus = {
  wireless: DdwrtMapper.parseDdwrtResponse(wirelessResponse),
  lan: DdwrtMapper.parseDdwrtResponse(lanResponse),
};

tap.test('does not fake DD-WRT command success without injected executor', async () => {
  const runtime = await new DdwrtIntegration().setup({ name: 'Snapshot Router', rawStatus, nativeHttpEnabled: false }, {});
  const result = await runtime.callService?.({ domain: 'ddwrt', service: 'reboot', target: {}, data: {} });

  expect(result?.success).toBeFalse();
  expect(result?.error || '').toContain('require an injected');
  await runtime.destroy();
});

tap.test('delegates DD-WRT commands to an injected executor', async () => {
  let command: IDdwrtCommand | undefined;
  const runtime = await new DdwrtIntegration().setup({
    name: 'Executor Router',
    rawStatus,
    nativeHttpEnabled: false,
    commandExecutor: async (commandArg) => {
      command = commandArg;
      return { success: true, transmitted: true, data: { accepted: true } };
    },
  }, {});

  const result = await runtime.callService?.({ domain: 'ddwrt', service: 'reboot', target: {}, data: {} });
  expect(result?.success).toBeTrue();
  expect(command?.action).toEqual('reboot');

  const snapshotResult = await runtime.callService?.({ domain: 'ddwrt', service: 'snapshot', target: {}, data: {} });
  const snapshot = snapshotResult?.data as { metadata?: { lastCommand?: string } } | undefined;
  expect(snapshot?.metadata?.lastCommand).toEqual('reboot');
  await runtime.destroy();
});

tap.test('reads local DD-WRT live status pages over native HTTP', async () => {
  const requests: Array<{ url?: string; auth?: string }> = [];
  const expectedAuth = `Basic ${Buffer.from('admin:secret').toString('base64')}`;
  const server = http.createServer((request, response) => {
    requests.push({ url: request.url, auth: request.headers.authorization });
    if (request.headers.authorization !== expectedAuth) {
      response.statusCode = 401;
      response.end('unauthorized');
      return;
    }
    if (request.url === '/Status_Wireless.live.asp') {
      response.end(wirelessResponse);
      return;
    }
    if (request.url === '/Status_Lan.live.asp') {
      response.end(lanResponse);
      return;
    }
    response.statusCode = 404;
    response.end('not found');
  });

  await listen(server);
  const address = server.address();
  const port = typeof address === 'object' && address ? address.port : 0;
  try {
    const client = new DdwrtClient({ host: `127.0.0.1:${port}`, username: 'admin', password: 'secret', ssl: false, timeoutMs: 1000 });
    const snapshot = await client.getSnapshot(true);
    const runtime = await new DdwrtIntegration().setup({ host: `127.0.0.1:${port}`, username: 'admin', password: 'secret', ssl: false, timeoutMs: 1000 }, {});
    const entities = await runtime.entities();
    const reboot = await runtime.callService?.({ domain: 'ddwrt', service: 'reboot', target: {}, data: {} });

    expect(snapshot.connected).toBeTrue();
    expect(snapshot.source).toEqual('http');
    expect(snapshot.clients[0].hostname).toEqual('phone');
    expect(entities.find((entityArg) => entityArg.id === 'binary_sensor.phone_connected')?.available).toBeTrue();
    expect(reboot?.success).toBeFalse();
    expect(requests.some((requestArg) => requestArg.url === '/Status_Wireless.live.asp')).toBeTrue();
    expect(requests.some((requestArg) => requestArg.url === '/Status_Lan.live.asp')).toBeTrue();
    await runtime.destroy();
  } finally {
    await close(server);
  }
});

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();
