import type { ActionFunctionArgs } from "@remix-run/server-runtime";
import { json } from "@remix-run/server-runtime";
import { z } from "zod";
import {
  ApiAlertChannelPresenter,
  ApiCreateAlertChannel,
} from "~/presenters/v3/ApiAlertChannelPresenter.server";
import { logger } from "~/services/logger.server";
import { authenticateApiRequestWithPersonalAccessToken } from "~/services/personalAccessToken.server";
import { CreateAlertChannelService } from "~/v3/services/alerts/createAlertChannel.server";
import { ServiceValidationError } from "~/v3/services/baseService.server";

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

export async function action({ request, params }: ActionFunctionArgs) {
  try {
    const authenticationResult = await authenticateApiRequestWithPersonalAccessToken(request);

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

    const parsedParams = ParamsSchema.safeParse(params);

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

    const { projectRef } = parsedParams.data;

    const rawBody = await request.json();

    const body = ApiCreateAlertChannel.safeParse(rawBody);

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

    const service = new CreateAlertChannelService();

    try {
      if (body.data.channel === "email") {
        if (!body.data.channelData.email) {
          return json({ error: "Email is required" }, { status: 422 });
        }

        const alertChannel = await service.call(projectRef, authenticationResult.userId, {
          name: body.data.name,
          alertTypes: body.data.alertTypes.map((type) =>
            ApiAlertChannelPresenter.alertTypeFromApi(type)
          ),
          channel: {
            type: "EMAIL",
            email: body.data.channelData.email,
          },
          deduplicationKey: body.data.deduplicationKey,
          environmentTypes: body.data.environmentTypes,
        });

        return json(await ApiAlertChannelPresenter.alertChannelToApi(alertChannel));
      }

      if (body.data.channel === "webhook") {
        if (!body.data.channelData.url) {
          return json({ error: "webhook url is required" }, { status: 422 });
        }

        // The webhook URL is validated in CreateAlertChannelService.call();
        // an unsafe URL surfaces as a ServiceValidationError -> 422 below.
        const alertChannel = await service.call(projectRef, authenticationResult.userId, {
          name: body.data.name,
          alertTypes: body.data.alertTypes.map((type) =>
            ApiAlertChannelPresenter.alertTypeFromApi(type)
          ),
          channel: {
            type: "WEBHOOK",
            url: body.data.channelData.url,
            secret: body.data.channelData.secret,
          },
          deduplicationKey: body.data.deduplicationKey,
          environmentTypes: body.data.environmentTypes,
        });

        return json(await ApiAlertChannelPresenter.alertChannelToApi(alertChannel));
      }

      return json({ error: "Invalid channel type" }, { status: 422 });
    } catch (error) {
      if (error instanceof ServiceValidationError) {
        return json({ error: error.message }, { status: 422 });
      }

      logger.error("Failed to create alert channel", { error });
      return json({ error: "Something went wrong, please try again." }, { status: 500 });
    }
  } catch (error) {
    if (error instanceof Response) throw error;
    logger.error("Failed to create alert channel (outer)", { error });
    return json({ error: "Internal Server Error" }, { status: 500 });
  }
}
