import { describe, it } from 'node:test';
import { strict as assert } from 'node:assert';
import * as fs from 'node:fs';
import * as net from 'node:net';
import * as os from 'node:os';
import * as path from 'node:path';

import type { ILoadedMatrixPackageEnvironment } from '../../src/utils/package-environment.js';
import type { IResolvedRunnerTransportPlan } from '../../src/utils/runner-transport.js';
import type { ILoadedMatrixWebappManifest } from '../../src/utils/webapp-manifest.js';
import { startStandaloneWebappServer } from '../../src/utils/standalone-webapp-server.js';

async function getAvailablePort(): Promise<number> {
  const server = await new Promise<net.Server>((resolve, reject) => {
    const candidate = net.createServer();
    candidate.once('error', reject);
    candidate.listen(0, '127.0.0.1', () => resolve(candidate));
  });
  const address = server.address();
  assert.ok(address && typeof address === 'object');
  const port = address.port;
  await new Promise<void>((resolve, reject) => {
    server.close((error) => (error ? reject(error) : resolve()));
  });
  return port;
}

describe('standalone webapp server', () => {
  it('advertises JWT browser auth when the runner transport uses credentialsRef', async () => {
    const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'standalone-webapp-server-'));
    const packageDir = path.join(tempRoot, 'package');
    const distDir = path.join(packageDir, 'dist');
    fs.mkdirSync(distDir, { recursive: true });
    const httpPort = await getAvailablePort();
    const credentialsPath = path.join(tempRoot, 'credentials.json');
    fs.writeFileSync(credentialsPath, JSON.stringify({
      jwt: 'sample.jwt.value',
      seed: 'SUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
    }), 'utf8');
    fs.writeFileSync(
      path.join(distDir, 'index.html'),
      '<!doctype html><html><body>fixture</body></html>',
      'utf8',
    );

    const loadedEnvironment: ILoadedMatrixPackageEnvironment = {
      packageDir,
      envName: 'host',
      environmentPath: path.join(packageDir, '.matrix', 'host.environment.json'),
      environment: {
        name: 'host',
        runtime: { root: 'COM.TEST.EXTERNAL' },
        nats: {
          mode: 'external',
          url: 'nats://example.test:4222',
          wsUrl: 'wss://example.test/nats-ws',
          credentialsRef: `file:${credentialsPath}`,
        },
        http: {
          enabled: true,
          port: httpPort,
        },
      },
    };
    const webapp: ILoadedMatrixWebappManifest = {
      packageDir,
      packageName: '@acme/webapp',
      manifestPath: path.join(packageDir, 'matrix.json'),
      distDir,
      entryFile: 'index.html',
      appName: 'webapp',
    };
    const runnerTransport: IResolvedRunnerTransportPlan = {
      envName: 'host',
      root: 'COM.TEST.EXTERNAL',
      mode: 'external',
      url: 'nats://example.test:4222',
      wsUrl: 'wss://example.test/nats-ws',
      credentialsRef: `file:${credentialsPath}`,
    };

    const server = await startStandaloneWebappServer({
      loadedEnvironment,
      webapp,
      runnerTransport,
    });
    try {
      const bootstrap = await fetch(`${server.baseUrl}/api/bootstrap`);
      assert.equal(bootstrap.status, 200);
      const bootstrapPayload = await bootstrap.json() as {
        authorityContext?: {
          assetHostRoot?: string;
          platformServiceRoot?: string;
          sessionAuthorityRoot?: string | null;
          selectedAuthorityRoot?: string | null;
          runtimeInstanceRoot?: string | null;
          isAuthenticated?: boolean;
          isPlatformAdmin?: boolean;
          selectableAuthorityRoots?: string[];
        };
        authorityRoot?: string;
        busRoot?: string;
        runtimeInstanceRoot?: string | null;
        transportPlan?: {
          authMode?: string;
          authEndpoint?: string | null;
          wsPath?: string;
        };
        sources?: {
          transportAuthMode?: string;
        };
      };
      assert.deepEqual(bootstrapPayload.authorityContext, {
        assetHostRoot: 'COM.TEST.EXTERNAL',
        platformServiceRoot: 'COM.TEST.EXTERNAL',
        sessionAuthorityRoot: 'COM.TEST.EXTERNAL',
        selectedAuthorityRoot: 'COM.TEST.EXTERNAL',
        runtimeInstanceRoot: null,
        isAuthenticated: true,
        isPlatformAdmin: false,
        selectableAuthorityRoots: ['COM.TEST.EXTERNAL'],
      });
      assert.equal(bootstrapPayload.authorityRoot, 'COM.TEST.EXTERNAL');
      assert.equal(bootstrapPayload.busRoot, 'COM.TEST.EXTERNAL');
      assert.equal(bootstrapPayload.runtimeInstanceRoot, null);
      assert.equal(bootstrapPayload.transportPlan?.wsPath, '/nats-ws');
      assert.equal(bootstrapPayload.transportPlan?.authMode, 'jwt');
      assert.equal(bootstrapPayload.transportPlan?.authEndpoint, '/api/nats-jwt');
      assert.equal(bootstrapPayload.sources?.transportAuthMode, 'credentials-ref');

      const auth = await fetch(`${server.baseUrl}/api/nats-jwt`, { method: 'POST' });
      assert.equal(auth.status, 200);
      const authPayload = await auth.json() as {
        mode?: string;
        jwt?: string;
        seed?: string;
      };
      assert.equal(authPayload.mode, 'jwt');
      assert.equal(authPayload.jwt, 'sample.jwt.value');
      assert.equal(authPayload.seed, 'SUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
    } finally {
      await server.shutdown();
    }
  });

  it('forwards explicit upstream WebSocket paths without duplicating /nats-ws', async () => {
    const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'standalone-webapp-server-proxy-'));
    const packageDir = path.join(tempRoot, 'package');
    const distDir = path.join(packageDir, 'dist');
    fs.mkdirSync(distDir, { recursive: true });
    fs.writeFileSync(
      path.join(distDir, 'index.html'),
      '<!doctype html><html><body>fixture</body></html>',
      'utf8',
    );
    const httpPort = await getAvailablePort();
    const upstreamPort = await getAvailablePort();
    let capturedRequest = '';
    const upstream = net.createServer();
    const captured = new Promise<void>((resolve) => {
      upstream.on('connection', (socket) => {
        socket.once('data', (chunk: Buffer) => {
          capturedRequest = chunk.toString('utf8');
          socket.end('HTTP/1.1 404 Not Found\r\nConnection: close\r\nContent-Length: 0\r\n\r\n');
          resolve();
        });
      });
    });
    await new Promise<void>((resolve, reject) => {
      upstream.once('error', reject);
      upstream.listen(upstreamPort, '127.0.0.1', () => {
        upstream.off('error', reject);
        resolve();
      });
    });

    const loadedEnvironment: ILoadedMatrixPackageEnvironment = {
      packageDir,
      envName: 'host',
      environmentPath: path.join(packageDir, '.matrix', 'host.environment.json'),
      environment: {
        name: 'host',
        runtime: { root: 'COM.TEST.EXTERNAL' },
        nats: {
          mode: 'external',
          url: `nats://127.0.0.1:${upstreamPort}`,
          wsUrl: `ws://127.0.0.1:${upstreamPort}/nats-ws`,
        },
        http: {
          enabled: true,
          port: httpPort,
        },
      },
    };
    const webapp: ILoadedMatrixWebappManifest = {
      packageDir,
      packageName: '@acme/webapp',
      manifestPath: path.join(packageDir, 'matrix.json'),
      distDir,
      entryFile: 'index.html',
      appName: 'webapp',
    };
    const runnerTransport: IResolvedRunnerTransportPlan = {
      envName: 'host',
      root: 'COM.TEST.EXTERNAL',
      mode: 'external',
      url: `nats://127.0.0.1:${upstreamPort}`,
      wsUrl: `ws://127.0.0.1:${upstreamPort}/nats-ws`,
    };

    const server = await startStandaloneWebappServer({
      loadedEnvironment,
      webapp,
      runnerTransport,
    });
    const client = net.connect(httpPort, '127.0.0.1');
    try {
      await new Promise<void>((resolve) => {
        client.once('connect', resolve);
      });
      client.write([
        'GET /nats-ws?probe=1 HTTP/1.1',
        `Host: 127.0.0.1:${httpPort}`,
        'Connection: Upgrade',
        'Upgrade: websocket',
        'Origin: http://127.0.0.1:9999',
        'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==',
        'Sec-WebSocket-Version: 13',
        '',
        '',
      ].join('\r\n'));
      await captured;
      assert.match(capturedRequest, /^GET \/nats-ws\?probe=1 HTTP\/1\.1\r\n/);
      assert.doesNotMatch(capturedRequest, /\/nats-ws\/nats-ws/);
      assert.doesNotMatch(capturedRequest, /\r\nOrigin:/i);
    } finally {
      client.destroy();
      await server.shutdown();
      await new Promise<void>((resolve) => {
        upstream.close(() => resolve());
      });
    }
  });
});
