import type { FastifyInstance } from 'fastify';
import WebSocket from 'ws';

function boocontrolWsUrl(origin: string, path: string): string {
  const u = new URL(origin);
  u.protocol = u.protocol === 'https:' ? 'wss:' : 'ws:';
  u.pathname = path;
  u.search = '';
  return u.toString();
}

/**
 * Reverse-proxy /api/control/* HTTP + /api/control/ws WS through BooChat's
 * single origin.
 *
 * CLAUDE.md keep-in-sync: this file mirrors routes/coder-proxy.ts. Keep the
 * two files in sync — if you change one, update the other.
 */
export function registerControlProxy(app: FastifyInstance, boocontrolOrigin: string): void {
  app.get('/api/control/ws', { websocket: true }, (clientSocket, _req) => {
    const target = boocontrolWsUrl(boocontrolOrigin, '/api/ws/control');
    const upstream = new WebSocket(target);

    upstream.on('open', () => {
      app.log.debug('control ws proxy: upstream connected');
    });

    upstream.on('message', (data, isBinary) => {
      if (clientSocket.readyState !== clientSocket.OPEN) return;
      clientSocket.send(data, { binary: isBinary });
    });

    upstream.on('close', (code, reason) => {
      if (clientSocket.readyState === clientSocket.OPEN) {
        clientSocket.close(code, reason.toString());
      }
    });

    upstream.on('error', (err) => {
      app.log.warn({ err, target }, 'control ws proxy: upstream error');
      if (clientSocket.readyState === clientSocket.OPEN) {
        clientSocket.close(1011, 'upstream error');
      }
    });

    clientSocket.on('message', (data, isBinary) => {
      if (upstream.readyState !== WebSocket.OPEN) return;
      upstream.send(data, { binary: isBinary });
    });

    clientSocket.on('close', () => {
      if (upstream.readyState === WebSocket.OPEN || upstream.readyState === WebSocket.CONNECTING) {
        upstream.close();
      }
    });

    clientSocket.on('error', () => {
      if (upstream.readyState === WebSocket.OPEN || upstream.readyState === WebSocket.CONNECTING) {
        upstream.close();
      }
    });
  });

  app.all('/api/control/*', async (req, reply) => {
    const targetPath = req.url.replace('/api/control', '/api');
    const targetUrl = `${boocontrolOrigin}${targetPath}`;
    const headers: Record<string, string> = {};
    if (req.headers['content-type']) headers['content-type'] = req.headers['content-type'] as string;
    if (req.headers['authorization']) headers['authorization'] = req.headers['authorization'] as string;

    try {
      const res = await fetch(targetUrl, {
        method: req.method as string,
        headers,
        body: req.method !== 'GET' && req.method !== 'HEAD' ? JSON.stringify(req.body) : undefined,
      });
      reply.code(res.status);
      for (const [key, value] of res.headers) {
        if (key === 'transfer-encoding') continue;
        reply.header(key, value);
      }
      const body = await res.text();
      return reply.send(body);
    } catch (err) {
      app.log.error({ err, targetUrl }, 'control proxy error');
      reply.code(502).send({ error: 'control backend unavailable' });
    }
  });
}
