/**
 * Mock HTTP Server
 *
 * Simulates HTTP server responses without actual network operations.
 * No port binding - instant responses for fast testing.
 */

import type {
  HttpMethod,
  MockHttpServer,
  MockHttpServerConfig,
  MockResponse,
  CapturedRequest,
  RouteKey,
} from './types';

/** Default 404 response for unmatched routes */
const DEFAULT_NOT_FOUND: MockResponse = {
  status: 404,
  body: { error: 'Not Found' },
};

/**
 * Create a mock HTTP server that handles requests without network
 *
 * @example
 * const server = createMockHttpServer({
 *   routes: {
 *     'GET /health': { status: 200, body: { healthy: true } },
 *     'POST /upload': { status: 201, body: { id: '123' } },
 *   }
 * });
 * const response = server.handle('GET', '/health');
 */
export function createMockHttpServer(config: MockHttpServerConfig): MockHttpServer {
  const capturedRequests: CapturedRequest[] = [];
  const { routes, defaultResponse = DEFAULT_NOT_FOUND } = config;

  return {
    handle(method: HttpMethod, path: string, body?: unknown): MockResponse {
      // Capture request for assertions
      capturedRequests.push({
        url: path,
        method,
        headers: {},
        body: body ? JSON.stringify(body) : null,
      });

      // Find matching route
      const routeKey = `${method} ${path}` as RouteKey;
      const exactMatch = routes[routeKey];
      if (exactMatch) {
        return exactMatch;
      }

      // Try pattern matching for routes with wildcards
      for (const [key, response] of Object.entries(routes)) {
        // Split on first space only to preserve spaces in path
        const spaceIndex = key.indexOf(' ');
        if (spaceIndex === -1) continue;
        const routeMethod = key.slice(0, spaceIndex);
        const routePath = key.slice(spaceIndex + 1);
        if (routeMethod !== method) continue;

        // Check if route path is a pattern (contains *)
        if (routePath.includes('*')) {
          // Escape regex special chars, then replace * with .*
          const escaped = routePath.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
          const pattern = escaped.replace(/\*/g, '.*');
          const regex = new RegExp(`^${pattern}$`);
          if (regex.test(path)) {
            return response;
          }
        }
      }

      return defaultResponse;
    },

    getCapturedRequests(): CapturedRequest[] {
      return [...capturedRequests];
    },

    clearCapturedRequests(): void {
      capturedRequests.length = 0;
    },
  };
}

/**
 * Create a mock Response object (Web API compatible)
 *
 * @example
 * const response = createMockResponse({ status: 200, body: { ok: true } });
 */
export function createMockResponse(config: MockResponse): Response {
  const { status = 200, body, headers = {} } = config;

  let responseBody: string | null = null;
  const responseHeaders = new Headers(headers);

  if (body !== null && body !== undefined) {
    if (typeof body === 'object' && !(body instanceof Buffer)) {
      responseBody = JSON.stringify(body);
      if (!responseHeaders.has('Content-Type')) {
        responseHeaders.set('Content-Type', 'application/json');
      }
    } else if (body instanceof Buffer) {
      responseBody = body.toString();
    } else {
      responseBody = body;
    }
  }

  return new Response(responseBody, {
    status,
    headers: responseHeaders,
  });
}
