import { EnvironmentPauseSource, type PrismaClientOrTransaction } from "@trigger.dev/database";
import { prisma } from "~/db.server";
import { logger } from "~/services/logger.server";
import { getManualPauseEnvironmentResult } from "~/v3/services/billingLimit/manualPauseEnvironmentGuard.server";
import { updateEnvConcurrencyLimits } from "../runQueue.server";
import { WithRunEngine } from "./baseService.server";
import type { AuthenticatedEnvironment } from "~/services/apiAuth.server";
import { controlPlaneResolver } from "~/v3/runOpsMigration/controlPlaneResolver.server";

export type PauseStatus = "paused" | "resumed";

export type PauseEnvironmentResult =
  | {
      success: true;
      state: PauseStatus;
    }
  | {
      success: false;
      error: string;
    };

export class PauseEnvironmentService extends WithRunEngine {
  constructor(protected readonly _prisma: PrismaClientOrTransaction = prisma) {
    super({ prisma });
  }

  public async call(
    environment: AuthenticatedEnvironment,
    action: PauseStatus
  ): Promise<PauseEnvironmentResult> {
    try {
      const org = await this._prisma.organization.findFirst({
        where: {
          id: environment.organizationId,
        },
        select: {
          runsEnabled: true,
        },
      });

      if (!org) {
        throw new Error("Organization not found");
      }

      const previousPauseState = await this._prisma.runtimeEnvironment.findFirst({
        where: { id: environment.id },
        select: {
          paused: true,
          pauseSource: true,
        },
      });

      const manualPauseGuard = getManualPauseEnvironmentResult(
        action,
        previousPauseState?.pauseSource
      );
      if (!manualPauseGuard.proceed) {
        if (manualPauseGuard.success) {
          return {
            success: true,
            state: manualPauseGuard.state,
          };
        }
        // Expected, user-actionable guard result, not an error: return it as a failure
        // result so it doesn't reach Sentry via the catch below.
        return {
          success: false,
          error: manualPauseGuard.error,
        };
      }

      if (!org.runsEnabled && action === "resumed") {
        throw new Error(
          "Runs are disabled for this organization. Your free plan has probably been exceeded. If not please contact support."
        );
      }

      if (action === "resumed") {
        const resumed = await this._prisma.runtimeEnvironment.updateMany({
          where: {
            id: environment.id,
            // NOT on a nullable field excludes NULL rows in Prisma, which made
            // user-paused envs (pauseSource null) unresumable.
            OR: [
              { pauseSource: null },
              { NOT: { pauseSource: EnvironmentPauseSource.BILLING_LIMIT } },
            ],
          },
          data: {
            paused: false,
            pauseSource: null,
          },
        });

        if (resumed.count === 0) {
          // Raced into the paused state after the guard read above: expected,
          // return as a failure result rather than throwing to Sentry.
          return {
            success: false,
            error:
              "This environment is paused because your organization reached its billing limit. Resolve the limit on the billing limits settings page to resume.",
          };
        }
      } else {
        await this._prisma.runtimeEnvironment.update({
          where: { id: environment.id },
          data: { paused: true },
        });
      }

      try {
        if (action === "paused") {
          logger.debug("PauseEnvironmentService: pausing environment", {
            environmentId: environment.id,
          });
          await updateEnvConcurrencyLimits(environment, 0);
        } else {
          logger.debug("PauseEnvironmentService: resuming environment", {
            environmentId: environment.id,
          });
          await updateEnvConcurrencyLimits(environment);
        }
      } catch (error) {
        await this._prisma.runtimeEnvironment.update({
          where: { id: environment.id },
          data: {
            paused: previousPauseState?.paused ?? action === "resumed",
            pauseSource: previousPauseState?.pauseSource ?? null,
          },
        });
        // Rollback still wrote the env row; drop any cached copy before rethrowing.
        controlPlaneResolver.invalidateEnvironment(environment.id);
        throw error;
      }

      // The env's `paused` state changed in the control-plane; drop any cached copy.
      controlPlaneResolver.invalidateEnvironment(environment.id);

      return {
        success: true,
        state: action,
      };
    } catch (error) {
      logger.error("PauseEnvironmentService: error pausing environment", {
        action,
        environmentId: environment.id,
        error,
      });

      return {
        success: false,
        error: error instanceof Error ? error.message : "Unknown error",
      };
    }
  }
}
