import { createServer } from 'node:http';
import type { IncomingMessage, ServerResponse } from 'node:http';

import type { Id } from '@spoon/backend/convex/_generated/dataModel.js';

import { env } from './env';
import { attachTerminalServer } from './terminal';
import {
  abortWorkspaceAgent,
  cleanupOrphanedWorkspaces,
  getWorkerHealth,
  getWorkspaceAgentStatus,
  getWorkspaceDiff,
  listWorkspaceTree,
  openWorkspacePullRequest,
  readWorkspaceFile,
  replyToInteraction,
  runWorkspaceCommand,
  sendWorkspaceMessage,
  stopWorkspace,
  writeWorkspaceFile,
} from './worker';

const sendJson = (response: ServerResponse, status: number, body: unknown) => {
  response.writeHead(status, { 'content-type': 'application/json' });
  response.end(JSON.stringify(body));
};

const readBody = async (request: IncomingMessage) =>
  await new Promise<string>((resolve, reject) => {
    let body = '';
    request.on('data', (chunk: Buffer) => {
      body += chunk.toString('utf8');
    });
    request.on('end', () => resolve(body));
    request.on('error', reject);
  });

const parseJson = async <T>(request: IncomingMessage) => {
  const body = await readBody(request);
  if (!body.trim()) return {} as T;
  return JSON.parse(body) as T;
};

const requireAuth = (request: IncomingMessage) => {
  const header = request.headers.authorization;
  const token = header?.startsWith('Bearer ') ? header.slice(7) : '';
  if (!env.internalToken || token !== env.internalToken) {
    throw new Error('Unauthorized');
  }
};

const jobRoute = (pathname: string) => {
  const match = /^\/jobs\/([^/]+)\/(.+)$/.exec(pathname);
  if (!match?.[1] || !match[2]) return null;
  return { jobId: decodeURIComponent(match[1]), action: match[2] };
};

export const startWorkerServer = () => {
  const server = createServer((request, response) => {
    void (async () => {
      try {
        requireAuth(request);
        const url = new URL(
          request.url ?? '/',
          `http://localhost:${env.httpPort}`,
        );
        if (url.pathname === '/health' && request.method === 'GET') {
          sendJson(response, 200, await getWorkerHealth());
          return;
        }
        if (url.pathname === '/cleanup' && request.method === 'POST') {
          sendJson(response, 200, await cleanupOrphanedWorkspaces());
          return;
        }
        const route = jobRoute(url.pathname);
        if (!route) {
          sendJson(response, 404, { error: 'Not found' });
          return;
        }

        if (request.method === 'GET' && route.action === 'tree') {
          sendJson(response, 200, {
            tree: await listWorkspaceTree(route.jobId),
          });
          return;
        }
        if (request.method === 'GET' && route.action === 'file') {
          const filePath = url.searchParams.get('path') ?? '';
          sendJson(response, 200, {
            path: filePath,
            content: await readWorkspaceFile(route.jobId, filePath),
          });
          return;
        }
        if (request.method === 'PUT' && route.action === 'file') {
          const body = await parseJson<{ path?: string; content?: string }>(
            request,
          );
          sendJson(
            response,
            200,
            await writeWorkspaceFile(
              route.jobId,
              body.path ?? '',
              body.content ?? '',
            ),
          );
          return;
        }
        if (request.method === 'GET' && route.action === 'diff') {
          sendJson(response, 200, {
            diff: await getWorkspaceDiff(route.jobId),
          });
          return;
        }
        if (request.method === 'POST' && route.action === 'message') {
          const body = await parseJson<{ content?: string }>(request);
          await sendWorkspaceMessage(route.jobId, body.content ?? '');
          sendJson(response, 200, { success: true });
          return;
        }
        if (request.method === 'GET' && route.action === 'agent/status') {
          sendJson(response, 200, getWorkspaceAgentStatus(route.jobId));
          return;
        }
        if (request.method === 'POST' && route.action === 'agent/abort') {
          sendJson(response, 200, await abortWorkspaceAgent(route.jobId));
          return;
        }
        const interactionMatch = /^interactions\/([^/]+)\/reply$/.exec(
          route.action,
        );
        if (request.method === 'POST' && interactionMatch?.[1]) {
          const body = await parseJson<{
            externalRequestId?: string;
            response?: string;
          }>(request);
          sendJson(
            response,
            200,
            await replyToInteraction(route.jobId, {
              interactionId: decodeURIComponent(
                interactionMatch[1],
              ) as Id<'agentInteractionRequests'>,
              externalRequestId: body.externalRequestId ?? '',
              response: body.response ?? 'once',
            }),
          );
          return;
        }
        if (request.method === 'POST' && route.action === 'run-command') {
          const body = await parseJson<{ command?: string }>(request);
          sendJson(
            response,
            200,
            await runWorkspaceCommand(route.jobId, body.command ?? ''),
          );
          return;
        }
        if (request.method === 'POST' && route.action === 'open-pr') {
          sendJson(response, 200, await openWorkspacePullRequest(route.jobId));
          return;
        }
        if (request.method === 'POST' && route.action === 'stop') {
          sendJson(response, 200, await stopWorkspace(route.jobId));
          return;
        }
        sendJson(response, 404, { error: 'Not found' });
      } catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        console.error(
          `Worker HTTP ${request.method ?? 'UNKNOWN'} ${request.url ?? '/'} failed: ${message}`,
        );
        const status =
          message === 'Unauthorized'
            ? 401
            : message.includes('not supported')
              ? 409
              : 500;
        sendJson(response, status, {
          error: message,
        });
      }
    })();
  });
  attachTerminalServer(server);
  server.listen(env.httpPort, () => {
    console.log(
      `Spoon agent worker HTTP server listening on port ${env.httpPort}`,
    );
  });
};
