import { describe, it, expect, beforeEach, vi } from "vitest";
import { QualityGatesService, QualityGateResult } from "./quality-gates.service";
import { CoordinatorClientService, QualityCheckResponse } from "./coordinator-client.service";
import { ConfigService } from "@nestjs/config";

describe("QualityGatesService", () => {
  let service: QualityGatesService;
  let mockCoordinatorClient: CoordinatorClientService;
  let mockConfigService: ConfigService;

  beforeEach(() => {
    // Mock CoordinatorClientService
    mockCoordinatorClient = {
      checkQuality: vi.fn(),
      isHealthy: vi.fn(),
    } as unknown as CoordinatorClientService;

    // Mock ConfigService
    mockConfigService = {
      get: vi.fn((key: string) => {
        if (key === "orchestrator.yolo.enabled") {
          return false; // Default: YOLO disabled
        }
        return undefined;
      }),
    } as unknown as ConfigService;

    service = new QualityGatesService(mockCoordinatorClient, mockConfigService);
  });

  it("should be defined", () => {
    expect(service).toBeDefined();
  });

  describe("preCommitCheck", () => {
    const preCommitParams = {
      taskId: "task-123",
      agentId: "agent-456",
      files: ["src/test.ts", "src/test.spec.ts"],
      diffSummary: "Added new test file with unit tests",
    };

    it("should call coordinator with correct gate type for pre-commit", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: true,
        gate: "pre-commit",
        message: "Pre-commit checks passed",
        details: {
          typecheck: "passed",
          lint: "passed",
          tests: "passed",
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.preCommitCheck(preCommitParams);

      expect(mockCoordinatorClient.checkQuality).toHaveBeenCalledWith({
        taskId: preCommitParams.taskId,
        agentId: preCommitParams.agentId,
        files: preCommitParams.files,
        diffSummary: preCommitParams.diffSummary,
      });

      expect(result.approved).toBe(true);
      expect(result.gate).toBe("pre-commit");
    });

    it("should return approved result when all pre-commit gates pass", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: true,
        gate: "pre-commit",
        message: "All pre-commit checks passed",
        details: {
          typecheck: "passed",
          lint: "passed",
          tests: "passed",
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.preCommitCheck(preCommitParams);

      expect(result.approved).toBe(true);
      expect(result.message).toBe("All pre-commit checks passed");
      expect(result.details).toEqual({
        typecheck: "passed",
        lint: "passed",
        tests: "passed",
      });
    });

    it("should return rejected result when lint fails", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: false,
        gate: "pre-commit",
        message: "Linting failed",
        details: {
          errors: ["Unexpected any type at src/test.ts:10"],
          file: "src/test.ts",
          gate: "lint",
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.preCommitCheck(preCommitParams);

      expect(result.approved).toBe(false);
      expect(result.message).toBe("Linting failed");
      expect(result.details?.errors).toContain("Unexpected any type at src/test.ts:10");
    });

    it("should return rejected result when tests fail", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: false,
        gate: "pre-commit",
        message: "Tests failed",
        details: {
          gate: "tests",
          failed: 3,
          total: 10,
          errors: [
            "Expected true to be false at test.spec.ts:42",
            "TypeError: Cannot read property 'id' of undefined",
          ],
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.preCommitCheck(preCommitParams);

      expect(result.approved).toBe(false);
      expect(result.message).toBe("Tests failed");
      expect(result.details?.failed).toBe(3);
    });

    it("should return rejected result when typecheck fails", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: false,
        gate: "pre-commit",
        message: "Type checking failed",
        details: {
          gate: "typecheck",
          errors: ["src/test.ts:15:5 - error TS2339: Property 'foo' does not exist on type 'Bar'"],
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.preCommitCheck(preCommitParams);

      expect(result.approved).toBe(false);
      expect(result.message).toBe("Type checking failed");
      expect(result.details?.gate).toBe("typecheck");
    });

    it("should throw error when coordinator is unavailable", async () => {
      vi.mocked(mockCoordinatorClient.checkQuality).mockRejectedValueOnce(
        new Error("Coordinator quality check failed: 503 Service Unavailable")
      );

      await expect(service.preCommitCheck(preCommitParams)).rejects.toThrow(
        "Coordinator quality check failed: 503 Service Unavailable"
      );
    });

    it("should handle multiple file changes", async () => {
      const multiFileParams = {
        taskId: "task-789",
        agentId: "agent-012",
        files: ["src/feature.ts", "src/feature.spec.ts", "src/feature.module.ts", "README.md"],
        diffSummary: "Implemented new feature with tests and module registration",
      };

      const mockResponse: QualityCheckResponse = {
        approved: true,
        gate: "pre-commit",
        message: "All checks passed",
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.preCommitCheck(multiFileParams);

      expect(mockCoordinatorClient.checkQuality).toHaveBeenCalledWith({
        taskId: multiFileParams.taskId,
        agentId: multiFileParams.agentId,
        files: multiFileParams.files,
        diffSummary: multiFileParams.diffSummary,
      });

      expect(result.approved).toBe(true);
    });
  });

  describe("postCommitCheck", () => {
    const postCommitParams = {
      taskId: "task-123",
      agentId: "agent-456",
      files: ["src/test.ts", "src/test.spec.ts"],
      diffSummary: "Added new test file with unit tests",
    };

    it("should call coordinator with correct gate type for post-commit", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: true,
        gate: "post-commit",
        message: "Post-commit checks passed",
        details: {
          coverage: "passed",
          build: "passed",
          integration: "passed",
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.postCommitCheck(postCommitParams);

      expect(mockCoordinatorClient.checkQuality).toHaveBeenCalledWith({
        taskId: postCommitParams.taskId,
        agentId: postCommitParams.agentId,
        files: postCommitParams.files,
        diffSummary: postCommitParams.diffSummary,
      });

      expect(result.approved).toBe(true);
      expect(result.gate).toBe("post-commit");
    });

    it("should return approved result when all post-commit gates pass", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: true,
        gate: "post-commit",
        message: "All post-commit checks passed",
        details: {
          coverage: "passed (87%)",
          build: "passed",
          integration: "passed (10/10)",
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.postCommitCheck(postCommitParams);

      expect(result.approved).toBe(true);
      expect(result.message).toBe("All post-commit checks passed");
      expect(result.details?.coverage).toBe("passed (87%)");
    });

    it("should return rejected result when coverage is insufficient", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: false,
        gate: "post-commit",
        message: "Coverage below threshold",
        details: {
          gate: "coverage",
          current: 78.5,
          required: 85,
          uncoveredFiles: ["src/feature.ts"],
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.postCommitCheck(postCommitParams);

      expect(result.approved).toBe(false);
      expect(result.message).toBe("Coverage below threshold");
      expect(result.details?.current).toBe(78.5);
      expect(result.details?.required).toBe(85);
    });

    it("should return rejected result when build fails", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: false,
        gate: "post-commit",
        message: "Build failed",
        details: {
          gate: "build",
          errors: ["apps/orchestrator/src/test.ts:10:15 - error TS2304: Cannot find name 'foo'"],
          exitCode: 1,
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.postCommitCheck(postCommitParams);

      expect(result.approved).toBe(false);
      expect(result.message).toBe("Build failed");
      expect(result.details?.gate).toBe("build");
      expect(result.details?.exitCode).toBe(1);
    });

    it("should return rejected result when integration tests fail", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: false,
        gate: "post-commit",
        message: "Integration tests failed",
        details: {
          gate: "integration",
          failed: 2,
          total: 15,
          failures: ["API endpoint /api/test returns 500", "Database connection timeout"],
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.postCommitCheck(postCommitParams);

      expect(result.approved).toBe(false);
      expect(result.message).toBe("Integration tests failed");
      expect(result.details?.failed).toBe(2);
    });

    it("should throw error when coordinator is unavailable", async () => {
      vi.mocked(mockCoordinatorClient.checkQuality).mockRejectedValueOnce(
        new Error("Coordinator quality check failed: 503 Service Unavailable")
      );

      await expect(service.postCommitCheck(postCommitParams)).rejects.toThrow(
        "Coordinator quality check failed: 503 Service Unavailable"
      );
    });

    it("should include AI reviewer results in post-commit", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: true,
        gate: "post-commit",
        message: "All checks passed including AI review",
        details: {
          coverage: "passed (92%)",
          build: "passed",
          integration: "passed",
          aiReview: {
            confidence: 0.95,
            approved: true,
            findings: [],
          },
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.postCommitCheck(postCommitParams);

      expect(result.approved).toBe(true);
      expect(result.details?.aiReview).toBeDefined();
      expect((result.details?.aiReview as unknown as Record<string, unknown>).confidence).toBe(
        0.95
      );
    });
  });

  describe("50% rule enforcement (ORCH-116)", () => {
    const postCommitParams = {
      taskId: "task-999",
      agentId: "agent-888",
      files: ["src/feature.ts", "src/feature.spec.ts"],
      diffSummary: "Implemented new feature with comprehensive tests",
    };

    it("should approve when AI confirmation passes with mechanical gates", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: true,
        gate: "post-commit",
        message: "All gates passed: mechanical and AI confirmation",
        details: {
          typecheck: "passed",
          lint: "passed",
          tests: "passed",
          coverage: { current: 89, required: 85 },
          build: "passed",
          aiReview: {
            confidence: 0.92,
            approved: true,
            findings: [],
            aiGeneratedPercent: 45,
          },
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.postCommitCheck(postCommitParams);

      expect(result.approved).toBe(true);
      expect(result.details?.aiReview).toBeDefined();
      expect(
        (result.details?.aiReview as unknown as Record<string, unknown>).aiGeneratedPercent
      ).toBe(45);
      expect((result.details?.aiReview as unknown as Record<string, unknown>).confidence).toBe(
        0.92
      );
    });

    it("should reject when AI confidence is below threshold (< 0.9)", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: false,
        gate: "post-commit",
        message: "AI review confidence below threshold",
        details: {
          typecheck: "passed",
          lint: "passed",
          tests: "passed",
          coverage: { current: 87, required: 85 },
          aiReview: {
            confidence: 0.75,
            approved: false,
            findings: [
              "Code quality concerns detected",
              "Potential logic errors in error handling",
            ],
            aiGeneratedPercent: 48,
          },
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.postCommitCheck(postCommitParams);

      expect(result.approved).toBe(false);
      expect(result.message).toBe("AI review confidence below threshold");
      expect((result.details?.aiReview as unknown as Record<string, unknown>).confidence).toBe(
        0.75
      );
      expect((result.details?.aiReview as unknown as Record<string, unknown>).approved).toBe(false);
    });

    it("should reject when 50% rule violated (>50% AI-generated code)", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: false,
        gate: "post-commit",
        message: "50% rule violated: excessive AI-generated code detected",
        details: {
          typecheck: "passed",
          lint: "passed",
          tests: "passed",
          coverage: { current: 90, required: 85 },
          aiReview: {
            confidence: 0.88,
            approved: false,
            findings: [
              "Detected 65% AI-generated code in PR",
              "Exceeds 50% threshold for AI-generated content",
              "Requires more human review and modification",
            ],
            aiGeneratedPercent: 65,
          },
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.postCommitCheck(postCommitParams);

      expect(result.approved).toBe(false);
      expect(result.message).toContain("50% rule violated");
      expect(
        (result.details?.aiReview as unknown as Record<string, unknown>).aiGeneratedPercent
      ).toBe(65);
      expect((result.details?.aiReview as unknown as Record<string, unknown>).findings).toContain(
        "Detected 65% AI-generated code in PR"
      );
    });

    it("should reject when mechanical gates pass but AI review fails", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: false,
        gate: "post-commit",
        message: "AI review rejected changes",
        details: {
          typecheck: "passed",
          lint: "passed",
          tests: "passed",
          coverage: { current: 91, required: 85 },
          build: "passed",
          aiReview: {
            confidence: 0.65,
            approved: false,
            findings: [
              "Security vulnerability: SQL injection risk in query builder",
              "Logic error: race condition in concurrent operations",
              "Code quality: excessive complexity in main function",
            ],
            aiGeneratedPercent: 42,
          },
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.postCommitCheck(postCommitParams);

      expect(result.approved).toBe(false);
      expect(result.message).toBe("AI review rejected changes");
      expect(
        (result.details?.aiReview as unknown as Record<string, unknown>).findings
      ).toHaveLength(3);
      expect((result.details?.aiReview as unknown as Record<string, unknown>).findings).toContain(
        "Security vulnerability: SQL injection risk in query builder"
      );
    });

    it("should not run AI review when mechanical gates fail", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: false,
        gate: "post-commit",
        message: "Coverage below threshold",
        details: {
          typecheck: "passed",
          lint: "passed",
          tests: "passed",
          coverage: { current: 78, required: 85 },
          // No aiReview - short-circuited due to mechanical failure
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.postCommitCheck(postCommitParams);

      expect(result.approved).toBe(false);
      expect(result.details?.aiReview).toBeUndefined();
      expect(result.message).toBe("Coverage below threshold");
    });

    it("should handle AI review with detailed security findings", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: false,
        gate: "post-commit",
        message: "AI review identified security concerns",
        details: {
          typecheck: "passed",
          lint: "passed",
          tests: "passed",
          coverage: { current: 88, required: 85 },
          aiReview: {
            confidence: 0.82,
            approved: false,
            findings: [
              "XSS vulnerability: unsanitized user input in template",
              "Authentication bypass possible via token manipulation",
              "Sensitive data exposure in error messages",
            ],
            aiGeneratedPercent: 38,
            securityRisk: "high",
          },
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.postCommitCheck(postCommitParams);

      expect(result.approved).toBe(false);
      expect((result.details?.aiReview as unknown as Record<string, unknown>).securityRisk).toBe(
        "high"
      );
      expect((result.details?.aiReview as unknown as Record<string, unknown>).findings).toContain(
        "XSS vulnerability: unsanitized user input in template"
      );
    });

    it("should approve when at exactly 50% AI-generated code", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: true,
        gate: "post-commit",
        message: "All gates passed at 50% threshold",
        details: {
          typecheck: "passed",
          lint: "passed",
          tests: "passed",
          coverage: { current: 86, required: 85 },
          aiReview: {
            confidence: 0.91,
            approved: true,
            findings: [],
            aiGeneratedPercent: 50,
          },
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.postCommitCheck(postCommitParams);

      expect(result.approved).toBe(true);
      expect(
        (result.details?.aiReview as unknown as Record<string, unknown>).aiGeneratedPercent
      ).toBe(50);
      expect((result.details?.aiReview as unknown as Record<string, unknown>).approved).toBe(true);
    });

    it("should handle AI review unavailable (coordinator fallback)", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: true,
        gate: "post-commit",
        message: "Mechanical gates passed (AI review unavailable)",
        details: {
          typecheck: "passed",
          lint: "passed",
          tests: "passed",
          coverage: { current: 92, required: 85 },
          aiReview: {
            confidence: 0,
            approved: false,
            findings: ["AI reviewer service unavailable"],
            error: "AI service timeout",
          },
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.postCommitCheck(postCommitParams);

      // Coordinator decides approval based on its policy
      expect(result.approved).toBe(true);
      expect((result.details?.aiReview as unknown as Record<string, unknown>).error).toBe(
        "AI service timeout"
      );
    });

    it("should preserve all AI review details for debugging", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: false,
        gate: "post-commit",
        message: "AI review rejected: multiple concerns",
        details: {
          typecheck: "passed",
          lint: "passed",
          tests: "passed",
          coverage: { current: 87, required: 85 },
          aiReview: {
            confidence: 0.72,
            approved: false,
            findings: ["Issue 1", "Issue 2", "Issue 3"],
            aiGeneratedPercent: 55,
            reviewerId: "ai-reviewer-42",
            reviewTimestamp: "2026-02-02T12:00:00Z",
            modelVersion: "claude-opus-4-5",
          },
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.postCommitCheck(postCommitParams);

      expect(result.approved).toBe(false);
      const aiReview = result.details?.aiReview as unknown as Record<string, unknown>;
      expect(aiReview.reviewerId).toBe("ai-reviewer-42");
      expect(aiReview.reviewTimestamp).toBe("2026-02-02T12:00:00Z");
      expect(aiReview.modelVersion).toBe("claude-opus-4-5");
    });
  });

  describe("error handling", () => {
    const params = {
      taskId: "task-123",
      agentId: "agent-456",
      files: ["src/test.ts"],
      diffSummary: "Test change",
    };

    it("should propagate coordinator client errors", async () => {
      const error = new Error("Invalid coordinator response");
      vi.mocked(mockCoordinatorClient.checkQuality).mockRejectedValueOnce(error);

      await expect(service.preCommitCheck(params)).rejects.toThrow("Invalid coordinator response");
    });

    it("should handle network errors", async () => {
      const error = new Error("ECONNREFUSED");
      vi.mocked(mockCoordinatorClient.checkQuality).mockRejectedValueOnce(error);

      await expect(service.postCommitCheck(params)).rejects.toThrow("ECONNREFUSED");
    });

    it("should handle timeout errors", async () => {
      const error = new Error("Request timeout after 30000ms");
      vi.mocked(mockCoordinatorClient.checkQuality).mockRejectedValueOnce(error);

      await expect(service.preCommitCheck(params)).rejects.toThrow("Request timeout");
    });

    it("should handle non-Error exceptions in pre-commit", async () => {
      vi.mocked(mockCoordinatorClient.checkQuality).mockRejectedValueOnce("String error message");

      await expect(service.preCommitCheck(params)).rejects.toThrow("String error message");
    });

    it("should handle non-Error exceptions in post-commit", async () => {
      vi.mocked(mockCoordinatorClient.checkQuality).mockRejectedValueOnce("String error message");

      await expect(service.postCommitCheck(params)).rejects.toThrow("String error message");
    });
  });

  describe("response parsing", () => {
    const params = {
      taskId: "task-123",
      agentId: "agent-456",
      files: ["src/test.ts"],
      diffSummary: "Test change",
    };

    it("should handle response with minimal fields", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: true,
        gate: "pre-commit",
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.preCommitCheck(params);

      expect(result.approved).toBe(true);
      expect(result.gate).toBe("pre-commit");
      expect(result.message).toBeUndefined();
      expect(result.details).toBeUndefined();
    });

    it("should preserve all response fields", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: false,
        gate: "post-commit",
        message: "Multiple gates failed",
        details: {
          coverage: { current: 70, required: 85 },
          build: { status: "failed", errors: ["error 1"] },
          timestamp: "2026-02-02T10:00:00Z",
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.postCommitCheck(params);

      expect(result).toEqual(mockResponse);
      expect(result.details?.coverage).toEqual({ current: 70, required: 85 });
      expect(result.details?.timestamp).toBe("2026-02-02T10:00:00Z");
    });
  });

  describe("hasAIConfirmation helper", () => {
    it("should return true when AI review is present", () => {
      const result: QualityGateResult = {
        approved: true,
        gate: "post-commit",
        details: {
          aiReview: {
            confidence: 0.92,
            approved: true,
          },
        },
      };

      expect(service.hasAIConfirmation(result)).toBe(true);
    });

    it("should return false when AI review is missing", () => {
      const result: QualityGateResult = {
        approved: true,
        gate: "post-commit",
        details: {
          typecheck: "passed",
          lint: "passed",
        },
      };

      expect(service.hasAIConfirmation(result)).toBe(false);
    });

    it("should return false when details is undefined", () => {
      const result: QualityGateResult = {
        approved: true,
        gate: "pre-commit",
      };

      expect(service.hasAIConfirmation(result)).toBe(false);
    });

    it("should return false when aiReview is not an object", () => {
      const result: QualityGateResult = {
        approved: true,
        gate: "post-commit",
        details: {
          aiReview: "not an object" as unknown as Record<string, unknown>,
        },
      };

      expect(service.hasAIConfirmation(result)).toBe(false);
    });

    it("should return true for AI review with 50% rule details", () => {
      const result: QualityGateResult = {
        approved: false,
        gate: "post-commit",
        details: {
          aiReview: {
            confidence: 0.75,
            approved: false,
            aiGeneratedPercent: 65,
            findings: ["Exceeds 50% threshold"],
          },
        },
      };

      expect(service.hasAIConfirmation(result)).toBe(true);
    });
  });

  describe("gate configuration per-task (ORCH-124)", () => {
    const params = {
      taskId: "task-config-123",
      agentId: "agent-config-456",
      files: ["src/feature.ts"],
      diffSummary: "Implemented feature",
    };

    it("should use task-specific gate configuration", async () => {
      const gateConfig = {
        gates: {
          lint: true,
          tests: true,
          coverage: { enabled: true, threshold: 90 },
        },
        metadata: {
          profile: "custom" as const,
          agentType: "worker",
        },
      };

      const mockResponse: QualityCheckResponse = {
        approved: true,
        gate: "pre-commit",
        message: "Custom gates passed",
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.preCommitCheck(params, gateConfig);

      expect(mockCoordinatorClient.checkQuality).toHaveBeenCalledWith({
        taskId: params.taskId,
        agentId: params.agentId,
        files: params.files,
        diffSummary: params.diffSummary,
        gateRequirements: gateConfig,
      });

      expect(result.approved).toBe(true);
    });

    it("should skip gates not in configuration", async () => {
      const gateConfig = {
        gates: {
          tests: true, // Only tests enabled
        },
        metadata: {
          profile: "minimal" as const,
          agentType: "tester",
        },
      };

      const mockResponse: QualityCheckResponse = {
        approved: true,
        gate: "pre-commit",
        message: "Tests passed",
        details: {
          tests: "passed (10/10)",
          // No typecheck, lint, coverage results
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.preCommitCheck(params, gateConfig);

      expect(result.approved).toBe(true);
      expect(result.details?.tests).toBeDefined();
      expect(result.details?.typecheck).toBeUndefined();
      expect(result.details?.lint).toBeUndefined();
    });

    it("should apply custom coverage threshold from gate config", async () => {
      const gateConfig = {
        gates: {
          tests: true,
          coverage: { enabled: true, threshold: 95 }, // Higher threshold
        },
        metadata: {
          profile: "custom" as const,
          agentType: "worker",
        },
      };

      const mockResponse: QualityCheckResponse = {
        approved: true,
        gate: "post-commit",
        message: "Coverage meets custom threshold",
        details: {
          coverage: { current: 96, required: 95 },
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.postCommitCheck(params, gateConfig);

      expect(result.approved).toBe(true);
      expect(result.details?.coverage).toEqual({ current: 96, required: 95 });
    });

    it("should reject when custom threshold not met", async () => {
      const gateConfig = {
        gates: {
          coverage: { enabled: true, threshold: 95 },
        },
        metadata: {
          profile: "custom" as const,
          agentType: "worker",
        },
      };

      const mockResponse: QualityCheckResponse = {
        approved: false,
        gate: "post-commit",
        message: "Coverage below custom threshold",
        details: {
          coverage: { current: 92, required: 95 },
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.postCommitCheck(params, gateConfig);

      expect(result.approved).toBe(false);
      expect(result.details?.coverage).toEqual({ current: 92, required: 95 });
    });

    it("should use strict profile for reviewer agents", async () => {
      const gateConfig = {
        gates: {
          typecheck: true,
          lint: true,
          tests: true,
          coverage: { enabled: true, threshold: 85 },
          build: true,
          integration: true,
          aiReview: true,
        },
        metadata: {
          profile: "strict" as const,
          agentType: "reviewer",
        },
      };

      const mockResponse: QualityCheckResponse = {
        approved: true,
        gate: "post-commit",
        message: "All strict gates passed",
        details: {
          typecheck: "passed",
          lint: "passed",
          tests: "passed",
          coverage: { current: 88, required: 85 },
          build: "passed",
          integration: "passed",
          aiReview: { confidence: 0.92, approved: true },
        },
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      const result = await service.postCommitCheck(params, gateConfig);

      expect(result.approved).toBe(true);
      expect(result.details?.aiReview).toBeDefined();
    });

    it("should work without gate config (backward compatibility)", async () => {
      const mockResponse: QualityCheckResponse = {
        approved: true,
        gate: "pre-commit",
        message: "Default gates passed",
      };

      vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

      // Call without gateConfig parameter
      const result = await service.preCommitCheck(params);

      expect(mockCoordinatorClient.checkQuality).toHaveBeenCalledWith({
        taskId: params.taskId,
        agentId: params.agentId,
        files: params.files,
        diffSummary: params.diffSummary,
        // No gateRequirements passed
      });

      expect(result.approved).toBe(true);
    });

    it("should override gate config when YOLO mode enabled", async () => {
      // Enable YOLO mode
      vi.mocked(mockConfigService.get).mockImplementation((key: string) => {
        if (key === "orchestrator.yolo.enabled") {
          return true;
        }
        return undefined;
      });

      const gateConfig = {
        gates: {
          typecheck: true,
          lint: true,
          tests: true,
        },
        metadata: {
          profile: "standard" as const,
          agentType: "worker",
        },
      };

      // YOLO mode should bypass gates even with config
      const result = await service.preCommitCheck(params, gateConfig);

      // Should NOT call coordinator
      expect(mockCoordinatorClient.checkQuality).not.toHaveBeenCalled();

      // Should return YOLO mode result
      expect(result.approved).toBe(true);
      expect(result.message).toBe("Quality gates disabled (YOLO mode)");
      expect(result.details?.yoloMode).toBe(true);
    });
  });

  describe("YOLO mode (ORCH-123)", () => {
    const preCommitParams = {
      taskId: "task-yolo-123",
      agentId: "agent-yolo-456",
      files: ["src/feature.ts"],
      diffSummary: "Quick feature implementation",
    };

    const postCommitParams = {
      taskId: "task-yolo-789",
      agentId: "agent-yolo-012",
      files: ["src/feature.ts", "src/feature.spec.ts"],
      diffSummary: "Feature with tests",
    };

    describe("YOLO mode enabled", () => {
      beforeEach(() => {
        // Enable YOLO mode
        vi.mocked(mockConfigService.get).mockImplementation((key: string) => {
          if (key === "orchestrator.yolo.enabled") {
            return true;
          }
          return undefined;
        });
      });

      it("should skip quality gates on pre-commit check", async () => {
        const result = await service.preCommitCheck(preCommitParams);

        // Should NOT call coordinator
        expect(mockCoordinatorClient.checkQuality).not.toHaveBeenCalled();

        // Should return approved result with warning
        expect(result.approved).toBe(true);
        expect(result.gate).toBe("pre-commit");
        expect(result.message).toBe("Quality gates disabled (YOLO mode)");
        expect(result.details?.yoloMode).toBe(true);
      });

      it("should skip quality gates on post-commit check", async () => {
        const result = await service.postCommitCheck(postCommitParams);

        // Should NOT call coordinator
        expect(mockCoordinatorClient.checkQuality).not.toHaveBeenCalled();

        // Should return approved result with warning
        expect(result.approved).toBe(true);
        expect(result.gate).toBe("post-commit");
        expect(result.message).toBe("Quality gates disabled (YOLO mode)");
        expect(result.details?.yoloMode).toBe(true);
      });

      it("should log YOLO mode usage for pre-commit", async () => {
        const loggerSpy = vi.spyOn(service["logger"], "warn");

        await service.preCommitCheck(preCommitParams);

        // Should log warning with audit trail
        expect(loggerSpy).toHaveBeenCalledWith(
          expect.stringContaining("YOLO mode enabled"),
          expect.objectContaining({
            taskId: preCommitParams.taskId,
            agentId: preCommitParams.agentId,
            gate: "pre-commit",
          })
        );
      });

      it("should log YOLO mode usage for post-commit", async () => {
        const loggerSpy = vi.spyOn(service["logger"], "warn");

        await service.postCommitCheck(postCommitParams);

        // Should log warning with audit trail
        expect(loggerSpy).toHaveBeenCalledWith(
          expect.stringContaining("YOLO mode enabled"),
          expect.objectContaining({
            taskId: postCommitParams.taskId,
            agentId: postCommitParams.agentId,
            gate: "post-commit",
          })
        );
      });

      it("should include warning details in response", async () => {
        const result = await service.preCommitCheck(preCommitParams);

        expect(result.details).toEqual({
          yoloMode: true,
          warning: "Quality gates were bypassed. Code may not meet quality standards.",
        });
      });
    });

    describe("YOLO mode disabled", () => {
      beforeEach(() => {
        // Disable YOLO mode (default)
        vi.mocked(mockConfigService.get).mockImplementation((key: string) => {
          if (key === "orchestrator.yolo.enabled") {
            return false;
          }
          return undefined;
        });
      });

      it("should run quality gates normally on pre-commit", async () => {
        const mockResponse: QualityCheckResponse = {
          approved: true,
          gate: "pre-commit",
          message: "All checks passed",
        };

        vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

        const result = await service.preCommitCheck(preCommitParams);

        // Should call coordinator
        expect(mockCoordinatorClient.checkQuality).toHaveBeenCalledWith({
          taskId: preCommitParams.taskId,
          agentId: preCommitParams.agentId,
          files: preCommitParams.files,
          diffSummary: preCommitParams.diffSummary,
        });

        // Should return coordinator response
        expect(result.approved).toBe(true);
        expect(result.gate).toBe("pre-commit");
        expect(result.message).toBe("All checks passed");
      });

      it("should run quality gates normally on post-commit", async () => {
        const mockResponse: QualityCheckResponse = {
          approved: true,
          gate: "post-commit",
          message: "All checks passed",
        };

        vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

        const result = await service.postCommitCheck(postCommitParams);

        // Should call coordinator
        expect(mockCoordinatorClient.checkQuality).toHaveBeenCalledWith({
          taskId: postCommitParams.taskId,
          agentId: postCommitParams.agentId,
          files: postCommitParams.files,
          diffSummary: postCommitParams.diffSummary,
        });

        // Should return coordinator response
        expect(result.approved).toBe(true);
        expect(result.gate).toBe("post-commit");
        expect(result.message).toBe("All checks passed");
      });

      it("should NOT log YOLO mode when disabled", async () => {
        const loggerWarnSpy = vi.spyOn(service["logger"], "warn");

        const mockResponse: QualityCheckResponse = {
          approved: true,
          gate: "pre-commit",
        };

        vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

        await service.preCommitCheck(preCommitParams);

        // Should NOT log YOLO mode warning
        expect(loggerWarnSpy).not.toHaveBeenCalledWith(
          expect.stringContaining("YOLO mode"),
          expect.anything()
        );
      });
    });

    describe("YOLO mode not configured (default)", () => {
      beforeEach(() => {
        // YOLO mode not set - should default to false
        vi.mocked(mockConfigService.get).mockImplementation((key: string) => {
          if (key === "orchestrator.yolo.enabled") {
            return undefined;
          }
          return undefined;
        });
      });

      it("should default to gates enabled when YOLO_MODE not set", async () => {
        const mockResponse: QualityCheckResponse = {
          approved: true,
          gate: "pre-commit",
        };

        vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

        const result = await service.preCommitCheck(preCommitParams);

        // Should call coordinator (gates enabled by default)
        expect(mockCoordinatorClient.checkQuality).toHaveBeenCalled();
        expect(result.approved).toBe(true);
      });
    });

    describe("audit trail", () => {
      beforeEach(() => {
        // Enable YOLO mode
        vi.mocked(mockConfigService.get).mockImplementation((key: string) => {
          if (key === "orchestrator.yolo.enabled") {
            return true;
          }
          return undefined;
        });
      });

      it("should log complete audit trail for pre-commit", async () => {
        const loggerSpy = vi.spyOn(service["logger"], "warn");

        await service.preCommitCheck(preCommitParams);

        expect(loggerSpy).toHaveBeenCalledWith("YOLO mode enabled: skipping quality gates", {
          taskId: preCommitParams.taskId,
          agentId: preCommitParams.agentId,
          gate: "pre-commit",
          files: preCommitParams.files,
          timestamp: expect.any(String),
        });
      });

      it("should log complete audit trail for post-commit", async () => {
        const loggerSpy = vi.spyOn(service["logger"], "warn");

        await service.postCommitCheck(postCommitParams);

        expect(loggerSpy).toHaveBeenCalledWith("YOLO mode enabled: skipping quality gates", {
          taskId: postCommitParams.taskId,
          agentId: postCommitParams.agentId,
          gate: "post-commit",
          files: postCommitParams.files,
          timestamp: expect.any(String),
        });
      });
    });

    describe("YOLO mode blocked in production (SEC-ORCH-13)", () => {
      const params = {
        taskId: "task-prod-123",
        agentId: "agent-prod-456",
        files: ["src/feature.ts"],
        diffSummary: "Production deployment",
      };

      it("should block YOLO mode when NODE_ENV is production", async () => {
        // Enable YOLO mode but set production environment
        vi.mocked(mockConfigService.get).mockImplementation((key: string) => {
          if (key === "orchestrator.yolo.enabled") {
            return true;
          }
          if (key === "NODE_ENV") {
            return "production";
          }
          return undefined;
        });

        const mockResponse: QualityCheckResponse = {
          approved: true,
          gate: "pre-commit",
          message: "All checks passed",
        };

        vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

        const result = await service.preCommitCheck(params);

        // Should call coordinator (YOLO mode blocked in production)
        expect(mockCoordinatorClient.checkQuality).toHaveBeenCalled();

        // Should return coordinator response, not YOLO bypass
        expect(result.approved).toBe(true);
        expect(result.message).toBe("All checks passed");
        expect(result.details?.yoloMode).toBeUndefined();
      });

      it("should log warning when YOLO mode is blocked in production", async () => {
        // Enable YOLO mode but set production environment
        vi.mocked(mockConfigService.get).mockImplementation((key: string) => {
          if (key === "orchestrator.yolo.enabled") {
            return true;
          }
          if (key === "NODE_ENV") {
            return "production";
          }
          return undefined;
        });

        const loggerWarnSpy = vi.spyOn(service["logger"], "warn");

        const mockResponse: QualityCheckResponse = {
          approved: true,
          gate: "pre-commit",
        };

        vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

        await service.preCommitCheck(params);

        // Should log warning about YOLO mode being blocked
        expect(loggerWarnSpy).toHaveBeenCalledWith(
          "YOLO mode blocked in production environment - quality gates will be enforced",
          expect.objectContaining({
            requestedYoloMode: true,
            environment: "production",
          })
        );
      });

      it("should allow YOLO mode in development environment", async () => {
        // Enable YOLO mode with development environment
        vi.mocked(mockConfigService.get).mockImplementation((key: string) => {
          if (key === "orchestrator.yolo.enabled") {
            return true;
          }
          if (key === "NODE_ENV") {
            return "development";
          }
          return undefined;
        });

        const result = await service.preCommitCheck(params);

        // Should NOT call coordinator (YOLO mode enabled)
        expect(mockCoordinatorClient.checkQuality).not.toHaveBeenCalled();

        // Should return YOLO bypass result
        expect(result.approved).toBe(true);
        expect(result.message).toBe("Quality gates disabled (YOLO mode)");
        expect(result.details?.yoloMode).toBe(true);
      });

      it("should allow YOLO mode in test environment", async () => {
        // Enable YOLO mode with test environment
        vi.mocked(mockConfigService.get).mockImplementation((key: string) => {
          if (key === "orchestrator.yolo.enabled") {
            return true;
          }
          if (key === "NODE_ENV") {
            return "test";
          }
          return undefined;
        });

        const result = await service.postCommitCheck(params);

        // Should NOT call coordinator (YOLO mode enabled)
        expect(mockCoordinatorClient.checkQuality).not.toHaveBeenCalled();

        // Should return YOLO bypass result
        expect(result.approved).toBe(true);
        expect(result.message).toBe("Quality gates disabled (YOLO mode)");
        expect(result.details?.yoloMode).toBe(true);
      });

      it("should block YOLO mode for post-commit in production", async () => {
        // Enable YOLO mode but set production environment
        vi.mocked(mockConfigService.get).mockImplementation((key: string) => {
          if (key === "orchestrator.yolo.enabled") {
            return true;
          }
          if (key === "NODE_ENV") {
            return "production";
          }
          return undefined;
        });

        const mockResponse: QualityCheckResponse = {
          approved: false,
          gate: "post-commit",
          message: "Coverage below threshold",
          details: {
            coverage: { current: 78, required: 85 },
          },
        };

        vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

        const result = await service.postCommitCheck(params);

        // Should call coordinator and enforce quality gates
        expect(mockCoordinatorClient.checkQuality).toHaveBeenCalled();

        // Should return coordinator rejection, not YOLO bypass
        expect(result.approved).toBe(false);
        expect(result.message).toBe("Coverage below threshold");
        expect(result.details?.coverage).toEqual({ current: 78, required: 85 });
      });

      it("should work when NODE_ENV is not set (default to non-production)", async () => {
        // Enable YOLO mode without NODE_ENV set
        vi.mocked(mockConfigService.get).mockImplementation((key: string) => {
          if (key === "orchestrator.yolo.enabled") {
            return true;
          }
          if (key === "NODE_ENV") {
            return undefined;
          }
          return undefined;
        });

        // Also clear process.env.NODE_ENV
        const originalNodeEnv = process.env.NODE_ENV;
        delete process.env.NODE_ENV;

        try {
          const result = await service.preCommitCheck(params);

          // Should allow YOLO mode when NODE_ENV not set
          expect(mockCoordinatorClient.checkQuality).not.toHaveBeenCalled();
          expect(result.approved).toBe(true);
          expect(result.details?.yoloMode).toBe(true);
        } finally {
          // Restore NODE_ENV
          process.env.NODE_ENV = originalNodeEnv;
        }
      });

      it("should fall back to process.env.NODE_ENV when config not set", async () => {
        // Enable YOLO mode, config returns undefined but process.env is production
        vi.mocked(mockConfigService.get).mockImplementation((key: string) => {
          if (key === "orchestrator.yolo.enabled") {
            return true;
          }
          if (key === "NODE_ENV") {
            return undefined;
          }
          return undefined;
        });

        // Set process.env.NODE_ENV to production
        const originalNodeEnv = process.env.NODE_ENV;
        process.env.NODE_ENV = "production";

        try {
          const mockResponse: QualityCheckResponse = {
            approved: true,
            gate: "pre-commit",
          };

          vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse);

          const result = await service.preCommitCheck(params);

          // Should block YOLO mode (production via process.env)
          expect(mockCoordinatorClient.checkQuality).toHaveBeenCalled();
          expect(result.details?.yoloMode).toBeUndefined();
        } finally {
          // Restore NODE_ENV
          process.env.NODE_ENV = originalNodeEnv;
        }
      });
    });
  });
});
