import { createHmac } from 'node:crypto';
import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';
import { expect, tap } from '@git.zone/tstest/tapbundle';
import { FreeboxClient, FreeboxConfigFlow, FreeboxIntegration, FreeboxMapper, type IFreeboxCommandRequest, type IFreeboxRawData } from '../../ts/integrations/freebox/index.js';

const json = (responseArg: ServerResponse, valueArg: unknown, statusArg = 200): void => {
  responseArg.statusCode = statusArg;
  responseArg.setHeader('content-type', 'application/json');
  responseArg.end(JSON.stringify(valueArg));
};

const ok = (resultArg?: unknown): Record<string, unknown> => ({
  success: true,
  ...(resultArg === undefined ? {} : { result: resultArg }),
});

const startFreeboxServer = async (): Promise<{ url: string; calls: string[]; close(): Promise<void> }> => {
  const calls: string[] = [];
  const server = createServer((requestArg: IncomingMessage, responseArg: ServerResponse) => {
    const url = new URL(requestArg.url || '/', 'http://127.0.0.1');
    const chunks: Buffer[] = [];
    requestArg.on('data', (chunkArg: Buffer | string) => chunks.push(Buffer.isBuffer(chunkArg) ? chunkArg : Buffer.from(String(chunkArg))));
    requestArg.on('end', () => {
      const body = Buffer.concat(chunks).toString('utf8');
      calls.push(`${requestArg.method} ${url.pathname}`);

      if (url.pathname === '/api_version') {
        json(responseArg, { uid: 'freebox-uid', device_name: 'Freebox Server', api_version: '6.0', api_base_url: '/api/', device_type: 'FreeboxServer1,2' });
        return;
      }
      if (url.pathname === '/api/v6/login/' && requestArg.method === 'GET') {
        json(responseArg, ok({ logged_in: false, challenge: 'challenge' }));
        return;
      }
      if (url.pathname === '/api/v6/login/session/' && requestArg.method === 'POST') {
        const parsed = JSON.parse(body) as Record<string, unknown>;
        expect(parsed.app_id).toEqual('hass');
        expect(parsed.password).toEqual(createHmac('sha1', 'app-token').update('challenge').digest('hex'));
        json(responseArg, ok({ session_token: 'session-token', permissions: { settings: true, calls: true, home: true } }));
        return;
      }

      if (!['/api/v6/login/logout/'].includes(url.pathname)) {
        expect(requestArg.headers['x-fbx-app-auth']).toEqual('session-token');
      }

      if (url.pathname === '/api/v6/system/') {
        json(responseArg, ok({ mac: 'F4:CA:E5:5C:EA:14', serial: '805400T144100853', firmware_version: '4.8.0', board_name: 'fbxgw1r', uptime_val: 3600, model_info: { pretty_name: 'Freebox Server', name: 'fbxgw1r' }, sensors: [{ name: 'CPU', value: 55 }] }));
        return;
      }
      if (url.pathname === '/api/v6/connection/') {
        json(responseArg, ok({ rate_down: 1234, rate_up: 321, media: 'ftth', state: 'up', ipv4: '203.0.113.10' }));
        return;
      }
      if (url.pathname === '/api/v6/call/log/') {
        json(responseArg, ok([{ id: 1, type: 'missed', datetime: 1700000000, name: 'Alice', number: '0102030405', new: true }]));
        return;
      }
      if (url.pathname === '/api/v6/storage/disk/') {
        json(responseArg, ok([{ id: 1, model: 'Hitachi', serial: 'DISK123', state: 'enabled', temp: 42, partitions: [{ id: 3, disk_id: 1, label: 'Disque dur', total_bytes: 1000, free_bytes: 250 }] }]));
        return;
      }
      if (url.pathname === '/api/v6/storage/raid/') {
        json(responseArg, ok([{ id: 7, name: 'main', degraded: false }]));
        return;
      }
      if (url.pathname === '/api/v6/wifi/config/' && requestArg.method === 'GET') {
        json(responseArg, ok({ enabled: true, mac_filter_state: 'disabled' }));
        return;
      }
      if (url.pathname === '/api/v6/wifi/config/' && requestArg.method === 'PUT') {
        const parsed = JSON.parse(body) as Record<string, unknown>;
        expect(parsed.enabled).toEqual(false);
        json(responseArg, ok({ enabled: false, mac_filter_state: 'disabled' }));
        return;
      }
      if (url.pathname === '/api/v6/lan/browser/interfaces/') {
        json(responseArg, ok([{ name: 'pub', host_count: 1 }]));
        return;
      }
      if (url.pathname === '/api/v6/lan/browser/pub/') {
        json(responseArg, ok([{ id: 'ether-aa:bb:cc:dd:ee:ff', primary_name: 'Phone', vendor_name: 'Example', host_type: 'smartphone', active: true, reachable: true, l2ident: { id: 'AA:BB:CC:DD:EE:FF' }, l3connectivities: [{ addr: '192.168.1.20', af: 'ipv4', active: true }] }]));
        return;
      }
      if (url.pathname === '/api/v6/home/nodes') {
        json(responseArg, ok([{ id: 10, label: 'Hall Motion', category: 'pir', status: 'active', props: {}, type: { endpoints: [{ id: 100, ep_type: 'signal', name: 'trigger' }] }, show_endpoints: [{ ep_type: 'signal', name: 'trigger', value: false }] }]));
        return;
      }
      if (url.pathname === '/api/v6/system/reboot/' && requestArg.method === 'POST') {
        json(responseArg, ok());
        return;
      }
      if (url.pathname === '/api/v6/call/log/mark_all_as_read/' && requestArg.method === 'POST') {
        json(responseArg, ok());
        return;
      }
      if (url.pathname === '/api/v6/login/logout/' && requestArg.method === 'POST') {
        json(responseArg, ok());
        return;
      }

      json(responseArg, { success: false, error_code: 'not_found', msg: 'not found' }, 404);
    });
  });

  await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', resolve));
  const address = server.address();
  const port = typeof address === 'object' && address ? address.port : 0;
  return {
    url: `http://127.0.0.1:${port}`,
    calls,
    close: async () => new Promise<void>((resolve, reject) => server.close((errorArg) => errorArg ? reject(errorArg) : resolve())),
  };
};

