import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/server-runtime";
import { json } from "@remix-run/server-runtime";
import type { ScheduleObject } from "@trigger.dev/core/v3";
import { UpdateScheduleOptions } from "@trigger.dev/core/v3";
import { z } from "zod";
import { Prisma, prisma } from "~/db.server";
import { clientSafeErrorMessage } from "~/utils/prismaErrors";
import { scheduleUniqWhereClause } from "~/models/schedules.server";
import { ViewSchedulePresenter } from "~/presenters/v3/ViewSchedulePresenter.server";
import { authenticateApiRequest } from "~/services/apiAuth.server";
import { logger } from "~/services/logger.server";
import type { UpsertSchedule } from "~/v3/schedules";
import { ServiceValidationError } from "~/v3/services/baseService.server";
import { UpsertTaskScheduleService } from "~/v3/services/upsertTaskSchedule.server";

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

export async function action({ request, params }: ActionFunctionArgs) {
  // Authenticate the request
  const authenticationResult = await authenticateApiRequest(request);

  if (!authenticationResult) {
    return json({ error: "Invalid or Missing API Key" }, { status: 401 });
  }

  const parsedParams = ParamsSchema.safeParse(params);

  if (!parsedParams.success) {
    return json(
      { error: "Invalid request parameters", issues: parsedParams.error.issues },
      { status: 400 }
    );
  }

  const method = request.method.toUpperCase();

  switch (method) {
    case "DELETE": {
      try {
        const deletedSchedule = await prisma.taskSchedule.delete({
          where: scheduleUniqWhereClause(
            authenticationResult.environment.projectId,
            parsedParams.data.scheduleId
          ),
        });

        return json(
          {
            id: deletedSchedule.friendlyId,
          },
          { status: 200 }
        );
      } catch (error) {
        // Check if it's a Prisma error
        if (error instanceof Prisma.PrismaClientKnownRequestError) {
          return json(
            {
              error: error.code === "P2025" ? "Schedule not found" : clientSafeErrorMessage(error),
            },
            { status: error.code === "P2025" ? 404 : 422 }
          );
        } else {
          logger.error("Failed to delete schedule", { error });
          return json({ error: "Something went wrong, please try again." }, { status: 500 });
        }
      }
    }
    case "PUT": {
      const rawBody = await request.json();

      const body = UpdateScheduleOptions.safeParse(rawBody);

      if (!body.success) {
        return json({ error: "Invalid request body", issues: body.error.issues }, { status: 400 });
      }

      const service = new UpsertTaskScheduleService();

      try {
        const options: UpsertSchedule = {
          friendlyId: parsedParams.data.scheduleId,
          taskIdentifier: body.data.task,
          cron: body.data.cron,
          timezone: body.data.timezone,
          environments: [authenticationResult.environment.id],
          externalId: body.data.externalId,
        };

        const schedule = await service.call(authenticationResult.environment.projectId, options);

        const responseObject: ScheduleObject = {
          id: schedule.id,
          type: schedule.type,
          task: schedule.task,
          active: schedule.active,
          generator: {
            type: "CRON",
            expression: schedule.cron,
            description: schedule.cronDescription,
          },
          timezone: schedule.timezone,
          externalId: schedule.externalId ?? undefined,
          deduplicationKey: schedule.deduplicationKey,
          environments: schedule.environments,
          nextRun: schedule.nextRun,
        };

        return json(responseObject, { status: 200 });
      } catch (error) {
        if (error instanceof ServiceValidationError) {
          return json({ error: error.message }, { status: 422 });
        }

        logger.error("Failed to upsert schedule", { error });
        return json({ error: "Something went wrong, please try again." }, { status: 500 });
      }
    }
  }
}

export async function loader({ request, params }: LoaderFunctionArgs) {
  // Authenticate the request
  const authenticationResult = await authenticateApiRequest(request);

  if (!authenticationResult) {
    return json({ error: "Invalid or Missing API Key" }, { status: 401 });
  }

  const parsedParams = ParamsSchema.safeParse(params);

  if (!parsedParams.success) {
    return json(
      { error: "Invalid request parameters", issues: parsedParams.error.issues },
      { status: 400 }
    );
  }

  const presenter = new ViewSchedulePresenter();

  const result = await presenter.call({
    projectId: authenticationResult.environment.projectId,
    friendlyId: parsedParams.data.scheduleId,
    environmentId: authenticationResult.environment.id,
  });

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

  return json(presenter.toJSONResponse(result), { status: 200 });
}
