import { createServer } from 'node:http';
import { expect, tap } from '@git.zone/tstest/tapbundle';
import { PrusaLinkClient, PrusaLinkIntegration, PrusaLinkMapper, type IPrusaLinkCommandRequest } from '../../ts/integrations/prusalink/index.js';

tap.test('reads PrusaLink data over native local HTTP with Digest authentication', async () => {
  const requests: Array<{ url: string; method?: string; authorization?: string }> = [];
  const server = createServer((requestArg, responseArg) => {
    requests.push({ url: requestArg.url || '', method: requestArg.method, authorization: requestArg.headers.authorization });
    if (!requestArg.headers.authorization) {
      responseArg.statusCode = 401;
      responseArg.setHeader('www-authenticate', 'Digest realm="PrusaLink", nonce="abc123", qop="auth"');
      responseArg.end('auth required');
      return;
    }
    responseArg.setHeader('content-type', 'application/json');
    if (requestArg.url === '/api/version') {
      responseArg.end(JSON.stringify({ api: '2.0.0', version: '5.1.0', printer: 'Original Prusa MK4', text: 'MK4', firmware: '6.0.1', hostname: 'mk4' }));
      return;
    }
    if (requestArg.url === '/api/v1/status') {
      responseArg.end(JSON.stringify({ printer: { state: 'PRINTING', temp_nozzle: 210, temp_bed: 61, speed: 100 } }));
      return;
    }
    if (requestArg.url === '/api/v1/job') {
      responseArg.end(JSON.stringify({ id: 7, state: 'PRINTING', progress: 33, time_printing: 300, file: { display_name: 'cube.gcode', refs: { thumbnail: '/thumb/cube.png' } } }));
      return;
    }
    if (requestArg.url === '/api/v1/info') {
      responseArg.end(JSON.stringify({ name: 'Network MK4', serial: 'SN123', mmu: false, nozzle_diameter: 0.4 }));
      return;
    }
    if (requestArg.url === '/api/printer') {
      responseArg.end(JSON.stringify({ telemetry: { material: 'PLA' } }));
      return;
    }
    if (requestArg.url === '/api/v1/job/7/pause' && requestArg.method === 'PUT') {
      responseArg.statusCode = 204;
      responseArg.end();
      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 PrusaLinkClient({ host: '127.0.0.1', port, username: 'maker', password: 'secret', timeoutMs: 1000 });
    const snapshot = await client.getSnapshot();
    await client.execute({ action: 'pause', jobId: 7, method: 'PUT', path: '/api/v1/job/7/pause' });

    expect(snapshot.online).toBeTrue();
    expect(snapshot.source).toEqual('http');
    expect(snapshot.printer.serialNumber).toEqual('SN123');
    expect(snapshot.telemetry.tempNozzle).toEqual(210);
    expect(snapshot.telemetry.material).toEqual('PLA');
    expect(snapshot.job.progress).toEqual(33);
    expect(requests.some((requestArg) => requestArg.url === '/api/version' && !requestArg.authorization)).toBeTrue();
    expect(requests.some((requestArg) => requestArg.url === '/api/version' && requestArg.authorization?.startsWith('Digest '))).toBeTrue();
    expect(requests.some((requestArg) => requestArg.url === '/api/v1/job/7/pause' && requestArg.method === 'PUT' && requestArg.authorization?.includes('uri="/api/v1/job/7/pause"'))).toBeTrue();
  } finally {
    await new Promise<void>((resolve, reject) => server.close((errorArg) => errorArg ? reject(errorArg) : resolve()));
  }
});

tap.test('does not report live command success without HTTP transport or executor', async () => {
  const snapshot = PrusaLinkMapper.toSnapshot({
    config: { name: 'Static MK4' },
    rawData: {
      status: { printer: { state: 'PRINTING' } },
      job: { id: 7, state: 'PRINTING', progress: 50 },
    },
    online: true,
    source: 'manual',
  });
  const runtime = await new PrusaLinkIntegration().setup({ snapshot }, {});
  const result = await runtime.callService?.({ domain: 'prusalink', service: 'pause_job', target: {} });

  expect(result?.success).toBeFalse();
  expect(result?.error).toContain('config.host or commandExecutor');
  await runtime.destroy();
});

tap.test('models restart commands safely through an injected executor', async () => {
  const commands: IPrusaLinkCommandRequest[] = [];
  const snapshot = PrusaLinkMapper.toSnapshot({
    config: { name: 'Executor MK4' },
    rawData: {
      status: { printer: { state: 'PAUSED' } },
      job: { id: 7, state: 'PAUSED', progress: 50 },
    },
    online: true,
    source: 'manual',
  });
  const runtime = await new PrusaLinkIntegration().setup({
    snapshot,
    commandExecutor: {
      execute: async (requestArg) => {
        commands.push(requestArg);
        return { accepted: true };
      },
    },
  }, {});

  const result = await runtime.callService?.({ domain: 'prusalink', service: 'restart_job', target: {}, data: { job_id: 7 } });

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

export default tap.start();
