import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';
import { expect, tap } from '@git.zone/tstest/tapbundle';
import { FroniusClient, FroniusConfigFlow, FroniusIntegration, FroniusMapper, type IFroniusCommandRequest, type IFroniusRawData } from '../../ts/integrations/fronius/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 = (dataArg: unknown): Record<string, unknown> => ({
  Head: { Status: { Code: 0, Reason: 'OK' }, Timestamp: '2026-01-01T00:00:00+00:00' },
  Body: { Data: dataArg },
});

const startFroniusServer = async (): Promise<{ url: string; close(): Promise<void> }> => {
  const server = createServer((requestArg: IncomingMessage, responseArg: ServerResponse) => {
    const url = new URL(requestArg.url || '/', 'http://127.0.0.1');

    if (url.pathname === '/solar_api/GetAPIVersion.cgi') {
      json(responseArg, { APIVersion: 1, BaseURL: '/solar_api/v1/' });
      return;
    }
    if (url.pathname === '/solar_api/v1/GetLoggerInfo.cgi') {
      json(responseArg, ok({ UniqueID: 'LOGGER123', SWVersion: '1.2.3', ProductID: 'Datalogger Web' }));
      return;
    }
    if (url.pathname === '/solar_api/v1/GetInverterInfo.cgi') {
      json(responseArg, ok({ '1': { DT: 1, PVPower: 5000, StatusCode: 7, UniqueID: 'INV123', CustomName: 'Roof Inverter' } }));
      return;
    }
    if (url.pathname === '/solar_api/v1/GetActiveDeviceInfo.cgi') {
      json(responseArg, ok({ Inverter: { '1': { DT: 1, Serial: 'INV123' } } }));
      return;
    }
    if (url.pathname === '/solar_api/v1/GetPowerFlowRealtimeData.fcgi') {
      json(responseArg, ok({ Site: { P_PV: 4200, P_Load: -3100, P_Grid: -1100, E_Day: 1234, E_Total: 555000 }, Inverters: { '1': { SOC: 68 } } }));
      return;
    }
    if (url.pathname === '/solar_api/v1/GetMeterRealtimeData.cgi' && url.searchParams.get('Scope') === 'System') {
      json(responseArg, ok({ '0': { PowerReal_P_Sum: -1100, Details: { Manufacturer: 'Fronius', Model: 'Smart Meter', Serial: 'METER123' } } }));
      return;
    }
    if (url.pathname === '/solar_api/v1/GetInverterRealtimeData.cgi' && url.searchParams.get('Scope') === 'Device') {
      expect(url.searchParams.get('DeviceId')).toEqual('1');
      json(responseArg, ok({ DAY_ENERGY: { Value: 1234, Unit: 'Wh' }, PAC: { Value: 4200, Unit: 'W' }, DeviceStatus: { StatusCode: 7, ErrorCode: 0 } }));
      return;
    }

    responseArg.statusCode = 404;
    responseArg.end('{}');
  });

  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}`,
    close: async () => new Promise<void>((resolve, reject) => server.close((errorArg) => errorArg ? reject(errorArg) : resolve())),
  };
};

tap.test('reads Fronius Solar API snapshots over local HTTP', async () => {
  const server = await startFroniusServer();
  try {
    const client = new FroniusClient({ url: server.url, timeoutMs: 1000 });
    const snapshot = await client.getSnapshot(true);
    const runtime = await new FroniusIntegration().setup({ url: server.url, timeoutMs: 1000 }, {});
    const entities = await runtime.entities();
    const refresh = await runtime.callService?.({ domain: 'fronius', service: 'refresh', target: {} });

    expect(snapshot.online).toBeTrue();
    expect(snapshot.source).toEqual('http');
    expect(snapshot.loggerInfo?.uniqueIdentifier).toEqual('LOGGER123');
    expect(snapshot.sensors.find((sensorArg) => sensorArg.id === 'power_photovoltaics')?.value).toEqual(4200);
    expect(entities.find((entityArg) => entityArg.id === 'sensor.roof_inverter_power_ac')?.state).toEqual(4200);
    expect(refresh?.success).toBeTrue();
    await runtime.destroy();
  } finally {
    await server.close();
  }
});

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

    expect(done.kind).toEqual('done');
    expect(done.config?.uniqueId).toEqual('LOGGER123');
    expect(done.config?.isLogger).toBeTrue();
  } finally {
    await server.close();
  }
});

tap.test('does not report write success for static Fronius snapshots', async () => {
  const rawData: Partial<IFroniusRawData> = {
    loggerInfo: { Body: { Data: { UniqueID: 'STATICLOGGER', SWVersion: '1.0', ProductID: 'Datalogger Web' } } },
    inverterInfo: { Body: { Data: { '1': { DT: 1, PVPower: 5000, StatusCode: 7, UniqueID: 'STATICINV' } } } },
    powerFlow: { Body: { Data: { Site: { P_PV: 1 } } } },
  };
  const snapshot = FroniusMapper.toSnapshot({ config: {}, rawData, online: true, source: 'manual' });
  const runtime = await new FroniusIntegration().setup({ snapshot }, {});
  const result = await runtime.callService?.({ domain: 'fronius', service: 'set_power_limit', target: {}, data: { watts: 1000 } });

  expect(result?.success).toBeFalse();
  expect(result?.error).toContain('read-only');
  await runtime.destroy();
});

tap.test('delegates Fronius custom commands to an injected executor', async () => {
  const commands: IFroniusCommandRequest[] = [];
  const snapshot = FroniusMapper.toSnapshot({ config: {}, rawData: { powerFlow: { Body: { Data: { Site: { P_PV: 1 } } } } }, online: true, source: 'manual' });
  const runtime = await new FroniusIntegration().setup({
    snapshot,
    commandExecutor: {
      execute: async (commandArg) => {
        commands.push(commandArg);
        return { accepted: true };
      },
    },
  }, {});
  const result = await runtime.callService?.({ domain: 'fronius', service: 'set_power_limit', target: {}, data: { watts: 1000 } });

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

export default tap.start();
