import { createServer } from 'node:http';
import { expect, tap } from '@git.zone/tstest/tapbundle';
import { HomeAssistantP1MonitorIntegration, P1MonitorClient, P1MonitorConfigFlow, P1MonitorIntegration, P1MonitorMapper, createP1MonitorDiscoveryDescriptor, p1MonitorProfile, type IP1MonitorSnapshot, type TP1MonitorRawData } from '../../ts/integrations/p1_monitor/index.js';

const rawData: TP1MonitorRawData = {
  device: {
    id: 'p1_monitor-device-1',
    name: "P1 Monitor Device",
    manufacturer: "P1 Monitor",
    model: "P1 Monitor local integration",
    serialNumber: 'p1_monitor-serial-1',
  },
  entities: [
    { id: 'status', name: 'Status', platform: "sensor", state: true, attributes: { domain: "p1_monitor" } },
  ],
  online: true,
  updatedAt: '2026-01-01T00:00:00.000Z',
  source: 'manual',
};

tap.test('matches manual P1 Monitor candidates and creates config flow output', async () => {
  const descriptor = createP1MonitorDiscoveryDescriptor();
  const matcher = descriptor.getMatchers().find((matcherArg) => matcherArg.id === 'p1_monitor-manual-match');
  const result = await matcher!.matches({ source: 'manual', id: 'p1_monitor-device-1', name: "P1 Monitor Device", metadata: { rawData } }, {});

  expect(result.matched).toBeTrue();
  expect(result.candidate?.integrationDomain).toEqual("p1_monitor");

  const validation = await descriptor.getValidators()[0].validate(result.candidate!, {});
  expect(validation.matched).toBeTrue();

  const done = await (await new P1MonitorConfigFlow().start(result.candidate!, {})).submit!({});
  expect(done.kind).toEqual('done');
  expect(done.config?.uniqueId).toEqual('p1_monitor-device-1');
  expect(done.config?.rawData).toEqual(rawData);
});

tap.test('maps P1 Monitor raw snapshots to runtime devices and entities', async () => {
  const client = new P1MonitorClient({ name: "P1 Monitor Runtime", rawData });
  const snapshot = await client.getSnapshot();
  const mappedSnapshot = P1MonitorMapper.toSnapshotFromRaw({ name: "P1 Monitor Runtime" }, rawData);
  const devices = P1MonitorMapper.toDevices(mappedSnapshot);
  const entities = P1MonitorMapper.toEntities(mappedSnapshot);

  expect(snapshot.online).toBeTrue();
  expect(mappedSnapshot.source).toEqual('manual');
  expect(devices[0].integrationDomain).toEqual("p1_monitor");
  expect(devices[0].manufacturer).toEqual("P1 Monitor");
  expect(entities.some((entityArg) => entityArg.integrationDomain === "p1_monitor" && entityArg.platform === "sensor")).toBeTrue();
});

