import { describe, it, expect, beforeEach, vi } from "vitest";
import { Test, TestingModule } from "@nestjs/testing";
import { UnauthorizedException } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { StitcherController } from "./stitcher.controller";
import { StitcherService } from "./stitcher.service";
import { ApiKeyGuard } from "../common/guards/api-key.guard";

/**
 * Security tests for StitcherController
 *
 * These tests verify that all stitcher endpoints require authentication
 * and reject requests without valid API keys.
 */
describe("StitcherController - Security", () => {
  let controller: StitcherController;
  let guard: ApiKeyGuard;

  const mockService = {
    handleWebhook: vi.fn(),
    dispatchJob: vi.fn(),
  };

  const mockConfigService = {
    get: vi.fn().mockReturnValue("test-api-key-12345"),
  };

  beforeEach(async () => {
    vi.clearAllMocks();

    const module: TestingModule = await Test.createTestingModule({
      controllers: [StitcherController],
      providers: [
        { provide: StitcherService, useValue: mockService },
        { provide: ConfigService, useValue: mockConfigService },
        ApiKeyGuard,
      ],
    }).compile();

    controller = module.get<StitcherController>(StitcherController);
    guard = module.get<ApiKeyGuard>(ApiKeyGuard);
  });

  describe("Authentication Requirements", () => {
    it("should have ApiKeyGuard applied to controller", () => {
      const guards = Reflect.getMetadata("__guards__", StitcherController);
      expect(guards).toBeDefined();
      expect(guards).toContain(ApiKeyGuard);
    });

    it("POST /stitcher/webhook should require authentication", () => {
      const mockContext = {
        switchToHttp: () => ({
          getRequest: () => ({ headers: {} }),
        }),
      };

      expect(() => guard.canActivate(mockContext as never)).toThrow(UnauthorizedException);
    });

    it("POST /stitcher/dispatch should require authentication", () => {
      const mockContext = {
        switchToHttp: () => ({
          getRequest: () => ({ headers: {} }),
        }),
      };

      expect(() => guard.canActivate(mockContext as never)).toThrow(UnauthorizedException);
    });
  });

  describe("Valid Authentication", () => {
    it("should allow requests with valid API key", () => {
      const mockContext = {
        switchToHttp: () => ({
          getRequest: () => ({
            headers: { "x-api-key": "test-api-key-12345" },
          }),
        }),
      };

      const result = guard.canActivate(mockContext as never);
      expect(result).toBe(true);
    });

    it("should reject requests with invalid API key", () => {
      const mockContext = {
        switchToHttp: () => ({
          getRequest: () => ({
            headers: { "x-api-key": "wrong-api-key" },
          }),
        }),
      };

      expect(() => guard.canActivate(mockContext as never)).toThrow(UnauthorizedException);
      expect(() => guard.canActivate(mockContext as never)).toThrow("Invalid API key");
    });

    it("should reject requests with empty API key", () => {
      const mockContext = {
        switchToHttp: () => ({
          getRequest: () => ({
            headers: { "x-api-key": "" },
          }),
        }),
      };

      expect(() => guard.canActivate(mockContext as never)).toThrow(UnauthorizedException);
      expect(() => guard.canActivate(mockContext as never)).toThrow("No API key provided");
    });
  });

  describe("Webhook Security", () => {
    it("should prevent unauthorized webhook submissions", () => {
      const mockContext = {
        switchToHttp: () => ({
          getRequest: () => ({
            headers: {},
            body: {
              issueNumber: "42",
              repository: "malicious/repo",
              action: "assigned",
            },
          }),
        }),
      };

      expect(() => guard.canActivate(mockContext as never)).toThrow(UnauthorizedException);
    });
  });
});
