import { describe, it, expect } from "vitest";
import { validate } from "class-validator";
import { plainToInstance } from "class-transformer";
import { CreateCoordinatorJobDto } from "./create-coordinator-job.dto";
import { FailJobDto } from "./fail-job.dto";
import { UpdateJobProgressDto } from "./update-job-progress.dto";
import { UpdateJobStatusDto, CoordinatorJobStatus } from "./update-job-status.dto";
import { CompleteJobDto } from "./complete-job.dto";

/**
 * Comprehensive validation tests for Coordinator Integration DTOs
 *
 * These tests verify that input validation prevents:
 * - SQL injection attacks
 * - XSS attacks
 * - Command injection
 * - Data corruption
 * - Type confusion vulnerabilities
 * - Buffer overflow attacks
 */
describe("Coordinator Integration DTOs - Input Validation", () => {
  describe("CreateCoordinatorJobDto", () => {
    it("should pass validation with valid data", async () => {
      const dto = plainToInstance(CreateCoordinatorJobDto, {
        workspaceId: "123e4567-e89b-42d3-a456-426614174000",
        type: "code-task",
        issueNumber: 42,
        repository: "owner/repo",
        priority: 5,
        metadata: { key: "value" },
      });

      const errors = await validate(dto);
      expect(errors).toHaveLength(0);
    });

    it("should reject missing workspaceId", async () => {
      const dto = plainToInstance(CreateCoordinatorJobDto, {
        type: "code-task",
        issueNumber: 42,
        repository: "owner/repo",
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      expect(errors[0].property).toBe("workspaceId");
    });

    it("should reject invalid UUID format for workspaceId", async () => {
      const dto = plainToInstance(CreateCoordinatorJobDto, {
        workspaceId: "not-a-uuid",
        type: "code-task",
        issueNumber: 42,
        repository: "owner/repo",
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const workspaceIdError = errors.find((e) => e.property === "workspaceId");
      expect(workspaceIdError).toBeDefined();
    });

    it("should reject empty type string", async () => {
      const dto = plainToInstance(CreateCoordinatorJobDto, {
        workspaceId: "123e4567-e89b-42d3-a456-426614174000",
        type: "",
        issueNumber: 42,
        repository: "owner/repo",
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const typeError = errors.find((e) => e.property === "type");
      expect(typeError).toBeDefined();
    });

    it("should reject excessively long type string (SQL injection prevention)", async () => {
      const dto = plainToInstance(CreateCoordinatorJobDto, {
        workspaceId: "123e4567-e89b-42d3-a456-426614174000",
        type: "a".repeat(256),
        issueNumber: 42,
        repository: "owner/repo",
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const typeError = errors.find((e) => e.property === "type");
      expect(typeError).toBeDefined();
    });

    it("should reject negative issue number", async () => {
      const dto = plainToInstance(CreateCoordinatorJobDto, {
        workspaceId: "123e4567-e89b-42d3-a456-426614174000",
        type: "code-task",
        issueNumber: -1,
        repository: "owner/repo",
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const issueError = errors.find((e) => e.property === "issueNumber");
      expect(issueError).toBeDefined();
    });

    it("should reject empty repository string", async () => {
      const dto = plainToInstance(CreateCoordinatorJobDto, {
        workspaceId: "123e4567-e89b-42d3-a456-426614174000",
        type: "code-task",
        issueNumber: 42,
        repository: "",
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const repoError = errors.find((e) => e.property === "repository");
      expect(repoError).toBeDefined();
    });

    it("should reject excessively long repository string (buffer overflow prevention)", async () => {
      const dto = plainToInstance(CreateCoordinatorJobDto, {
        workspaceId: "123e4567-e89b-42d3-a456-426614174000",
        type: "code-task",
        issueNumber: 42,
        repository: "a".repeat(513),
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const repoError = errors.find((e) => e.property === "repository");
      expect(repoError).toBeDefined();
    });

    it("should reject priority below 1", async () => {
      const dto = plainToInstance(CreateCoordinatorJobDto, {
        workspaceId: "123e4567-e89b-42d3-a456-426614174000",
        type: "code-task",
        issueNumber: 42,
        repository: "owner/repo",
        priority: 0,
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const priorityError = errors.find((e) => e.property === "priority");
      expect(priorityError).toBeDefined();
    });

    it("should reject priority above 100", async () => {
      const dto = plainToInstance(CreateCoordinatorJobDto, {
        workspaceId: "123e4567-e89b-42d3-a456-426614174000",
        type: "code-task",
        issueNumber: 42,
        repository: "owner/repo",
        priority: 101,
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const priorityError = errors.find((e) => e.property === "priority");
      expect(priorityError).toBeDefined();
    });
  });

  describe("FailJobDto", () => {
    it("should pass validation with valid data", async () => {
      const dto = plainToInstance(FailJobDto, {
        error: "Build failed",
        gateResults: { passed: false },
        failedStep: "compile",
        continuationPrompt: "Fix the syntax error",
      });

      const errors = await validate(dto);
      expect(errors).toHaveLength(0);
    });

    it("should reject missing error field", async () => {
      const dto = plainToInstance(FailJobDto, {});

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      expect(errors[0].property).toBe("error");
    });

    it("should reject empty error string", async () => {
      const dto = plainToInstance(FailJobDto, {
        error: "",
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const errorField = errors.find((e) => e.property === "error");
      expect(errorField).toBeDefined();
    });

    it("should reject excessively long error string (XSS prevention)", async () => {
      const dto = plainToInstance(FailJobDto, {
        error: "a".repeat(10001),
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const errorField = errors.find((e) => e.property === "error");
      expect(errorField).toBeDefined();
    });

    it("should reject excessively long failedStep string", async () => {
      const dto = plainToInstance(FailJobDto, {
        error: "Build failed",
        failedStep: "a".repeat(256),
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const stepError = errors.find((e) => e.property === "failedStep");
      expect(stepError).toBeDefined();
    });

    it("should reject excessively long continuationPrompt string", async () => {
      const dto = plainToInstance(FailJobDto, {
        error: "Build failed",
        continuationPrompt: "a".repeat(5001),
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const promptError = errors.find((e) => e.property === "continuationPrompt");
      expect(promptError).toBeDefined();
    });
  });

  describe("UpdateJobProgressDto", () => {
    it("should pass validation with valid data", async () => {
      const dto = plainToInstance(UpdateJobProgressDto, {
        progressPercent: 50,
        currentStep: "Building",
        tokensUsed: 1000,
      });

      const errors = await validate(dto);
      expect(errors).toHaveLength(0);
    });

    it("should reject negative progress percent", async () => {
      const dto = plainToInstance(UpdateJobProgressDto, {
        progressPercent: -1,
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const progressError = errors.find((e) => e.property === "progressPercent");
      expect(progressError).toBeDefined();
    });

    it("should reject progress percent above 100", async () => {
      const dto = plainToInstance(UpdateJobProgressDto, {
        progressPercent: 101,
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const progressError = errors.find((e) => e.property === "progressPercent");
      expect(progressError).toBeDefined();
    });

    it("should reject empty currentStep string", async () => {
      const dto = plainToInstance(UpdateJobProgressDto, {
        progressPercent: 50,
        currentStep: "",
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const stepError = errors.find((e) => e.property === "currentStep");
      expect(stepError).toBeDefined();
    });

    it("should reject excessively long currentStep string", async () => {
      const dto = plainToInstance(UpdateJobProgressDto, {
        progressPercent: 50,
        currentStep: "a".repeat(256),
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const stepError = errors.find((e) => e.property === "currentStep");
      expect(stepError).toBeDefined();
    });

    it("should reject negative tokensUsed", async () => {
      const dto = plainToInstance(UpdateJobProgressDto, {
        progressPercent: 50,
        tokensUsed: -1,
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const tokenError = errors.find((e) => e.property === "tokensUsed");
      expect(tokenError).toBeDefined();
    });
  });

  describe("UpdateJobStatusDto", () => {
    it("should pass validation with valid data", async () => {
      const dto = plainToInstance(UpdateJobStatusDto, {
        status: CoordinatorJobStatus.RUNNING,
        agentId: "agent-123",
        agentType: "coordinator",
      });

      const errors = await validate(dto);
      expect(errors).toHaveLength(0);
    });

    it("should reject invalid status enum", async () => {
      const dto = plainToInstance(UpdateJobStatusDto, {
        status: "INVALID_STATUS" as any,
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const statusError = errors.find((e) => e.property === "status");
      expect(statusError).toBeDefined();
    });

    it("should reject empty agentId string", async () => {
      const dto = plainToInstance(UpdateJobStatusDto, {
        status: CoordinatorJobStatus.RUNNING,
        agentId: "",
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const agentIdError = errors.find((e) => e.property === "agentId");
      expect(agentIdError).toBeDefined();
    });

    it("should reject excessively long agentId string", async () => {
      const dto = plainToInstance(UpdateJobStatusDto, {
        status: CoordinatorJobStatus.RUNNING,
        agentId: "a".repeat(256),
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const agentIdError = errors.find((e) => e.property === "agentId");
      expect(agentIdError).toBeDefined();
    });

    it("should reject empty agentType string", async () => {
      const dto = plainToInstance(UpdateJobStatusDto, {
        status: CoordinatorJobStatus.RUNNING,
        agentType: "",
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const agentTypeError = errors.find((e) => e.property === "agentType");
      expect(agentTypeError).toBeDefined();
    });

    it("should reject excessively long agentType string", async () => {
      const dto = plainToInstance(UpdateJobStatusDto, {
        status: CoordinatorJobStatus.RUNNING,
        agentType: "a".repeat(101),
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const agentTypeError = errors.find((e) => e.property === "agentType");
      expect(agentTypeError).toBeDefined();
    });
  });

  describe("CompleteJobDto", () => {
    it("should pass validation with valid data", async () => {
      const dto = plainToInstance(CompleteJobDto, {
        result: { success: true },
        tokensUsed: 5000,
        durationSeconds: 120,
      });

      const errors = await validate(dto);
      expect(errors).toHaveLength(0);
    });

    it("should reject negative tokensUsed", async () => {
      const dto = plainToInstance(CompleteJobDto, {
        tokensUsed: -1,
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const tokenError = errors.find((e) => e.property === "tokensUsed");
      expect(tokenError).toBeDefined();
    });

    it("should reject negative durationSeconds", async () => {
      const dto = plainToInstance(CompleteJobDto, {
        durationSeconds: -1,
      });

      const errors = await validate(dto);
      expect(errors.length).toBeGreaterThan(0);
      const durationError = errors.find((e) => e.property === "durationSeconds");
      expect(durationError).toBeDefined();
    });

    it("should pass validation with all fields empty (all optional)", async () => {
      const dto = plainToInstance(CompleteJobDto, {});

      const errors = await validate(dto);
      expect(errors).toHaveLength(0);
    });
  });
});
