import type { PrismaClient } from "@trigger.dev/database";
import { prisma } from "~/db.server";
import { marqs } from "~/v3/marqs/index.server";
import { engine } from "~/v3/runEngine.server";
import { controlPlaneResolver } from "~/v3/runOpsMigration/controlPlaneResolver.server";

type Options = ({ projectId: string } | { projectSlug: string }) & {
  userId: string;
};

export class DeleteProjectService {
  #prismaClient: PrismaClient;

  constructor(prismaClient: PrismaClient = prisma) {
    this.#prismaClient = prismaClient;
  }

  public async call(options: Options) {
    const projectId = await this.#getProjectId(options);
    const project = await this.#prismaClient.project.findFirst({
      include: {
        environments: true,
        organization: true,
      },
      where: {
        id: projectId,
        organization: { members: { some: { userId: options.userId } } },
      },
    });

    if (!project) {
      throw new Error("Project not found");
    }

    if (project.deletedAt) {
      return;
    }

    // Remove queues from MARQS
    for (const environment of project.environments) {
      await marqs?.removeEnvironmentQueuesFromMasterQueue(project.organization.id, environment.id);
    }

    // Delete all queues from the RunEngine 2 prod master queues
    for (const environment of project.environments) {
      await engine.removeEnvironmentQueuesFromMasterQueue({
        runtimeEnvironmentId: environment.id,
        organizationId: project.organization.id,
        projectId: project.id,
      });
    }

    // Soft delete only: run-ops rows are intentionally retained (no hard-delete cascade here).

    // Mark the project as deleted (do this last because it makes it impossible to try again)
    // - This disables all API keys
    // - This disables all schedules from being scheduled
    await this.#prismaClient.project.update({
      where: {
        id: project.id,
      },
      data: {
        deletedAt: new Date(),
      },
    });

    // project.deletedAt (which gates env resolution) changed; drop every cached env of this project.
    for (const environment of project.environments) {
      controlPlaneResolver.invalidateEnvironment(environment.id);
    }
  }

  async #getProjectId(options: Options) {
    if ("projectId" in options) {
      return options.projectId;
    }

    const { id } = await this.#prismaClient.project.findFirstOrThrow({
      select: {
        id: true,
      },
      where: {
        slug: options.projectSlug,
      },
    });

    return id;
  }
}
