import { vi } from "vitest";
import { RunnerJobStatus, JobStepStatus, JobStepPhase, JobStepType } from "@prisma/client";
import type { PrismaService } from "../../src/prisma/prisma.service";

/**
 * Create a mock RunnerJob
 */
export function createMockRunnerJob(
  overrides?: Partial<{
    id: string;
    workspaceId: string;
    type: string;
    status: RunnerJobStatus;
    priority: number;
    progressPercent: number;
    result: unknown;
    error: string | null;
    createdAt: Date;
    startedAt: Date | null;
    completedAt: Date | null;
    agentTaskId: string | null;
  }>
) {
  return {
    id: "job-123",
    workspaceId: "workspace-123",
    type: "code-task",
    status: RunnerJobStatus.PENDING,
    priority: 10,
    progressPercent: 0,
    result: null,
    error: null,
    createdAt: new Date(),
    startedAt: null,
    completedAt: null,
    agentTaskId: null,
    ...overrides,
  };
}

/**
 * Create a mock JobStep
 */
export function createMockJobStep(
  overrides?: Partial<{
    id: string;
    jobId: string;
    ordinal: number;
    phase: JobStepPhase;
    name: string;
    type: JobStepType;
    status: JobStepStatus;
    output: string | null;
    tokensInput: number | null;
    tokensOutput: number | null;
    startedAt: Date | null;
    completedAt: Date | null;
    durationMs: number | null;
  }>
) {
  return {
    id: "step-123",
    jobId: "job-123",
    ordinal: 1,
    phase: JobStepPhase.VALIDATION,
    name: "Validate requirements",
    type: JobStepType.TOOL,
    status: JobStepStatus.PENDING,
    output: null,
    tokensInput: null,
    tokensOutput: null,
    startedAt: null,
    completedAt: null,
    durationMs: null,
    ...overrides,
  };
}

/**
 * Create a mock JobEvent
 */
export function createMockJobEvent(
  overrides?: Partial<{
    id: string;
    jobId: string;
    stepId: string | null;
    type: string;
    timestamp: Date;
    actor: string;
    payload: unknown;
  }>
) {
  return {
    id: "event-123",
    jobId: "job-123",
    stepId: null,
    type: "job.created",
    timestamp: new Date(),
    actor: "stitcher",
    payload: {},
    ...overrides,
  };
}

/**
 * Create a mock Prisma service with commonly used methods
 */
export function createMockPrismaService(): Partial<PrismaService> {
  const jobs = new Map<string, ReturnType<typeof createMockRunnerJob>>();
  const steps = new Map<string, ReturnType<typeof createMockJobStep>>();
  const events: ReturnType<typeof createMockJobEvent>[] = [];

  return {
    runnerJob: {
      create: vi.fn().mockImplementation(({ data }) => {
        // Use a counter to ensure unique IDs even if called in quick succession
        const timestamp = Date.now();
        const randomSuffix = Math.floor(Math.random() * 1000);
        const job = createMockRunnerJob({
          id: `job-${timestamp}-${randomSuffix}`,
          workspaceId: data.workspaceId || data.workspace?.connect?.id,
          type: data.type,
          status: data.status,
          priority: data.priority,
          progressPercent: data.progressPercent,
        });
        jobs.set(job.id, job);
        return Promise.resolve(job);
      }),
      findUnique: vi.fn().mockImplementation(({ where, include }) => {
        const job = jobs.get(where.id);
        if (!job) return Promise.resolve(null);

        const result = { ...job };
        if (include?.steps) {
          (result as never)["steps"] = Array.from(steps.values()).filter((s) => s.jobId === job.id);
        }
        if (include?.events) {
          (result as never)["events"] = events.filter((e) => e.jobId === job.id);
        }
        return Promise.resolve(result);
      }),
      findMany: vi.fn().mockImplementation(({ where }) => {
        const allJobs = Array.from(jobs.values());
        if (!where) return Promise.resolve(allJobs);

        return Promise.resolve(
          allJobs.filter((job) => {
            if (where.workspaceId && job.workspaceId !== where.workspaceId) return false;
            if (where.status && job.status !== where.status) return false;
            return true;
          })
        );
      }),
      update: vi.fn().mockImplementation(({ where, data }) => {
        const job = jobs.get(where.id);
        if (!job) return Promise.resolve(null);

        const updated = { ...job, ...data };
        jobs.set(job.id, updated);
        return Promise.resolve(updated);
      }),
      count: vi.fn().mockImplementation(() => Promise.resolve(jobs.size)),
    } as never,
    jobStep: {
      create: vi.fn().mockImplementation(({ data }) => {
        const step = createMockJobStep({
          id: `step-${Date.now()}`,
          jobId: data.jobId || data.job?.connect?.id,
          ordinal: data.ordinal,
          phase: data.phase,
          name: data.name,
          type: data.type,
          status: data.status,
        });
        steps.set(step.id, step);
        return Promise.resolve(step);
      }),
      findUnique: vi.fn().mockImplementation(({ where }) => {
        const step = steps.get(where.id);
        return Promise.resolve(step || null);
      }),
      findMany: vi.fn().mockImplementation(({ where }) => {
        const allSteps = Array.from(steps.values());
        if (!where) return Promise.resolve(allSteps);

        return Promise.resolve(allSteps.filter((step) => step.jobId === where.jobId));
      }),
      update: vi.fn().mockImplementation(({ where, data }) => {
        const step = steps.get(where.id);
        if (!step) return Promise.resolve(null);

        const updated = { ...step, ...data };
        steps.set(step.id, updated);
        return Promise.resolve(updated);
      }),
    } as never,
    jobEvent: {
      create: vi.fn().mockImplementation(({ data }) => {
        const event = createMockJobEvent({
          id: `event-${Date.now()}`,
          jobId: data.jobId || data.job?.connect?.id,
          stepId: data.stepId || data.step?.connect?.id || null,
          type: data.type,
          timestamp: data.timestamp || new Date(),
          actor: data.actor,
          payload: data.payload,
        });
        events.push(event);
        return Promise.resolve(event);
      }),
      findMany: vi.fn().mockImplementation(({ where, orderBy }) => {
        let filtered = events;
        if (where?.jobId) {
          filtered = filtered.filter((e) => e.jobId === where.jobId);
        }
        if (orderBy?.timestamp) {
          filtered = [...filtered].sort((a, b) =>
            orderBy.timestamp === "asc"
              ? a.timestamp.getTime() - b.timestamp.getTime()
              : b.timestamp.getTime() - a.timestamp.getTime()
          );
        }
        return Promise.resolve(filtered);
      }),
    } as never,
    workspace: {
      findUnique: vi.fn().mockResolvedValue({
        id: "workspace-123",
        slug: "test-workspace",
        name: "Test Workspace",
      }),
    } as never,
  };
}
