import { beforeEach, describe, expect, it, vi } from "vitest";
import { Test, TestingModule } from "@nestjs/testing";
import { ProjectStatus, TaskPriority, TaskStatus } from "@prisma/client";
import { ImportService } from "./import.service";
import { PrismaService } from "../prisma/prisma.service";

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

  const mockPrismaService = {
    withWorkspaceContext: vi.fn(),
    domain: {
      findUnique: vi.fn(),
      create: vi.fn(),
    },
    project: {
      findFirst: vi.fn(),
      create: vi.fn(),
    },
    task: {
      findFirst: vi.fn(),
      create: vi.fn(),
    },
  };

  const workspaceId = "550e8400-e29b-41d4-a716-446655440001";
  const userId = "550e8400-e29b-41d4-a716-446655440002";

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        ImportService,
        {
          provide: PrismaService,
          useValue: mockPrismaService,
        },
      ],
    }).compile();

    service = module.get<ImportService>(ImportService);
    vi.clearAllMocks();

    mockPrismaService.withWorkspaceContext.mockImplementation(
      async (_userId: string, _workspaceId: string, fn: (client: unknown) => Promise<unknown>) => {
        return fn(mockPrismaService);
      }
    );
  });

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

  describe("importTasks", () => {
    it("maps status/priority/domain and imports a task", async () => {
      mockPrismaService.task.findFirst.mockResolvedValue(null);
      mockPrismaService.domain.findUnique.mockResolvedValue(null);
      mockPrismaService.domain.create.mockResolvedValue({ id: "domain-id" });
      mockPrismaService.project.findFirst.mockResolvedValue(null);
      mockPrismaService.task.create.mockResolvedValue({ id: "task-id" });

      const result = await service.importTasks(workspaceId, userId, [
        {
          id: "task-1",
          title: "Import me",
          domain: "Platform Ops",
          status: "in-progress",
          priority: "critical",
          project: null,
          related: [],
          blocks: [],
          blocked_by: [],
          progress: 42,
          due: "2026-03-15",
          created: "2026-03-01T10:00:00.000Z",
          updated: "2026-03-05T12:00:00.000Z",
          assignee: null,
          notes: "notes",
          notes_nontechnical: "non technical",
        },
      ]);

      expect(result).toEqual({ imported: 1, skipped: 0, errors: [] });
      expect(mockPrismaService.task.create).toHaveBeenCalledWith(
        expect.objectContaining({
          data: expect.objectContaining({
            title: "Import me",
            status: TaskStatus.IN_PROGRESS,
            priority: TaskPriority.HIGH,
            domainId: "domain-id",
          }),
        })
      );
    });

    it("skips existing task by brainId", async () => {
      mockPrismaService.task.findFirst.mockResolvedValue({ id: "existing-task-id" });

      const result = await service.importTasks(workspaceId, userId, [
        {
          id: "task-1",
          title: "Existing",
          domain: null,
          status: "pending",
          priority: "medium",
          project: null,
          related: [],
          blocks: [],
          blocked_by: [],
          progress: null,
          due: null,
          created: null,
          updated: null,
          assignee: null,
          notes: null,
          notes_nontechnical: null,
        },
      ]);

      expect(result.imported).toBe(0);
      expect(result.skipped).toBe(1);
      expect(mockPrismaService.task.create).not.toHaveBeenCalled();
    });

    it("collects mapping/missing-project errors while importing", async () => {
      mockPrismaService.task.findFirst.mockResolvedValue(null);
      mockPrismaService.project.findFirst.mockResolvedValue(null);
      mockPrismaService.task.create.mockResolvedValue({ id: "task-id" });

      const result = await service.importTasks(workspaceId, userId, [
        {
          id: "task-1",
          title: "Needs project",
          domain: null,
          status: "mystery-status",
          priority: "mystery-priority",
          project: "brain-project-1",
          related: [],
          blocks: [],
          blocked_by: [],
          progress: null,
          due: null,
          created: null,
          updated: null,
          assignee: null,
          notes: null,
          notes_nontechnical: null,
        },
      ]);

      expect(result.imported).toBe(1);
      expect(result.errors).toEqual(
        expect.arrayContaining([
          expect.stringContaining('Unknown task status "mystery-status"'),
          expect.stringContaining('Unknown task priority "mystery-priority"'),
          expect.stringContaining('referenced project "brain-project-1" not found'),
        ])
      );
      expect(mockPrismaService.task.create).toHaveBeenCalledWith(
        expect.objectContaining({
          data: expect.objectContaining({
            status: TaskStatus.NOT_STARTED,
            priority: TaskPriority.MEDIUM,
            projectId: null,
          }),
        })
      );
    });
  });

  describe("importProjects", () => {
    it("maps status/domain and imports a project", async () => {
      mockPrismaService.project.findFirst.mockResolvedValue(null);
      mockPrismaService.domain.findUnique.mockResolvedValue(null);
      mockPrismaService.domain.create.mockResolvedValue({ id: "domain-id" });
      mockPrismaService.project.create.mockResolvedValue({ id: "project-id" });

      const result = await service.importProjects(workspaceId, userId, [
        {
          id: "project-1",
          name: "Project One",
          description: "desc",
          domain: "Backend",
          status: "in-progress",
          priority: "high",
          progress: 50,
          repo: "git@example.com/repo",
          branch: "main",
          current_milestone: "MS21",
          next_milestone: "MS22",
          blocker: null,
          owner: "owner",
          docs_path: "docs/PRD.md",
          created: "2026-03-01",
          updated: "2026-03-05",
          target_date: "2026-04-01",
          notes: "notes",
          notes_nontechnical: "non tech",
          parent: null,
        },
      ]);

      expect(result).toEqual({ imported: 1, skipped: 0, errors: [] });
      expect(mockPrismaService.project.create).toHaveBeenCalledWith(
        expect.objectContaining({
          data: expect.objectContaining({
            name: "Project One",
            status: ProjectStatus.ACTIVE,
            domainId: "domain-id",
          }),
        })
      );
    });

    it("captures create failures as errors", async () => {
      mockPrismaService.project.findFirst.mockResolvedValue(null);
      mockPrismaService.project.create.mockRejectedValue(new Error("db failed"));

      const result = await service.importProjects(workspaceId, userId, [
        {
          id: "project-1",
          name: "Project One",
          description: null,
          domain: null,
          status: "planning",
          priority: null,
          progress: null,
          repo: null,
          branch: null,
          current_milestone: null,
          next_milestone: null,
          blocker: null,
          owner: null,
          docs_path: null,
          created: null,
          updated: null,
          target_date: null,
          notes: null,
          notes_nontechnical: null,
          parent: null,
        },
      ]);

      expect(result.imported).toBe(0);
      expect(result.skipped).toBe(1);
      expect(result.errors).toEqual([
        expect.stringContaining("project project-1: failed to import: db failed"),
      ]);
    });
  });
});
