/**
 * Server Lifecycle Utilities
 *
 * Runtime-aware helpers for server startup, path rewriting, and CORS handling.
 */

import chalk from "chalk";
import type { Hono as HonoType } from "hono";
import { getEnv, isDeno } from "./runtime.js";

export function isProductionMode(): boolean {
  // Only check NODE_ENV - CLI commands set this explicitly
  // 'mcp-use dev' sets NODE_ENV=development
  // 'mcp-use start' sets NODE_ENV=production
  return getEnv("NODE_ENV") === "production";
}

/**
 * Get Deno-specific CORS headers
 *
 * @returns CORS headers object for Deno responses
 */
export function getDenoCorsHeaders(): Record<string, string> {
  return {
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Headers":
      "authorization, x-client-info, apikey, content-type",
  };
}

/**
 * Apply CORS headers to a response
 *
 * @param response - The response to add CORS headers to
 * @returns New response with CORS headers applied
 */
export function applyDenoCorsHeaders(response: Response): Response {
  const corsHeaders = getDenoCorsHeaders();
  const newHeaders = new Headers(response.headers);
  Object.entries(corsHeaders).forEach(([key, value]) => {
    newHeaders.set(key, value);
  });

  return new Response(response.body, {
    status: response.status,
    statusText: response.statusText,
    headers: newHeaders,
  });
}

/**
 * Create a Supabase path rewriter function
 *
 * Supabase includes the function name in the path (e.g., /functions/v1/mcp-server/mcp or /mcp-server/mcp)
 * This function strips the function name prefix to get the actual route path.
 *
 * @returns Function that rewrites Supabase paths to actual route paths
 */
function createSupabasePathRewriter(): (pathname: string) => string {
  return (pathname: string): string => {
    let newPathname = pathname;

    // Match /functions/v1/{anything}/... and strip up to the function name
    const functionsMatch = pathname.match(/^\/functions\/v1\/[^/]+(\/.*)?$/);
    if (functionsMatch) {
      newPathname = functionsMatch[1] || "/";
    } else {
      // Match /{function-name}/... pattern
      const functionNameMatch = pathname.match(/^\/([^/]+)(\/.*)?$/);
      if (functionNameMatch && functionNameMatch[2]) {
        newPathname = functionNameMatch[2] || "/";
      }
    }

    return newPathname;
  };
}

/**
 * Rewrite request path for Supabase environment
 *
 * @param req - The original request
 * @returns New request with rewritten path if needed
 */
export function rewriteSupabaseRequest(req: Request): Request {
  const url = new URL(req.url);
  const pathname = url.pathname;
  const rewriter = createSupabasePathRewriter();
  const newPathname = rewriter(pathname);

  // Only create new request if path changed
  if (newPathname !== pathname) {
    const newUrl = new URL(newPathname + url.search, url.origin);
    return new Request(newUrl, {
      method: req.method,
      headers: req.headers,
      body: req.body,
      redirect: req.redirect,
    });
  }

  return req;
}

/** Handle to cleanly shut down the HTTP listener (Node) or no-op (Deno). */
type HttpServerHandle = {
  close: () => Promise<void>;
  /** Force-close all connections and stop listening immediately (Node 18.2+). */
  forceClose: () => Promise<void>;
};

/**
 * Start the provided Hono application as an HTTP server for the current runtime.
 *
 * @param app - Hono application instance to serve
 * @param port - Port number to listen on
 * @param host - Hostname to bind to
 * @param options - Optional runtime-specific hooks
 * @param options.onDenoRequest - Transform an incoming Deno `Request` before it is passed to the application
 * @param options.onDenoResponse - Transform a Deno `Response` before it is returned (if omitted, default CORS headers are applied)
 * @returns A handle whose {@link HttpServerHandle.close} stops the listener (Node); Deno returns a no-op close.
 */
export async function startServer(
  app: HonoType,
  port: number,
  host: string,
  options?: {
    onDenoRequest?: (req: Request) => Request | Promise<Request>;
    onDenoResponse?: (res: Response) => Response | Promise<Response>;
  }
): Promise<HttpServerHandle> {
  if (isDeno) {
    // Deno runtime
    const corsHeaders = getDenoCorsHeaders();

    (globalThis as any).Deno.serve(
      { port, hostname: host },
      async (req: Request) => {
        // Handle CORS preflight requests
        if (req.method === "OPTIONS") {
          return new Response("ok", { headers: corsHeaders });
        }

        // Apply request transformation if provided
        let finalReq = req;
        if (options?.onDenoRequest) {
          finalReq = await options.onDenoRequest(req);
        }

        // Call the app handler
        let response = await app.fetch(finalReq);

        // Apply response transformation if provided
        if (options?.onDenoResponse) {
          response = await options.onDenoResponse(response);
        } else {
          // Default: apply CORS headers
          response = applyDenoCorsHeaders(response);
        }

        return response;
      }
    );
    console.log(`[SERVER] Listening`);
    console.log(
      chalk.gray(
        `[MCP] Tip: connect with the CLI → npx mcp-use client connect http://${host}:${port}/mcp`
      )
    );
    return {
      close: async () => {},
      forceClose: async () => {},
    };
  } else {
    // Node.js runtime
    const { serve } = await import("@hono/node-server");
    const server = serve(
      {
        fetch: app.fetch,
        port,
        hostname: host,
      },
      (_info: any) => {
        console.log(`[SERVER] Listening on http://${host}:${port}`);
        console.log(`[MCP] Endpoints: http://${host}:${port}/mcp`);
        console.log(
          chalk.gray(
            `[MCP] Tip: connect with the CLI → npx mcp-use client connect http://${host}:${port}/mcp`
          )
        );
      }
    );

    // Set up Vite HMR WebSocket proxy if available.
    // This forwards WebSocket upgrades from the main server to Vite's internal WS port,
    // enabling HMR through reverse proxies (ngrok, E2B, etc.)
    const wsProxySetup = (app as any).__viteWsProxy;
    if (wsProxySetup && server) {
      wsProxySetup(server);
    }

    const nodeServer = server as import("node:http").Server;
    return {
      close: () =>
        new Promise((resolve, reject) => {
          nodeServer.close((err) => (err ? reject(err) : resolve()));
        }),
      forceClose: () =>
        new Promise<void>((resolve) => {
          if (typeof nodeServer.closeAllConnections === "function") {
            nodeServer.closeAllConnections();
          }
          nodeServer.close(() => resolve());
        }),
    };
  }
}