tap.test('reads authenticated Freebox local API snapshots and executes controls', async () => {
  const server = await startFreeboxServer();
  try {
    const client = new FreeboxClient({ url: server.url, protocol: 'http', appToken: 'app-token', timeoutMs: 1000 });
    const snapshot = await client.getSnapshot(true);
    const runtime = await new FreeboxIntegration().setup({ url: server.url, protocol: 'http', appToken: 'app-token', timeoutMs: 1000 }, {});
    const entities = await runtime.entities();
    const wifiEntity = entities.find((entityArg) => entityArg.id === 'switch.freebox_server_wifi');
    const wifiResult = await runtime.callService?.({ domain: 'switch', service: 'turn_off', target: { entityId: wifiEntity?.id }, data: {} });
    const refresh = await runtime.callService?.({ domain: 'freebox', service: 'refresh', target: {} });

    expect(snapshot.online).toBeTrue();
    expect(snapshot.source).toEqual('http');
    expect(snapshot.router.macAddress).toEqual('f4:ca:e5:5c:ea:14');
    expect(snapshot.sensors.find((sensorArg) => sensorArg.id === 'rate_down')?.value).toEqual(1234);
    expect(entities.find((entityArg) => entityArg.id === 'sensor.freebox_server_missed_calls')?.state).toEqual(1);
    expect(wifiResult?.success).toBeTrue();
    expect(refresh?.success).toBeTrue();
    expect(server.calls.includes('PUT /api/v6/wifi/config/')).toBeTrue();
    await runtime.destroy();
  } finally {
    await server.close();
  }
});

tap.test('validates a live Freebox host during config flow', async () => {
  const server = await startFreeboxServer();
  try {
    const address = new URL(server.url);
    const step = await new FreeboxConfigFlow().start({ source: 'manual', host: address.hostname, port: Number(address.port), metadata: { protocol: 'http', appToken: 'app-token' } }, {});
    const done = await step.submit!({});

    expect(done.kind).toEqual('done');
    expect(done.config?.uniqueId).toEqual('f4:ca:e5:5c:ea:14');
    expect(done.config?.modelId).toEqual('fbxgw1r');
  } finally {
    await server.close();
  }
});

tap.test('does not report write success for static Freebox snapshots', async () => {
  const rawData: Partial<IFreeboxRawData> = {
    systemConfig: { mac: 'F4:CA:E5:5C:EA:14', model_info: { pretty_name: 'Static Freebox' }, sensors: [] },
    wifiConfig: { enabled: true },
  };
  const snapshot = FreeboxMapper.toSnapshot({ config: {}, rawData, online: true, source: 'manual' });
  const runtime = await new FreeboxIntegration().setup({ snapshot }, {});
  const result = await runtime.callService?.({ domain: 'freebox', service: 'set_wifi_enabled', target: {}, data: { enabled: false } });

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

tap.test('delegates Freebox custom commands to an injected executor', async () => {
  const commands: IFreeboxCommandRequest[] = [];
  const snapshot = FreeboxMapper.toSnapshot({ config: {}, rawData: { systemConfig: { mac: 'F4:CA:E5:5C:EA:14', model_info: { pretty_name: 'Delegated Freebox' } }, wifiConfig: { enabled: true } }, online: true, source: 'manual' });
  const runtime = await new FreeboxIntegration().setup({
    snapshot,
    commandExecutor: {
      execute: async (commandArg) => {
        commands.push(commandArg);
        return { accepted: true };
      },
    },
  }, {});
  const result = await runtime.callService?.({ domain: 'freebox', service: 'set_wifi_enabled', target: {}, data: { enabled: false } });

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

export default tap.start();
