import { expect, tap } from '@git.zone/tstest/tapbundle';
import { HomeAssistantLookinIntegration, LookinClient, LookinConfigFlow, LookinIntegration, LookinMapper, createLookinDiscoveryDescriptor, lookinProfile, type ILookinSnapshot, type TLookinRawData } from '../../ts/integrations/lookin/index.js';

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

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

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

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

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

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

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

tap.test('polls LOOKin local HTTP endpoints and sends native IR commands', async () => {
  const originalFetch = globalThis.fetch;
  const requests: string[] = [];
  globalThis.fetch = (async (inputArg: RequestInfo | URL) => {
    const url = typeof inputArg === 'string' ? inputArg : inputArg instanceof URL ? inputArg.toString() : inputArg.url;
    requests.push(url);
    const parsed = new URL(url);

    if (parsed.pathname === '/device') {
      return jsonResponse({ Type: 'Remote', MRDC: '02FF', Status: 'ok', ID: '98f33093', Name: 'Living LOOKin', Firmware: '1.2.3' });
    }
    if (parsed.pathname === '/data') {
      return jsonResponse([
        { Type: '01', UUID: 'TV01' },
        { Type: '03', UUID: 'LT01' },
        { Type: 'EF', UUID: 'AC01' },
      ]);
    }
    if (parsed.pathname === '/data/TV01') {
      return jsonResponse({ Type: '01', UUID: 'TV01', Name: 'TV', Updated: '1', Status: '10F0', Functions: [{ Name: 'poweron', Type: 'button' }, { Name: 'poweroff', Type: 'button' }, { Name: 'volup', Type: 'button' }] });
    }
    if (parsed.pathname === '/data/LT01') {
      return jsonResponse({ Type: '03', UUID: 'LT01', Name: 'Lamp', Updated: '1', Status: '1000', Functions: [{ Name: 'power', Type: 'button' }] });
    }
    if (parsed.pathname === '/data/AC01') {
      return jsonResponse({ Type: 'EF', UUID: 'AC01', Name: 'AC', Updated: '1', Status: '1821', Extra: '', Functions: [{ Name: 'power', Type: 'button' }] });
    }
    if (parsed.pathname === '/sensors/meteo') {
      return jsonResponse({ Humidity: '41.5', Pressure: '1000', Temperature: '22.3', Updated: '1' });
    }
    if (parsed.pathname === '/commands/ir/localremote/TV0106FF') {
      return new Response(null, { status: 204 });
    }
    return new Response('not found', { status: 404 });
  }) as typeof fetch;

  try {
    const client = new LookinClient({ host: 'lookin.local', timeoutMs: 1000 });
    const snapshot = await client.getSnapshot(true);
    expect(snapshot.online).toBeTrue();
    expect(snapshot.device.id).toEqual('98F33093');
    expect(snapshot.entities.some((entityArg) => entityArg.platform === 'media_player' && entityArg.id === 'TV01')).toBeTrue();
    expect(snapshot.entities.some((entityArg) => entityArg.platform === 'light' && entityArg.id === 'LT01')).toBeTrue();
    expect(snapshot.entities.some((entityArg) => entityArg.platform === 'climate' && entityArg.id === 'AC01')).toBeTrue();
    expect(snapshot.entities.find((entityArg) => entityArg.id === 'temperature')?.state).toEqual(22.3);

    const command = await client.execute({ domain: 'media_player', service: 'volume_up', target: {}, data: { uuid: 'TV01' } });
    expect(command.success).toBeTrue();
    expect(requests).toContain('http://lookin.local/device');
    expect(requests).toContain('http://lookin.local/data/TV01');
    expect(requests).toContain('http://lookin.local/commands/ir/localremote/TV0106FF');
    expect(JSON.stringify(lookinProfile.metadata.localApi)).toContain('/device');
  } finally {
    globalThis.fetch = originalFetch;
  }
});

tap.test('exposes LOOKin runtime, HA alias, and unsupported control without executor', async () => {
  const integration = new LookinIntegration();
  const alias = new HomeAssistantLookinIntegration();
  expect(alias instanceof LookinIntegration).toBeTrue();
  expect(alias.domain).toEqual("lookin");
  expect(integration.status).toEqual("control-runtime");
  expect(lookinProfile.metadata.configFlow).toEqual(true);
  expect(lookinProfile.metadata.requirements).toEqual([
    "aiolookin==1.0.0",
  ]);

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

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

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

const jsonResponse = (valueArg: unknown): Response => {
  return new Response(JSON.stringify(valueArg), { headers: { 'content-type': 'application/json' } });
};

export default tap.start();
