import type { IncomingMessage } from "http";

import { timingSafeEqual } from "node:crypto";

export interface AuthConfig {
  apiKey?: string;
  oauth?: {
    error?: string;
    error_description?: string;
    error_uri?: string;
    protectedResource?: {
      resource?: string;
    };
    realm?: string;
    scope?: string;
  };
}

export class AuthenticationMiddleware {
  constructor(private config: AuthConfig = {}) {}

  getScopeChallengeResponse(
    requiredScopes: string[],
    errorDescription?: string,
    requestId?: unknown,
  ): { body: string; headers: Record<string, string>; statusCode: number } {
    const headers: Record<string, string> = {
      "Content-Type": "application/json",
    };

    // Build WWW-Authenticate header with all required parameters
    if (this.config.oauth?.protectedResource?.resource) {
      const parts = [
        "Bearer",
        'error="insufficient_scope"',
        `scope="${requiredScopes.join(" ")}"`,
        `resource_metadata="${this.config.oauth.protectedResource.resource}/.well-known/oauth-protected-resource"`,
      ];

      if (errorDescription) {
        // Escape quotes in description
        const escaped = errorDescription.replace(/"/g, '\\"');
        parts.push(`error_description="${escaped}"`);
      }

      headers["WWW-Authenticate"] = parts.join(", ");
    }

    return {
      body: JSON.stringify({
        error: {
          code: -32001, // Custom error code for insufficient scope
          data: {
            error: "insufficient_scope",
            required_scopes: requiredScopes,
          },
          message: errorDescription || "Insufficient scope",
        },
        id: requestId ?? null,
        jsonrpc: "2.0",
      }),
      headers,
      statusCode: 403,
    };
  }

  getUnauthorizedResponse(options?: {
    error?: string;
    error_description?: string;
    error_uri?: string;
    scope?: string;
  }): { body: string; headers: Record<string, string> } {
    const headers: Record<string, string> = {
      "Content-Type": "application/json",
    };

    // Build WWW-Authenticate header if OAuth config is available
    if (this.config.oauth) {
      const params: string[] = [];

      // Add realm if configured
      if (this.config.oauth.realm) {
        params.push(`realm="${this.config.oauth.realm}"`);
      }

      // Add resource_metadata if configured
      if (this.config.oauth.protectedResource?.resource) {
        params.push(
          `resource_metadata="${this.config.oauth.protectedResource.resource}/.well-known/oauth-protected-resource"`,
        );
      }

      // Add error from options or config (options takes precedence)
      const error =
        options?.error || this.config.oauth.error || "invalid_token";
      params.push(`error="${error}"`);

      // Add error_description from options or config (options takes precedence)
      const error_description =
        options?.error_description ||
        this.config.oauth.error_description ||
        "Unauthorized: Invalid or missing API key";
      // Escape quotes in error description
      const escaped = error_description.replace(/"/g, '\\"');
      params.push(`error_description="${escaped}"`);

      // Add error_uri from options or config (options takes precedence)
      const error_uri = options?.error_uri || this.config.oauth.error_uri;
      if (error_uri) {
        params.push(`error_uri="${error_uri}"`);
      }

      // Add scope from options or config (options takes precedence)
      const scope = options?.scope || this.config.oauth.scope;
      if (scope) {
        params.push(`scope="${scope}"`);
      }

      if (params.length > 0) {
        headers["WWW-Authenticate"] = `Bearer ${params.join(", ")}`;
      }
    }

    return {
      body: JSON.stringify({
        error: {
          code: 401,
          message:
            options?.error_description ||
            "Unauthorized: Invalid or missing API key",
        },
        id: null,
        jsonrpc: "2.0",
      }),
      headers,
    };
  }

  validateRequest(req: IncomingMessage): boolean {
    // No auth required if no API key configured (backward compatibility)
    if (!this.config.apiKey) {
      return true;
    }

    // Check X-API-Key header (case-insensitive)
    // Node.js http module automatically converts all header names to lowercase
    const apiKey = req.headers["x-api-key"];

    if (!apiKey || typeof apiKey !== "string") {
      return false;
    }

    const expected = Buffer.from(this.config.apiKey);
    const actual = Buffer.from(apiKey);
    return actual.length === expected.length && timingSafeEqual(actual, expected);
  }
}
