import { expect, tap } from '@git.zone/tstest/tapbundle';
import { SoundtouchClient, SoundtouchIntegration, type ISoundtouchRawCommandRequest, type ISoundtouchSnapshot } from '../../ts/integrations/soundtouch/index.js';

const snapshot: ISoundtouchSnapshot = {
  config: { deviceId: 'DEVICE-123', name: 'Living Room', type: 'SoundTouch 20', host: '192.168.1.50', port: 8090 },
  status: { source: 'INTERNET_RADIO', playStatus: 'PAUSE_STATE' },
  volume: { actual: 20, target: 20, muted: false },
  presets: [{ presetId: '1', name: 'Jazz Radio', source: 'INTERNET_RADIO', type: 'uri', location: 'station-1', sourceXml: '<ContentItem source="INTERNET_RADIO" type="uri" location="station-1"><itemName>Jazz Radio</itemName></ContentItem>' }],
  sourceList: ['AUX', 'BLUETOOTH'],
  available: true,
};

tap.test('models SoundTouch playback volume source and preset commands through an executor', async () => {
  const commands: ISoundtouchRawCommandRequest[] = [];
  const runtime = await new SoundtouchIntegration().setup({
    snapshot,
    commandExecutor: {
      execute: async (requestArg) => {
        commands.push(requestArg);
        return { body: '<ok />' };
      },
    },
  }, {});

  const play = await runtime.callService!({ domain: 'media_player', service: 'media_play', target: { entityId: 'media_player.living_room' } });
  const volume = await runtime.callService!({ domain: 'media_player', service: 'volume_set', target: { entityId: 'media_player.living_room' }, data: { volume_level: 0.35 } });
  const source = await runtime.callService!({ domain: 'media_player', service: 'select_source', target: { entityId: 'media_player.living_room' }, data: { source: 'AUX' } });
  const preset = await runtime.callService!({ domain: 'media_player', service: 'play_media', target: { entityId: 'media_player.living_room' }, data: { media_content_id: '1' } });

  expect(play.success).toBeTrue();
  expect(volume.success).toBeTrue();
  expect(source.success).toBeTrue();
  expect(preset.success).toBeTrue();
  expect(commands.map((commandArg) => `${commandArg.method} ${commandArg.path}`)).toEqual([
    'POST /key',
    'POST /key',
    'POST /volume',
    'POST /select',
    'POST /select',
  ]);
  expect(commands[0].body).toEqual('<key state="press" sender="Gabbo">PLAY</key>');
  expect(commands[1].body).toEqual('<key state="release" sender="Gabbo">PLAY</key>');
  expect(commands[2].body).toEqual('<volume>35</volume>');
  expect(commands[3].body?.includes('source="AUX"')).toBeTrue();
  expect(commands[4].body?.includes('location="station-1"')).toBeTrue();
});

tap.test('does not report live command success for static snapshots without transport', async () => {
  const runtime = await new SoundtouchIntegration().setup({ snapshot }, {});
  const result = await runtime.callService!({ domain: 'media_player', service: 'media_play', target: { entityId: 'media_player.living_room' } });
  expect(result.success).toBeFalse();
  expect(result.error?.includes('config.host or commandExecutor')).toBeTrue();
});

tap.test('parses native SoundTouch HTTP XML snapshots', async () => {
  const originalFetch = globalThis.fetch;
  globalThis.fetch = (async (urlArg: URL | RequestInfo) => {
    const path = new URL(String(urlArg)).pathname;
    const bodies: Record<string, string> = {
      '/info': '<info deviceID="DEVICE-123"><name>Living Room</name><type>SoundTouch 20</type><networkInfo type="SMSC"><macAddress>00:11:22:33:44:55</macAddress><ipAddress>192.168.1.50</ipAddress></networkInfo><components><component><componentCategory>PRODUCT</componentCategory><softwareVersion>27.0.1</softwareVersion><serialNumber>B123</serialNumber></component></components></info>',
      '/now_playing': '<nowPlaying source="INTERNET_RADIO"><ContentItem source="INTERNET_RADIO" type="uri" location="station-1" isPresetable="true"><itemName>Jazz Radio</itemName></ContentItem><stationName>Jazz Radio</stationName><artist>Example Artist</artist><track>Track</track><playStatus>PLAY_STATE</playStatus><time total="180">30</time></nowPlaying>',
      '/volume': '<volume><actualvolume>42</actualvolume><targetvolume>42</targetvolume><muteenabled>false</muteenabled></volume>',
      '/presets': '<presets><preset id="1"><ContentItem source="INTERNET_RADIO" type="uri" location="station-1" isPresetable="true"><itemName>Jazz Radio</itemName></ContentItem></preset></presets>',
      '/getZone': '<zone master="DEVICE-123"><member ipaddress="192.168.1.51" role="SLAVE">DEVICE-456</member></zone>',
    };
    return new Response(bodies[path] || '', { status: bodies[path] ? 200 : 404, headers: { 'content-type': 'text/xml' } });
  }) as typeof globalThis.fetch;

  try {
    const parsed = await new SoundtouchClient({ host: 'speaker.local' }).getSnapshot();
    expect(parsed.config.deviceId).toEqual('DEVICE-123');
    expect(parsed.config.name).toEqual('Living Room');
    expect(parsed.status?.stationName).toEqual('Jazz Radio');
    expect(parsed.volume?.actual).toEqual(42);
    expect(parsed.presets?.[0].presetId).toEqual('1');
    expect(parsed.zone?.isMaster).toBeTrue();
    expect(parsed.zone?.slaves[0].deviceId).toEqual('DEVICE-456');
  } finally {
    globalThis.fetch = originalFetch;
  }
});

tap.test('uses native SoundTouch HTTP endpoints for live commands', async () => {
  const originalFetch = globalThis.fetch;
  const calls: Array<{ url: string; method?: string; body?: string }> = [];
  globalThis.fetch = (async (urlArg: URL | RequestInfo, initArg?: RequestInit) => {
    calls.push({ url: String(urlArg), method: initArg?.method, body: String(initArg?.body || '') });
    return new Response('<ok />', { status: 200, headers: { 'content-type': 'text/xml' } });
  }) as typeof globalThis.fetch;

  try {
    await new SoundtouchClient({ host: 'speaker.local' }).execute({ command: 'set_volume', volumeLevel: 0.5 });
    expect(calls[0].url).toEqual('http://speaker.local:8090/volume');
    expect(calls[0].method).toEqual('POST');
    expect(calls[0].body).toEqual('<volume>50</volume>');
  } finally {
    globalThis.fetch = originalFetch;
  }
});

export default tap.start();
