import { expect, tap } from '@git.zone/tstest/tapbundle';
import { MotionEyeClient, type IMotionEyeCamera } from '../../ts/integrations/motioneye/index.js';

const rawCamera = {
  id: 1,
  name: 'Driveway',
  host: '10.0.0.5',
  streaming_port: 8081,
  streaming_auth_mode: 'basic',
  video_streaming: true,
  motion_detection: true,
  movies: true,
  still_images: true,
  actions: ['snapshot', 'record_start', 'record_stop'],
  root_directory: '/var/lib/motioneye',
};

tap.test('fetches signed live snapshots and executes commands only after HTTP success', async () => {
  const originalFetch = globalThis.fetch;
  const requests: Array<{ url: string; method: string; body?: string }> = [];
  let updatedMotionDetection: unknown;

  globalThis.fetch = (async (inputArg: RequestInfo | URL, initArg?: RequestInit) => {
    const url = typeof inputArg === 'string' ? inputArg : inputArg instanceof URL ? inputArg.toString() : inputArg.url;
    const parsed = new URL(url);
    const method = initArg?.method || 'GET';
    const body = typeof initArg?.body === 'string' ? initArg.body : undefined;
    requests.push({ url, method, body });

    expect(parsed.searchParams.get('_signature')).toBeTruthy();
    if (parsed.pathname === '/login') {
      expect(parsed.searchParams.get('_username')).toEqual('admin');
      return new Response('{}', { headers: { 'content-type': 'application/json' } });
    }
    if (parsed.pathname === '/config/list') {
      return new Response(JSON.stringify({ cameras: [rawCamera] }), { headers: { 'content-type': 'application/json' } });
    }
    if (parsed.pathname === '/manifest.json') {
      return new Response(JSON.stringify({ version: '0.43.1' }), { headers: { 'content-type': 'application/json' } });
    }
    if (parsed.pathname === '/config/main/get') {
      return new Response(JSON.stringify({ enabled: true }), { headers: { 'content-type': 'application/json' } });
    }
    if (parsed.pathname === '/picture/1/current/') {
      expect(parsed.searchParams.get('_username')).toEqual('viewer');
      return new Response(new Uint8Array([0xff, 0xd8, 0xff]), { headers: { 'content-type': 'image/jpeg' } });
    }
    if (parsed.pathname === '/config/1/get') {
      return new Response(JSON.stringify(rawCamera), { headers: { 'content-type': 'application/json' } });
    }
    if (parsed.pathname === '/config/1/set') {
      const payload = JSON.parse(body || '{}') as Record<string, unknown>;
      updatedMotionDetection = payload.motion_detection;
      return new Response(JSON.stringify({ ok: true }), { headers: { 'content-type': 'application/json' } });
    }
    if (parsed.pathname === '/action/1/record_start') {
      expect(method).toEqual('POST');
      expect(body).toEqual('{}');
      return new Response(JSON.stringify({ ok: true }), { headers: { 'content-type': 'application/json' } });
    }
    return new Response('not found', { status: 404 });
  }) as typeof fetch;

  try {
    const client = new MotionEyeClient({ url: 'http://127.0.0.1:8765', adminPassword: 'secret', surveillanceUsername: 'viewer', surveillancePassword: 'view' });
    const snapshot = await client.getSnapshot();
    expect(snapshot.connected).toBeTrue();
    expect(snapshot.cameras[0].mjpegUrl).toEqual('http://10.0.0.5:8081/');
    expect(snapshot.cameras[0].snapshotUrl?.includes('_username=viewer')).toBeTrue();
    expect(snapshot.switches.find((switchArg) => switchArg.key === 'motion_detection')?.isOn).toBeTrue();
    expect(snapshot.sensors.find((sensorArg) => sensorArg.key === 'actions')?.value).toEqual(3);

    const image = await client.execute({ type: 'snapshot_image', service: 'snapshot', cameraId: '1' });
    expect((image as { contentType: string }).contentType).toEqual('image/jpeg');

    const switchResult = await client.execute({ type: 'set_switch', service: 'turn_off', cameraId: '1', key: 'motion_detection', enabled: false });
    expect((switchResult as { ok: boolean }).ok).toBeTrue();
    expect(updatedMotionDetection).toEqual(false);

    const actionResult = await client.execute({ type: 'action', service: 'record_start', cameraId: '1', action: 'record_start' });
    expect((actionResult as { ok: boolean }).ok).toBeTrue();
    expect(requests.some((requestArg) => requestArg.url.includes('/action/1/record_start'))).toBeTrue();
  } finally {
    globalThis.fetch = originalFetch;
  }
});

tap.test('does not fake command success without a live endpoint or successful HTTP response', async () => {
  const configuredCamera: IMotionEyeCamera = {
    id: '1',
    numericId: 1,
    name: 'Driveway',
    isStreaming: true,
    motionDetectionEnabled: true,
    actions: ['record_start'],
  };
  const clientWithoutEndpoint = new MotionEyeClient({ connected: true, cameras: [configuredCamera] });
  let missingEndpointError = '';
  try {
    await clientWithoutEndpoint.execute({ type: 'action', service: 'record_start', cameraId: '1', action: 'record_start' });
  } catch (errorArg) {
    missingEndpointError = errorArg instanceof Error ? errorArg.message : String(errorArg);
  }
  expect(missingEndpointError.includes('requires config.url or config.host')).toBeTrue();

  const originalFetch = globalThis.fetch;
  globalThis.fetch = (async (inputArg: RequestInfo | URL) => {
    const url = typeof inputArg === 'string' ? inputArg : inputArg instanceof URL ? inputArg.toString() : inputArg.url;
    if (new URL(url).pathname === '/action/1/record_start') {
      return new Response('failed', { status: 500 });
    }
    return new Response(JSON.stringify({ cameras: [rawCamera] }), { headers: { 'content-type': 'application/json' } });
  }) as typeof fetch;

  try {
    const client = new MotionEyeClient({ url: 'http://127.0.0.1:8765', snapshot: { deviceInfo: { id: 'motioneye', online: true }, cameras: [configuredCamera], sensors: [], switches: [], rawCameras: [rawCamera], connected: true } });
    let httpError = '';
    try {
      await client.execute({ type: 'action', service: 'record_start', cameraId: '1', action: 'record_start' });
    } catch (errorArg) {
      httpError = errorArg instanceof Error ? errorArg.message : String(errorArg);
    }
    expect(httpError.includes('failed with HTTP 500')).toBeTrue();
  } finally {
    globalThis.fetch = originalFetch;
  }
});

export default tap.start();
