import { describe, it, expect, beforeEach, vi } from "vitest";
import { Test, TestingModule } from "@nestjs/testing";
import { ConfigService } from "@nestjs/config";
import { StitcherController } from "./stitcher.controller";
import { StitcherService } from "./stitcher.service";
import { WebhookPayloadDto, DispatchJobDto } from "./dto";
import type { JobDispatchResult } from "./interfaces";
import { ApiKeyGuard } from "../common/guards";

describe("StitcherController", () => {
  let controller: StitcherController;
  let service: StitcherService;

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

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

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      controllers: [StitcherController],
      providers: [
        { provide: StitcherService, useValue: mockStitcherService },
        { provide: ConfigService, useValue: mockConfigService },
      ],
    })
      .overrideGuard(ApiKeyGuard)
      .useValue({ canActivate: () => true })
      .compile();

    controller = module.get<StitcherController>(StitcherController);
    service = module.get<StitcherService>(StitcherService);

    vi.clearAllMocks();
  });

  describe("webhook", () => {
    it("should handle webhook payload and return job result", async () => {
      const payload: WebhookPayloadDto = {
        issueNumber: "42",
        repository: "mosaic/stack",
        action: "assigned",
      };

      const mockResult: JobDispatchResult = {
        jobId: "job-123",
        queueName: "mosaic-jobs",
        status: "PENDING",
      };

      mockStitcherService.handleWebhook.mockResolvedValue(mockResult);

      const result = await controller.webhook(payload);

      expect(result).toEqual(mockResult);
      expect(mockStitcherService.handleWebhook).toHaveBeenCalledWith(payload);
    });

    it("should handle webhook errors", async () => {
      const payload: WebhookPayloadDto = {
        issueNumber: "42",
        repository: "mosaic/stack",
        action: "assigned",
      };

      mockStitcherService.handleWebhook.mockRejectedValue(new Error("Webhook processing failed"));

      await expect(controller.webhook(payload)).rejects.toThrow("Webhook processing failed");
    });
  });

  describe("dispatch", () => {
    it("should dispatch job with provided context", async () => {
      const dto: DispatchJobDto = {
        workspaceId: "workspace-123",
        type: "code-task",
        context: { issueId: "42" },
      };

      const mockResult: JobDispatchResult = {
        jobId: "job-456",
        queueName: "mosaic-jobs",
        status: "PENDING",
      };

      mockStitcherService.dispatchJob.mockResolvedValue(mockResult);

      const result = await controller.dispatch(dto);

      expect(result).toEqual(mockResult);
      expect(mockStitcherService.dispatchJob).toHaveBeenCalledWith({
        workspaceId: "workspace-123",
        type: "code-task",
        metadata: { issueId: "42" },
      });
    });

    it("should handle missing workspace ID", async () => {
      const dto = {
        type: "code-task",
      } as DispatchJobDto;

      // Validation should fail before reaching service
      // This test ensures DTO validation works
      expect(dto.workspaceId).toBeUndefined();
    });
  });
});
