import { IncomingMessage } from "http";
import { describe, expect, it } from "vitest";

import { AuthenticationMiddleware } from "./authentication.js";

describe("AuthenticationMiddleware", () => {
  const createMockRequest = (
    headers: Record<string, string> = {},
  ): IncomingMessage => {
    // Simulate Node.js http module behavior which converts all header names to lowercase
    const lowercaseHeaders: Record<string, string> = {};
    for (const [key, value] of Object.entries(headers)) {
      lowercaseHeaders[key.toLowerCase()] = value;
    }
    return {
      headers: lowercaseHeaders,
    } as IncomingMessage;
  };

  describe("when no auth is configured", () => {
    it("should allow all requests", () => {
      const middleware = new AuthenticationMiddleware({});
      const req = createMockRequest();

      expect(middleware.validateRequest(req)).toBe(true);
    });

    it("should allow requests even with headers", () => {
      const middleware = new AuthenticationMiddleware({});
      const req = createMockRequest({ "x-api-key": "some-key" });

      expect(middleware.validateRequest(req)).toBe(true);
    });
  });

  describe("X-API-Key validation", () => {
    const apiKey = "test-api-key-123";

    it("should accept valid API key", () => {
      const middleware = new AuthenticationMiddleware({ apiKey });
      const req = createMockRequest({ "x-api-key": apiKey });

      expect(middleware.validateRequest(req)).toBe(true);
    });

    it("should reject missing API key", () => {
      const middleware = new AuthenticationMiddleware({ apiKey });
      const req = createMockRequest();

      expect(middleware.validateRequest(req)).toBe(false);
    });

    it("should reject incorrect API key", () => {
      const middleware = new AuthenticationMiddleware({ apiKey });
      const req = createMockRequest({ "x-api-key": "wrong-key" });

      expect(middleware.validateRequest(req)).toBe(false);
    });

    it("should reject same-length API key with different content", () => {
      const middleware = new AuthenticationMiddleware({ apiKey });
      const req = createMockRequest({ "x-api-key": "test-api-key-124" });

      expect(middleware.validateRequest(req)).toBe(false);
    });

    it("should reject API keys of arbitrary length without throwing", () => {
      const middleware = new AuthenticationMiddleware({ apiKey });
      const shortReq = createMockRequest({ "x-api-key": "a" });
      const longReq = createMockRequest({ "x-api-key": "a".repeat(1024) });

      expect(middleware.validateRequest(shortReq)).toBe(false);
      expect(middleware.validateRequest(longReq)).toBe(false);
    });

    it("should reject empty API key", () => {
      const middleware = new AuthenticationMiddleware({ apiKey });
      const req = createMockRequest({ "x-api-key": "" });

      expect(middleware.validateRequest(req)).toBe(false);
    });

    it("should be case-insensitive for header names", () => {
      const middleware = new AuthenticationMiddleware({ apiKey });
      const req = createMockRequest({ "X-API-KEY": apiKey });

      expect(middleware.validateRequest(req)).toBe(true);
    });

    it("should work with mixed case header names", () => {
      const middleware = new AuthenticationMiddleware({ apiKey });
      const req = createMockRequest({ "X-Api-Key": apiKey });

      expect(middleware.validateRequest(req)).toBe(true);
    });

    it("should handle array headers (if multiple same headers)", () => {
      const middleware = new AuthenticationMiddleware({ apiKey });
      const req = {
        headers: {
          "x-api-key": [apiKey, "another-key"],
        },
      } as unknown as IncomingMessage;

      // Should fail because header is an array, not a string
      expect(middleware.validateRequest(req)).toBe(false);
    });
  });

  describe("getUnauthorizedResponse", () => {
    it("should return proper unauthorized response", () => {
      const middleware = new AuthenticationMiddleware({ apiKey: "test" });
      const response = middleware.getUnauthorizedResponse();

      expect(response.headers["Content-Type"]).toBe("application/json");

      const body = JSON.parse(response.body);
      expect(body.error.code).toBe(401);
      expect(body.error.message).toBe(
        "Unauthorized: Invalid or missing API key",
      );
      expect(body.jsonrpc).toBe("2.0");
      expect(body.id).toBe(null);
    });

    it("should have consistent body format regardless of configuration", () => {
      const middleware1 = new AuthenticationMiddleware({});
      const middleware2 = new AuthenticationMiddleware({ apiKey: "test" });

      const response1 = middleware1.getUnauthorizedResponse();
      const response2 = middleware2.getUnauthorizedResponse();

      expect(response1.body).toEqual(response2.body);
    });

    it("should not include WWW-Authenticate header without OAuth config", () => {
      const middleware = new AuthenticationMiddleware({ apiKey: "test" });
      const response = middleware.getUnauthorizedResponse();

      expect(response.headers["WWW-Authenticate"]).toBeUndefined();
    });

    it("should include WWW-Authenticate header with OAuth config", () => {
      const middleware = new AuthenticationMiddleware({
        apiKey: "test",
        oauth: {
          protectedResource: {
            resource: "https://example.com",
          },
        },
      });
      const response = middleware.getUnauthorizedResponse();

      expect(response.headers["WWW-Authenticate"]).toBe(
        'Bearer resource_metadata="https://example.com/.well-known/oauth-protected-resource", error="invalid_token", error_description="Unauthorized: Invalid or missing API key"',
      );
    });

    it("should handle OAuth config with trailing slash in resource URL", () => {
      const middleware = new AuthenticationMiddleware({
        apiKey: "test",
        oauth: {
          protectedResource: {
            resource: "https://example.com/",
          },
        },
      });
      const response = middleware.getUnauthorizedResponse();

      expect(response.headers["WWW-Authenticate"]).toBe(
        'Bearer resource_metadata="https://example.com//.well-known/oauth-protected-resource", error="invalid_token", error_description="Unauthorized: Invalid or missing API key"',
      );
    });

    it("should include minimal WWW-Authenticate header when OAuth config is empty object", () => {
      const middleware = new AuthenticationMiddleware({
        apiKey: "test",
        oauth: {},
      });
      const response = middleware.getUnauthorizedResponse();

      // Even with empty oauth object, default error and error_description are added
      expect(response.headers["WWW-Authenticate"]).toBe(
        'Bearer error="invalid_token", error_description="Unauthorized: Invalid or missing API key"',
      );
    });

    it("should include minimal WWW-Authenticate header when protectedResource is empty", () => {
      const middleware = new AuthenticationMiddleware({
        apiKey: "test",
        oauth: {
          protectedResource: {},
        },
      });
      const response = middleware.getUnauthorizedResponse();

      // Even without resource_metadata, default error and error_description are added
      expect(response.headers["WWW-Authenticate"]).toBe(
        'Bearer error="invalid_token", error_description="Unauthorized: Invalid or missing API key"',
      );
    });

    it("should include WWW-Authenticate header with OAuth config but no apiKey", () => {
      const middleware = new AuthenticationMiddleware({
        oauth: {
          protectedResource: {
            resource: "https://example.com",
          },
        },
      });
      const response = middleware.getUnauthorizedResponse();

      expect(response.headers["WWW-Authenticate"]).toBe(
        'Bearer resource_metadata="https://example.com/.well-known/oauth-protected-resource", error="invalid_token", error_description="Unauthorized: Invalid or missing API key"',
      );
    });

    it("should include realm in WWW-Authenticate header when configured", () => {
      const middleware = new AuthenticationMiddleware({
        apiKey: "test",
        oauth: {
          protectedResource: {
            resource: "https://example.com",
          },
          realm: "example-realm",
        },
      });
      const response = middleware.getUnauthorizedResponse();

      expect(response.headers["WWW-Authenticate"]).toContain(
        'realm="example-realm"',
      );
      expect(response.headers["WWW-Authenticate"]).toContain(
        'resource_metadata="https://example.com/.well-known/oauth-protected-resource"',
      );
    });

    it("should include custom error in WWW-Authenticate header via options", () => {
      const middleware = new AuthenticationMiddleware({
        apiKey: "test",
        oauth: {
          protectedResource: {
            resource: "https://example.com",
          },
        },
      });
      const response = middleware.getUnauthorizedResponse({
        error: "insufficient_scope",
        error_description: "The request requires higher privileges",
      });

      expect(response.headers["WWW-Authenticate"]).toContain(
        'error="insufficient_scope"',
      );
      expect(response.headers["WWW-Authenticate"]).toContain(
        'error_description="The request requires higher privileges"',
      );
    });

    it("should include scope in WWW-Authenticate header", () => {
      const middleware = new AuthenticationMiddleware({
        apiKey: "test",
        oauth: {
          protectedResource: {
            resource: "https://example.com",
          },
          scope: "read write",
        },
      });
      const response = middleware.getUnauthorizedResponse();

      expect(response.headers["WWW-Authenticate"]).toContain(
        'scope="read write"',
      );
    });

    it("should override config error with options error", () => {
      const middleware = new AuthenticationMiddleware({
        apiKey: "test",
        oauth: {
          error: "invalid_request",
          error_description: "Config error description",
          protectedResource: {
            resource: "https://example.com",
          },
        },
      });
      const response = middleware.getUnauthorizedResponse({
        error: "invalid_token",
        error_description: "Options error description",
      });

      expect(response.headers["WWW-Authenticate"]).toContain(
        'error="invalid_token"',
      );
      expect(response.headers["WWW-Authenticate"]).toContain(
        'error_description="Options error description"',
      );
      expect(response.headers["WWW-Authenticate"]).not.toContain(
        "invalid_request",
      );
      expect(response.headers["WWW-Authenticate"]).not.toContain(
        "Config error description",
      );
    });

    it("should include error_uri in WWW-Authenticate header", () => {
      const middleware = new AuthenticationMiddleware({
        apiKey: "test",
        oauth: {
          error_uri: "https://example.com/errors/auth",
          protectedResource: {
            resource: "https://example.com",
          },
        },
      });
      const response = middleware.getUnauthorizedResponse();

      expect(response.headers["WWW-Authenticate"]).toContain(
        'error_uri="https://example.com/errors/auth"',
      );
    });

    it("should properly escape quotes in error_description", () => {
      const middleware = new AuthenticationMiddleware({
        apiKey: "test",
        oauth: {
          protectedResource: {
            resource: "https://example.com",
          },
        },
      });
      const response = middleware.getUnauthorizedResponse({
        error_description: 'Token "abc123" is invalid',
      });

      expect(response.headers["WWW-Authenticate"]).toContain(
        'error_description="Token \\"abc123\\" is invalid"',
      );
    });

    it("should include all parameters in correct order", () => {
      const middleware = new AuthenticationMiddleware({
        apiKey: "test",
        oauth: {
          error: "invalid_token",
          error_description: "Token expired",
          error_uri: "https://example.com/errors",
          protectedResource: {
            resource: "https://example.com",
          },
          realm: "my-realm",
          scope: "read write",
        },
      });
      const response = middleware.getUnauthorizedResponse();

      const header = response.headers["WWW-Authenticate"];
      expect(header).toContain('realm="my-realm"');
      expect(header).toContain(
        'resource_metadata="https://example.com/.well-known/oauth-protected-resource"',
      );
      expect(header).toContain('error="invalid_token"');
      expect(header).toContain('error_description="Token expired"');
      expect(header).toContain('error_uri="https://example.com/errors"');
      expect(header).toContain('scope="read write"');

      // Check order: realm, resource_metadata, error, error_description, error_uri, scope
      expect(header.indexOf("realm=")).toBeLessThan(
        header.indexOf("resource_metadata="),
      );
      expect(header.indexOf("resource_metadata=")).toBeLessThan(
        header.indexOf("error="),
      );
      expect(header.indexOf("error=")).toBeLessThan(
        header.indexOf("error_description="),
      );
    });
  });

  describe("getScopeChallengeResponse", () => {
    it("should return 403 status code", () => {
      const middleware = new AuthenticationMiddleware({
        oauth: {
          protectedResource: {
            resource: "https://example.com",
          },
        },
      });
      const response = middleware.getScopeChallengeResponse(["read", "write"]);

      expect(response.statusCode).toBe(403);
    });

    it("should include required scopes in WWW-Authenticate header", () => {
      const middleware = new AuthenticationMiddleware({
        oauth: {
          protectedResource: {
            resource: "https://example.com",
          },
        },
      });
      const response = middleware.getScopeChallengeResponse(["read", "write"]);

      expect(response.headers["WWW-Authenticate"]).toContain(
        'error="insufficient_scope"',
      );
      expect(response.headers["WWW-Authenticate"]).toContain(
        'scope="read write"',
      );
      expect(response.headers["WWW-Authenticate"]).toContain(
        'resource_metadata="https://example.com/.well-known/oauth-protected-resource"',
      );
    });

    it("should include error_description in WWW-Authenticate header", () => {
      const middleware = new AuthenticationMiddleware({
        oauth: {
          protectedResource: {
            resource: "https://example.com",
          },
        },
      });
      const response = middleware.getScopeChallengeResponse(
        ["admin"],
        "Admin access required",
      );

      expect(response.headers["WWW-Authenticate"]).toContain(
        'error_description="Admin access required"',
      );
    });

    it("should escape quotes in error_description", () => {
      const middleware = new AuthenticationMiddleware({
        oauth: {
          protectedResource: {
            resource: "https://example.com",
          },
        },
      });
      const response = middleware.getScopeChallengeResponse(
        ["admin"],
        'Requires "admin" scope',
      );

      expect(response.headers["WWW-Authenticate"]).toContain(
        'error_description="Requires \\"admin\\" scope"',
      );
    });

    it("should include request ID in response body", () => {
      const middleware = new AuthenticationMiddleware({
        oauth: {
          protectedResource: {
            resource: "https://example.com",
          },
        },
      });
      const response = middleware.getScopeChallengeResponse(
        ["read"],
        undefined,
        123,
      );

      const body = JSON.parse(response.body);
      expect(body.id).toBe(123);
    });

    it("should include required scopes in response body data", () => {
      const middleware = new AuthenticationMiddleware({
        oauth: {
          protectedResource: {
            resource: "https://example.com",
          },
        },
      });
      const response = middleware.getScopeChallengeResponse(["read", "write"]);

      const body = JSON.parse(response.body);
      expect(body.error.code).toBe(-32001);
      expect(body.error.message).toBe("Insufficient scope");
      expect(body.error.data.error).toBe("insufficient_scope");
      expect(body.error.data.required_scopes).toEqual(["read", "write"]);
    });

    it("should use custom error_description in response body message", () => {
      const middleware = new AuthenticationMiddleware({
        oauth: {
          protectedResource: {
            resource: "https://example.com",
          },
        },
      });
      const response = middleware.getScopeChallengeResponse(
        ["admin"],
        "Admin privileges required",
      );

      const body = JSON.parse(response.body);
      expect(body.error.message).toBe("Admin privileges required");
    });

    it("should handle single scope", () => {
      const middleware = new AuthenticationMiddleware({
        oauth: {
          protectedResource: {
            resource: "https://example.com",
          },
        },
      });
      const response = middleware.getScopeChallengeResponse(["admin"]);

      expect(response.headers["WWW-Authenticate"]).toContain('scope="admin"');
      const body = JSON.parse(response.body);
      expect(body.error.data.required_scopes).toEqual(["admin"]);
    });

    it("should handle multiple scopes", () => {
      const middleware = new AuthenticationMiddleware({
        oauth: {
          protectedResource: {
            resource: "https://example.com",
          },
        },
      });
      const response = middleware.getScopeChallengeResponse([
        "read",
        "write",
        "admin",
      ]);

      expect(response.headers["WWW-Authenticate"]).toContain(
        'scope="read write admin"',
      );
      const body = JSON.parse(response.body);
      expect(body.error.data.required_scopes).toEqual([
        "read",
        "write",
        "admin",
      ]);
    });

    it("should not include WWW-Authenticate header without OAuth config", () => {
      const middleware = new AuthenticationMiddleware({});
      const response = middleware.getScopeChallengeResponse(["admin"]);

      expect(response.headers["WWW-Authenticate"]).toBeUndefined();
      expect(response.headers["Content-Type"]).toBe("application/json");
    });

    it("should return proper JSON-RPC 2.0 format", () => {
      const middleware = new AuthenticationMiddleware({
        oauth: {
          protectedResource: {
            resource: "https://example.com",
          },
        },
      });
      const response = middleware.getScopeChallengeResponse(
        ["read"],
        "Description",
        "req-123",
      );

      const body = JSON.parse(response.body);
      expect(body.jsonrpc).toBe("2.0");
      expect(body.id).toBe("req-123");
      expect(body.error).toBeDefined();
      expect(body.error.code).toBe(-32001);
      expect(body.error.message).toBe("Description");
      expect(body.error.data).toBeDefined();
    });

    it("should include Content-Type header", () => {
      const middleware = new AuthenticationMiddleware({
        oauth: {
          protectedResource: {
            resource: "https://example.com",
          },
        },
      });
      const response = middleware.getScopeChallengeResponse(["read"]);

      expect(response.headers["Content-Type"]).toBe("application/json");
    });

    it("should handle empty scopes array", () => {
      const middleware = new AuthenticationMiddleware({
        oauth: {
          protectedResource: {
            resource: "https://example.com",
          },
        },
      });
      const response = middleware.getScopeChallengeResponse([]);

      expect(response.headers["WWW-Authenticate"]).toContain('scope=""');
      const body = JSON.parse(response.body);
      expect(body.error.data.required_scopes).toEqual([]);
    });
  });
});