tap.test('polls P1 Monitor over the documented local HTTP API', async () => {
  const seenPaths: string[] = [];
  const server = createServer((requestArg, responseArg) => {
    const url = new URL(requestArg.url || '/', 'http://127.0.0.1');
    seenPaths.push(`${url.pathname}?${url.searchParams.toString()}`);
    responseArg.setHeader('content-type', 'application/json');
    if (url.pathname === '/api/v1/smartmeter') {
      responseArg.end(JSON.stringify([{ CONSUMPTION_GAS_M3: 12.5, CONSUMPTION_W: 345, CONSUMPTION_KWH_HIGH: 1000.1, CONSUMPTION_KWH_LOW: 900.2, PRODUCTION_W: 12, PRODUCTION_KWH_HIGH: 20.5, PRODUCTION_KWH_LOW: 10.25, TARIFCODE: 'P' }]));
      return;
    }
    if (url.pathname === '/api/v1/status') {
      responseArg.end(JSON.stringify([
        { STATUS_ID: 103, STATUS: '231.1' },
        { STATUS_ID: 104, STATUS: '232.2' },
        { STATUS_ID: 105, STATUS: '233.3' },
        { STATUS_ID: 100, STATUS: '1.1' },
        { STATUS_ID: 101, STATUS: '1.2' },
        { STATUS_ID: 102, STATUS: '1.3' },
        { STATUS_ID: 74, STATUS: '0.345' },
        { STATUS_ID: 75, STATUS: '0.456' },
        { STATUS_ID: 76, STATUS: '0.567' },
        { STATUS_ID: 77, STATUS: '0.012' },
        { STATUS_ID: 78, STATUS: '0.013' },
        { STATUS_ID: 79, STATUS: '0.014' },
      ]));
      return;
    }
    if (url.pathname === '/api/v1/configuration') {
      responseArg.end(JSON.stringify([
        { CONFIGURATION_ID: 15, PARAMETER: '1.25' },
        { CONFIGURATION_ID: 1, PARAMETER: '0.20' },
        { CONFIGURATION_ID: 2, PARAMETER: '0.30' },
        { CONFIGURATION_ID: 3, PARAMETER: '0.05' },
        { CONFIGURATION_ID: 4, PARAMETER: '0.06' },
      ]));
      return;
    }
    if (url.pathname === '/api/v2/watermeter/day') {
      responseArg.end(JSON.stringify([{ WATERMETER_CONSUMPTION_LITER: 34, WATERMETER_CONSUMPTION_TOTAL_M3: 56.7, WATERMETER_PULS_COUNT: 89 }]));
      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 P1MonitorClient({ host: '127.0.0.1', port, timeoutMs: 1000 });
    const snapshot = await client.getSnapshot();

    expect(snapshot.online).toBeTrue();
    expect(snapshot.source).toEqual('http');
    expect(snapshot.device.port).toEqual(port);
    expect(snapshot.entities.find((entityArg) => entityArg.id === 'energy_tariff_period')?.state).toEqual('high');
    expect(snapshot.entities.find((entityArg) => entityArg.id === 'power_consumed_phase_l1')?.state).toEqual(345);
    expect(snapshot.entities.find((entityArg) => entityArg.id === 'consumption_day')?.state).toEqual(34);
    expect(seenPaths.some((pathArg) => pathArg.startsWith('/api/v1/smartmeter?'))).toBeTrue();
  } finally {
    await new Promise<void>((resolve, reject) => server.close((errorArg) => errorArg ? reject(errorArg) : resolve()));
  }
});

tap.test('exposes P1 Monitor runtime, HA alias, and unsupported control without executor', async () => {
  const integration = new P1MonitorIntegration();
  const alias = new HomeAssistantP1MonitorIntegration();
  expect(alias instanceof P1MonitorIntegration).toBeTrue();
  expect(alias.domain).toEqual("p1_monitor");
  expect(integration.status).toEqual("read-only-runtime");
  expect(p1MonitorProfile.metadata.configFlow).toEqual(true);
  expect(p1MonitorProfile.metadata.requirements).toEqual([
    "p1monitor==3.2.0",
  ]);

  const runtime = await integration.setup({ name: "P1 Monitor Runtime", rawData }, {});
  const statusResult = await runtime.callService!({ domain: "p1_monitor", service: 'status', target: {} });
  const refresh = await runtime.callService!({ domain: "p1_monitor", service: 'refresh', target: {} });
  const snapshot = statusResult.data as IP1MonitorSnapshot;

  expect(statusResult.success).toBeTrue();
  expect(refresh.success).toBeTrue();
  expect(snapshot.online).toBeTrue();
  expect((await runtime.devices())[0].name).toEqual("P1 Monitor Device");

  const command = await runtime.callService!({ domain: "p1_monitor", service: p1MonitorProfile.controlServices?.[0] || 'turn_on', target: {} });
  expect(command.success).toBeFalse();
  expect(command.error!).toContain('requires an injected client.execute() or commandExecutor');
  await runtime.destroy();
});

export default tap.start();
