import { NextRequest } from "next/server";
import { logger } from "@formbricks/logger";
import { DatabaseError, InvalidInputError, ResourceNotFoundError } from "@formbricks/types/errors";
import { ZResponseUpdateInput } from "@formbricks/types/responses";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { sendToPipeline } from "@/app/lib/pipelines";
import { getResponse } from "@/lib/response/service";
import { getSurvey } from "@/lib/survey/service";
import { validateOtherOptionLengthForMultipleChoice } from "@/modules/api/v2/lib/element";
import { createQuotaFullObject } from "@/modules/ee/quotas/lib/helpers";
import { validateFileUploads } from "@/modules/storage/utils";
import { updateResponseWithQuotaEvaluation } from "./lib/response";

export const OPTIONS = async (): Promise<Response> => {
  return responses.successResponse({}, true);
};

const handleDatabaseError = (error: Error, url: string, endpoint: string, responseId: string): Response => {
  if (error instanceof ResourceNotFoundError) {
    return responses.notFoundResponse("Response", responseId, true);
  }
  if (error instanceof InvalidInputError) {
    return responses.badRequestResponse(error.message, undefined, true);
  }
  if (error instanceof DatabaseError) {
    logger.error({ error, url }, `Error in ${endpoint}`);
    return responses.internalServerErrorResponse(error.message, true);
  }
  return responses.internalServerErrorResponse("Unknown error occurred", true);
};

export const PUT = withV1ApiWrapper({
  handler: async ({
    req,
    props,
  }: {
    req: NextRequest;
    props: { params: Promise<{ responseId: string }> };
  }) => {
    const params = await props.params;
    const { responseId } = params;

    if (!responseId) {
      return {
        response: responses.badRequestResponse("Response ID is missing", undefined, true),
      };
    }

    const responseUpdate = await req.json();
    const inputValidation = ZResponseUpdateInput.safeParse(responseUpdate);

    if (!inputValidation.success) {
      return {
        response: responses.badRequestResponse(
          "Fields are missing or incorrectly formatted",
          transformErrorToDetails(inputValidation.error),
          true
        ),
      };
    }

    let response;
    try {
      response = await getResponse(responseId);
    } catch (error) {
      const endpoint = "PUT /api/v1/client/[environmentId]/responses/[responseId]";
      return {
        response: handleDatabaseError(error, req.url, endpoint, responseId),
      };
    }

    if (response.finished) {
      return {
        response: responses.badRequestResponse("Response is already finished", undefined, true),
      };
    }

    // get survey to get environmentId
    let survey;
    try {
      survey = await getSurvey(response.surveyId);
    } catch (error) {
      const endpoint = "PUT /api/v1/client/[environmentId]/responses/[responseId]";
      return {
        response: handleDatabaseError(error, req.url, endpoint, responseId),
      };
    }

    if (!validateFileUploads(inputValidation.data.data, survey.questions)) {
      return {
        response: responses.badRequestResponse("Invalid file upload response", undefined, true),
      };
    }

    // Validate response data for "other" options exceeding character limit
    const otherResponseInvalidQuestionId = validateOtherOptionLengthForMultipleChoice({
      responseData: inputValidation.data.data,
      surveyQuestions: survey.questions,
      responseLanguage: inputValidation.data.language,
    });

    if (otherResponseInvalidQuestionId) {
      return {
        response: responses.badRequestResponse(
          `Response exceeds character limit`,
          {
            questionId: otherResponseInvalidQuestionId,
          },
          true
        ),
      };
    }

    // update response with quota evaluation
    let updatedResponse;
    try {
      updatedResponse = await updateResponseWithQuotaEvaluation(responseId, inputValidation.data);
    } catch (error) {
      if (error instanceof ResourceNotFoundError) {
        return {
          response: responses.notFoundResponse("Response", responseId, true),
        };
      }
      if (error instanceof InvalidInputError) {
        return {
          response: responses.badRequestResponse(error.message),
        };
      }
      if (error instanceof DatabaseError) {
        logger.error(
          { error, url: req.url },
          "Error in PUT /api/v1/client/[environmentId]/responses/[responseId]"
        );
        return {
          response: responses.internalServerErrorResponse(error.message),
        };
      }
    }

    const { quotaFull, ...responseData } = updatedResponse;

    // send response update to pipeline
    // don't await to not block the response
    sendToPipeline({
      event: "responseUpdated",
      environmentId: survey.environmentId,
      surveyId: survey.id,
      response: responseData,
    });

    if (updatedResponse.finished) {
      // send response to pipeline
      // don't await to not block the response
      sendToPipeline({
        event: "responseFinished",
        environmentId: survey.environmentId,
        surveyId: survey.id,
        response: responseData,
      });
    }

    const quotaObj = createQuotaFullObject(quotaFull);

    const responseDataWithQuota = {
      id: responseData.id,
      ...quotaObj,
    };

    return {
      response: responses.successResponse(responseDataWithQuota, true),
    };
  },
});
