import { $transaction, type PrismaClientOrTransaction, prisma } from "~/db.server";
import { type AuthenticatedEnvironment } from "~/services/apiAuth.server";
import { logger } from "~/services/logger.server";
import { isCancellableRunStatus } from "../taskStatus";
import { BaseService } from "./baseService.server";
import { FinalizeTaskRunService } from "./finalizeTaskRun.server";

export class CancelAttemptService extends BaseService {
  public async call(
    attemptId: string,
    taskRunId: string,
    cancelledAt: Date,
    reason: string,
    env?: AuthenticatedEnvironment
  ) {
    let environment: AuthenticatedEnvironment | undefined = env;

    if (!environment) {
      environment = await getAuthenticatedEnvironmentFromAttempt(attemptId);

      if (!environment) {
        return;
      }
    }

    return await this.traceWithEnv("call()", environment, async (span) => {
      span.setAttribute("taskRunId", taskRunId);
      span.setAttribute("attemptId", attemptId);

      const taskRunAttempt = await this._prisma.taskRunAttempt.findFirst({
        where: {
          friendlyId: attemptId,
        },
        include: {
          taskRun: true,
        },
      });

      if (!taskRunAttempt) {
        return;
      }

      if (taskRunAttempt.status === "CANCELED") {
        logger.warn("Task run attempt is already cancelled", {
          attemptId,
        });

        return;
      }

      await $transaction(this._prisma, "cancel attempt", async (tx) => {
        await tx.taskRunAttempt.update({
          where: {
            friendlyId: attemptId,
          },
          data: {
            status: "CANCELED",
            completedAt: cancelledAt,
          },
        });

        const isCancellable = isCancellableRunStatus(taskRunAttempt.taskRun.status);

        const finalizeService = new FinalizeTaskRunService(tx);
        await finalizeService.call({
          id: taskRunId,
          status: isCancellable ? "INTERRUPTED" : undefined,
          completedAt: isCancellable ? cancelledAt : undefined,
          attemptStatus: isCancellable ? "CANCELED" : undefined,
          error: isCancellable ? { type: "STRING_ERROR", raw: reason } : undefined,
        });
      });
    });
  }
}

async function getAuthenticatedEnvironmentFromAttempt(
  friendlyId: string,
  prismaClient?: PrismaClientOrTransaction
) {
  const taskRunAttempt = await (prismaClient ?? prisma).taskRunAttempt.findFirst({
    where: {
      friendlyId,
    },
    include: {
      runtimeEnvironment: {
        include: {
          organization: true,
          project: true,
        },
      },
    },
  });

  if (!taskRunAttempt) {
    return;
  }

  return taskRunAttempt?.runtimeEnvironment;
}
