import { describe, it, expect, beforeEach, vi } from "vitest";
import { Test, TestingModule } from "@nestjs/testing";
import { ExecutionContext } from "@nestjs/common";

// Mock @prisma/client BEFORE importing other modules
vi.mock("@prisma/client", async (importOriginal) => {
  const actual = await importOriginal<typeof import("@prisma/client")>();
  return {
    ...actual,
    JobStepPhase: {
      SETUP: "SETUP",
      EXECUTION: "EXECUTION",
      VALIDATION: "VALIDATION",
      CLEANUP: "CLEANUP",
    },
    JobStepType: {
      COMMAND: "COMMAND",
      AI_ACTION: "AI_ACTION",
      GATE: "GATE",
      ARTIFACT: "ARTIFACT",
    },
    JobStepStatus: {
      PENDING: "PENDING",
      RUNNING: "RUNNING",
      COMPLETED: "COMPLETED",
      FAILED: "FAILED",
      SKIPPED: "SKIPPED",
    },
  };
});

// Import after mocking
import { JobStepsController } from "./job-steps.controller";
import { JobStepsService } from "./job-steps.service";
import { JobStepPhase, JobStepType, JobStepStatus } from "@prisma/client";
import { AuthGuard } from "../auth/guards/auth.guard";
import { WorkspaceGuard } from "../common/guards/workspace.guard";
import { PermissionGuard } from "../common/guards/permission.guard";

describe("JobStepsController", () => {
  let controller: JobStepsController;
  let service: JobStepsService;

  const mockJobStepsService = {
    findAllByJob: vi.fn(),
    findOne: vi.fn(),
    create: vi.fn(),
    update: vi.fn(),
    startStep: vi.fn(),
    completeStep: vi.fn(),
    failStep: vi.fn(),
  };

  const mockAuthGuard = {
    canActivate: vi.fn((context: ExecutionContext) => {
      const request = context.switchToHttp().getRequest();
      request.user = {
        id: "user-123",
        workspaceId: "workspace-123",
      };
      return true;
    }),
  };

  const mockWorkspaceGuard = {
    canActivate: vi.fn(() => true),
  };

  const mockPermissionGuard = {
    canActivate: vi.fn(() => true),
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      controllers: [JobStepsController],
      providers: [
        {
          provide: JobStepsService,
          useValue: mockJobStepsService,
        },
      ],
    })
      .overrideGuard(AuthGuard)
      .useValue(mockAuthGuard)
      .overrideGuard(WorkspaceGuard)
      .useValue(mockWorkspaceGuard)
      .overrideGuard(PermissionGuard)
      .useValue(mockPermissionGuard)
      .compile();

    controller = module.get<JobStepsController>(JobStepsController);
    service = module.get<JobStepsService>(JobStepsService);

    // Clear all mocks before each test
    vi.clearAllMocks();
  });

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

  describe("findAll", () => {
    it("should return all steps for a job", async () => {
      const jobId = "job-123";
      const mockSteps = [
        {
          id: "step-1",
          jobId,
          ordinal: 1,
          phase: JobStepPhase.SETUP,
          name: "Clone repo",
          type: JobStepType.COMMAND,
          status: JobStepStatus.COMPLETED,
          output: "Cloned successfully",
          tokensInput: null,
          tokensOutput: null,
          startedAt: new Date("2024-01-01T10:00:00Z"),
          completedAt: new Date("2024-01-01T10:00:05Z"),
          durationMs: 5000,
        },
        {
          id: "step-2",
          jobId,
          ordinal: 2,
          phase: JobStepPhase.EXECUTION,
          name: "Run tests",
          type: JobStepType.COMMAND,
          status: JobStepStatus.RUNNING,
          output: null,
          tokensInput: null,
          tokensOutput: null,
          startedAt: new Date("2024-01-01T10:00:05Z"),
          completedAt: null,
          durationMs: null,
        },
      ];

      mockJobStepsService.findAllByJob.mockResolvedValue(mockSteps);

      const result = await controller.findAll(jobId);

      expect(result).toEqual(mockSteps);
      expect(service.findAllByJob).toHaveBeenCalledWith(jobId);
    });
  });

  describe("findOne", () => {
    it("should return a single step by ID", async () => {
      const jobId = "job-123";
      const stepId = "step-123";

      const mockStep = {
        id: stepId,
        jobId,
        ordinal: 1,
        phase: JobStepPhase.SETUP,
        name: "Clone repo",
        type: JobStepType.COMMAND,
        status: JobStepStatus.COMPLETED,
        output: "Cloned successfully",
        tokensInput: null,
        tokensOutput: null,
        startedAt: new Date("2024-01-01T10:00:00Z"),
        completedAt: new Date("2024-01-01T10:00:05Z"),
        durationMs: 5000,
      };

      mockJobStepsService.findOne.mockResolvedValue(mockStep);

      const result = await controller.findOne(jobId, stepId);

      expect(result).toEqual(mockStep);
      expect(service.findOne).toHaveBeenCalledWith(stepId, jobId);
    });
  });
});
