import { describe, it, expect, beforeEach } from "vitest";
import { GateConfigService } from "./gate-config.service";
import { GateProfileType } from "./types/gate-config.types";

describe("GateConfigService", () => {
  let service: GateConfigService;

  beforeEach(() => {
    service = new GateConfigService();
  });

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

  describe("getDefaultProfile", () => {
    it("should return strict profile for reviewer agents", () => {
      const profile = service.getDefaultProfile("reviewer");

      expect(profile.name).toBe("strict");
      expect(profile.gates.typecheck).toBe(true);
      expect(profile.gates.lint).toBe(true);
      expect(profile.gates.tests).toBe(true);
      expect(profile.gates.coverage?.enabled).toBe(true);
      expect(profile.gates.coverage?.threshold).toBe(85);
      expect(profile.gates.build).toBe(true);
      expect(profile.gates.integration).toBe(true);
      expect(profile.gates.aiReview).toBe(true);
    });

    it("should return standard profile for worker agents", () => {
      const profile = service.getDefaultProfile("worker");

      expect(profile.name).toBe("standard");
      expect(profile.gates.typecheck).toBe(true);
      expect(profile.gates.lint).toBe(true);
      expect(profile.gates.tests).toBe(true);
      expect(profile.gates.coverage?.enabled).toBe(true);
      expect(profile.gates.coverage?.threshold).toBe(85);
      expect(profile.gates.build).toBeUndefined();
      expect(profile.gates.integration).toBeUndefined();
      expect(profile.gates.aiReview).toBeUndefined();
    });

    it("should return minimal profile for tester agents", () => {
      const profile = service.getDefaultProfile("tester");

      expect(profile.name).toBe("minimal");
      expect(profile.gates.tests).toBe(true);
      expect(profile.gates.typecheck).toBeUndefined();
      expect(profile.gates.lint).toBeUndefined();
      expect(profile.gates.coverage).toBeUndefined();
      expect(profile.gates.build).toBeUndefined();
      expect(profile.gates.integration).toBeUndefined();
      expect(profile.gates.aiReview).toBeUndefined();
    });
  });

  describe("getProfileByName", () => {
    it("should return strict profile", () => {
      const profile = service.getProfileByName("strict");

      expect(profile.name).toBe("strict");
      expect(profile.gates.typecheck).toBe(true);
      expect(profile.gates.lint).toBe(true);
      expect(profile.gates.tests).toBe(true);
      expect(profile.gates.coverage?.enabled).toBe(true);
      expect(profile.gates.build).toBe(true);
      expect(profile.gates.integration).toBe(true);
      expect(profile.gates.aiReview).toBe(true);
    });

    it("should return standard profile", () => {
      const profile = service.getProfileByName("standard");

      expect(profile.name).toBe("standard");
      expect(profile.gates.typecheck).toBe(true);
      expect(profile.gates.lint).toBe(true);
      expect(profile.gates.tests).toBe(true);
      expect(profile.gates.coverage?.enabled).toBe(true);
      expect(profile.gates.build).toBeUndefined();
      expect(profile.gates.integration).toBeUndefined();
      expect(profile.gates.aiReview).toBeUndefined();
    });

    it("should return minimal profile", () => {
      const profile = service.getProfileByName("minimal");

      expect(profile.name).toBe("minimal");
      expect(profile.gates.tests).toBe(true);
      expect(profile.gates.typecheck).toBeUndefined();
      expect(profile.gates.lint).toBeUndefined();
    });

    it("should return custom profile with empty gates", () => {
      const profile = service.getProfileByName("custom");

      expect(profile.name).toBe("custom");
      expect(profile.gates).toEqual({});
    });

    it("should throw error for invalid profile name", () => {
      expect(() => service.getProfileByName("invalid" as GateProfileType)).toThrow(
        "Invalid profile name: invalid"
      );
    });
  });

  describe("createTaskConfig", () => {
    it("should create task config with default profile for agent type", () => {
      const config = service.createTaskConfig("task-123", "worker");

      expect(config.taskId).toBe("task-123");
      expect(config.agentType).toBe("worker");
      expect(config.profile.name).toBe("standard");
      expect(config.profile.gates.typecheck).toBe(true);
      expect(config.profile.gates.lint).toBe(true);
      expect(config.profile.gates.tests).toBe(true);
    });

    it("should create task config with specified profile", () => {
      const config = service.createTaskConfig("task-456", "worker", "minimal");

      expect(config.taskId).toBe("task-456");
      expect(config.agentType).toBe("worker");
      expect(config.profile.name).toBe("minimal");
      expect(config.profile.gates.tests).toBe(true);
      expect(config.profile.gates.typecheck).toBeUndefined();
    });

    it("should create task config with custom gates", () => {
      const customGates = {
        lint: true,
        tests: true,
        coverage: { enabled: true, threshold: 90 },
      };

      const config = service.createTaskConfig("task-789", "worker", "custom", customGates);

      expect(config.taskId).toBe("task-789");
      expect(config.profile.name).toBe("custom");
      expect(config.profile.gates).toEqual(customGates);
    });

    it("should throw error when custom profile specified without gates", () => {
      expect(() => service.createTaskConfig("task-999", "worker", "custom")).toThrow(
        "Custom profile requires gate selection"
      );
    });

    it("should ignore custom gates when using predefined profile", () => {
      const customGates = {
        lint: true,
      };

      const config = service.createTaskConfig("task-111", "worker", "strict", customGates);

      expect(config.profile.name).toBe("strict");
      // Should use strict profile gates, not custom gates
      expect(config.profile.gates.typecheck).toBe(true);
      expect(config.profile.gates.build).toBe(true);
    });
  });

  describe("getGateRequirements", () => {
    it("should extract gate requirements from task config", () => {
      const config = service.createTaskConfig("task-123", "worker", "standard");

      const requirements = service.getGateRequirements(config);

      expect(requirements.gates).toEqual(config.profile.gates);
      expect(requirements.metadata?.profile).toBe("standard");
      expect(requirements.metadata?.agentType).toBe("worker");
    });

    it("should extract custom gate requirements", () => {
      const customGates = {
        lint: true,
        tests: true,
        coverage: { enabled: true, threshold: 70 },
      };

      const config = service.createTaskConfig("task-456", "tester", "custom", customGates);

      const requirements = service.getGateRequirements(config);

      expect(requirements.gates).toEqual(customGates);
      expect(requirements.metadata?.profile).toBe("custom");
      expect(requirements.metadata?.agentType).toBe("tester");
    });
  });

  describe("validateGateSelection", () => {
    it("should accept valid gate selection", () => {
      const gates = {
        typecheck: true,
        lint: true,
        tests: true,
        coverage: { enabled: true, threshold: 85 },
      };

      expect(() => service.validateGateSelection(gates)).not.toThrow();
    });

    it("should accept minimal gate selection", () => {
      const gates = {
        tests: true,
      };

      expect(() => service.validateGateSelection(gates)).not.toThrow();
    });

    it("should accept coverage with threshold", () => {
      const gates = {
        coverage: { enabled: true, threshold: 90 },
      };

      expect(() => service.validateGateSelection(gates)).not.toThrow();
    });

    it("should accept coverage without threshold (uses default)", () => {
      const gates = {
        coverage: { enabled: true },
      };

      expect(() => service.validateGateSelection(gates)).not.toThrow();
    });

    it("should reject invalid coverage threshold (< 0)", () => {
      const gates = {
        coverage: { enabled: true, threshold: -10 },
      };

      expect(() => service.validateGateSelection(gates)).toThrow(
        "Coverage threshold must be between 0 and 100"
      );
    });

    it("should reject invalid coverage threshold (> 100)", () => {
      const gates = {
        coverage: { enabled: true, threshold: 150 },
      };

      expect(() => service.validateGateSelection(gates)).toThrow(
        "Coverage threshold must be between 0 and 100"
      );
    });

    it("should reject empty gate selection", () => {
      const gates = {};

      expect(() => service.validateGateSelection(gates)).toThrow(
        "At least one gate must be enabled"
      );
    });

    it("should reject gate selection with all gates disabled", () => {
      const gates = {
        typecheck: false,
        lint: false,
        tests: false,
      };

      expect(() => service.validateGateSelection(gates)).toThrow(
        "At least one gate must be enabled"
      );
    });

    it("should reject coverage disabled without enabled flag", () => {
      const gates = {
        coverage: { enabled: false },
      };

      expect(() => service.validateGateSelection(gates)).toThrow(
        "At least one gate must be enabled"
      );
    });

    it("should accept coverage enabled as only gate", () => {
      const gates = {
        coverage: { enabled: true, threshold: 85 },
      };

      expect(() => service.validateGateSelection(gates)).not.toThrow();
    });
  });

  describe("mergeGateSelections", () => {
    it("should merge two gate selections", () => {
      const base = {
        typecheck: true,
        lint: true,
      };

      const override = {
        tests: true,
        coverage: { enabled: true, threshold: 90 },
      };

      const merged = service.mergeGateSelections(base, override);

      expect(merged).toEqual({
        typecheck: true,
        lint: true,
        tests: true,
        coverage: { enabled: true, threshold: 90 },
      });
    });

    it("should override base values with override values", () => {
      const base = {
        typecheck: true,
        lint: true,
        coverage: { enabled: true, threshold: 85 },
      };

      const override = {
        lint: false,
        coverage: { enabled: true, threshold: 95 },
      };

      const merged = service.mergeGateSelections(base, override);

      expect(merged.typecheck).toBe(true);
      expect(merged.lint).toBe(false);
      expect(merged.coverage?.threshold).toBe(95);
    });

    it("should handle empty override", () => {
      const base = {
        typecheck: true,
        lint: true,
      };

      const merged = service.mergeGateSelections(base, {});

      expect(merged).toEqual(base);
    });

    it("should handle empty base", () => {
      const override = {
        tests: true,
      };

      const merged = service.mergeGateSelections({}, override);

      expect(merged).toEqual(override);
    });
  });

  describe("real-world scenarios", () => {
    it("should configure strict gates for security-critical task", () => {
      const config = service.createTaskConfig("task-security-001", "reviewer", "strict");

      expect(config.profile.gates.typecheck).toBe(true);
      expect(config.profile.gates.lint).toBe(true);
      expect(config.profile.gates.tests).toBe(true);
      expect(config.profile.gates.coverage?.enabled).toBe(true);
      expect(config.profile.gates.build).toBe(true);
      expect(config.profile.gates.integration).toBe(true);
      expect(config.profile.gates.aiReview).toBe(true);
    });

    it("should configure minimal gates for documentation task", () => {
      const customGates = {
        lint: true, // Check markdown formatting
      };

      const config = service.createTaskConfig("task-docs-001", "worker", "custom", customGates);

      expect(config.profile.gates.lint).toBe(true);
      expect(config.profile.gates.tests).toBeUndefined(); // No tests for docs
      expect(config.profile.gates.coverage).toBeUndefined();
    });

    it("should configure standard gates with higher coverage for library code", () => {
      const customGates = {
        typecheck: true,
        lint: true,
        tests: true,
        coverage: { enabled: true, threshold: 95 }, // Higher threshold for library
      };

      const config = service.createTaskConfig("task-lib-001", "worker", "custom", customGates);

      expect(config.profile.gates.coverage?.threshold).toBe(95);
      expect(config.profile.gates.typecheck).toBe(true);
    });

    it("should configure test-only gates for test file generation", () => {
      const config = service.createTaskConfig("task-test-gen-001", "tester", "minimal");

      expect(config.profile.gates.tests).toBe(true);
      expect(config.profile.gates.typecheck).toBeUndefined();
      expect(config.profile.gates.lint).toBeUndefined();
      expect(config.profile.gates.coverage).toBeUndefined();
    });

    it("should configure custom gates for refactoring task", () => {
      const customGates = {
        typecheck: true,
        tests: true,
        coverage: { enabled: true, threshold: 85 },
        // No lint - allow style changes during refactor
        // No build/integration - handled separately
      };

      const config = service.createTaskConfig("task-refactor-001", "worker", "custom", customGates);

      expect(config.profile.gates.typecheck).toBe(true);
      expect(config.profile.gates.tests).toBe(true);
      expect(config.profile.gates.lint).toBeUndefined();
      expect(config.profile.gates.build).toBeUndefined();
    });
  });
});
