import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/server-runtime";
import { json } from "@remix-run/server-runtime";
import { z } from "zod";
import { prisma } from "~/db.server";
import { requireAdminApiRequest } from "~/services/personalAccessToken.server";
import { controlPlaneResolver } from "~/v3/runOpsMigration/controlPlaneResolver.server";
import { engine } from "~/v3/runEngine.server";
import { updateEnvConcurrencyLimits } from "~/v3/runQueue.server";

const ParamsSchema = z.object({
  environmentId: z.string(),
});

const RequestBodySchema = z.object({
  envMaximumConcurrencyLimit: z.number(),
  orgMaximumConcurrencyLimit: z.number(),
});

export async function action({ request, params }: ActionFunctionArgs) {
  await requireAdminApiRequest(request);

  const parsedParams = ParamsSchema.parse(params);

  const rawBody = await request.json();
  const body = RequestBodySchema.parse(rawBody);

  const environment = await prisma.runtimeEnvironment.update({
    where: {
      id: parsedParams.environmentId,
    },
    data: {
      maximumConcurrencyLimit: body.envMaximumConcurrencyLimit,
      organization: {
        update: {
          data: {
            maximumConcurrencyLimit: body.orgMaximumConcurrencyLimit,
          },
        },
      },
    },
    include: {
      organization: true,
      project: true,
    },
  });

  await updateEnvConcurrencyLimits(environment);

  // Org max-concurrency changed too, which is embedded in every env of the org; invalidating
  // the org drops the env/authEnv rows for all of them (including this env).
  controlPlaneResolver.invalidateOrganization(environment.organizationId);

  return json({ success: true });
}

const SearchParamsSchema = z.object({
  queue: z.string().optional(),
});

export async function loader({ request, params }: LoaderFunctionArgs) {
  await requireAdminApiRequest(request);

  const parsedParams = ParamsSchema.parse(params);

  const environment = await prisma.runtimeEnvironment.findFirst({
    where: {
      id: parsedParams.environmentId,
    },
    include: {
      organization: true,
      project: true,
    },
  });

  if (!environment) {
    return json({ error: "Environment not found" }, { status: 404 });
  }

  const requestUrl = new URL(request.url);
  const searchParams = SearchParamsSchema.parse(
    Object.fromEntries(requestUrl.searchParams.entries())
  );

  const concurrencyLimit = await engine.runQueue.getEnvConcurrencyLimit(environment);
  const currentConcurrency = await engine.runQueue.currentConcurrencyOfEnvironment(environment);

  if (searchParams.queue) {
    const queueConcurrencyLimit = await engine.runQueue.getQueueConcurrencyLimit(
      environment,
      searchParams.queue
    );
    const queueCurrentConcurrency = await engine.runQueue.currentConcurrencyOfQueue(
      environment,
      searchParams.queue
    );

    return json({
      id: environment.id,
      concurrencyLimit,
      currentConcurrency,
      queueConcurrencyLimit,
      queueCurrentConcurrency,
    });
  }

  return json({ id: environment.id, concurrencyLimit, currentConcurrency });
}
