import jwt from "jsonwebtoken";
import { logger } from "@formbricks/logger";
import { Result, err, ok } from "@formbricks/types/error-handlers";
import { ENCRYPTION_KEY } from "@/lib/constants";
import { symmetricDecrypt, symmetricEncrypt } from "@/lib/crypto";
import { getPublicDomain } from "@/lib/getPublicUrl";
import { generateSurveySingleUseId } from "@/lib/utils/single-use-surveys";
import { ApiErrorResponseV2 } from "@/modules/api/v2/types/api-error";
import { getSurvey } from "@/modules/survey/lib/survey";

// Creates an encrypted personalized survey link for a contact
export const getContactSurveyLink = async (
  contactId: string,
  surveyId: string,
  expirationDays?: number
): Promise<Result<string, ApiErrorResponseV2>> => {
  if (!ENCRYPTION_KEY) {
    return err({
      type: "internal_server_error",
      message: "Encryption key not found - cannot create personalized survey link",
    });
  }

  const survey = await getSurvey(surveyId);
  if (!survey) {
    return err({
      type: "not_found",
      message: "Survey not found",
      details: [{ field: "surveyId", issue: "not_found" }],
    });
  }

  const { enabled: isSingleUseEnabled, isEncrypted: isSingleUseEncrypted } = survey.singleUse ?? {};

  // Encrypt the contact and survey IDs
  const encryptedContactId = symmetricEncrypt(contactId, ENCRYPTION_KEY);
  const encryptedSurveyId = symmetricEncrypt(surveyId, ENCRYPTION_KEY);

  let singleUseId: string | undefined;

  if (isSingleUseEnabled) {
    singleUseId = generateSurveySingleUseId(isSingleUseEncrypted ?? false);
  }

  // Create JWT payload with encrypted IDs
  const payload = {
    contactId: encryptedContactId,
    surveyId: encryptedSurveyId,
  };

  // Set token options
  const tokenOptions: jwt.SignOptions = {
    algorithm: "HS256",
  };

  // Add expiration if specified
  if (expirationDays !== undefined && expirationDays > 0) {
    tokenOptions.expiresIn = `${expirationDays}d`;
  }

  // Sign the token with ENCRYPTION_KEY using SHA256
  const token = jwt.sign(payload, ENCRYPTION_KEY, tokenOptions);

  // Return the personalized URL
  return singleUseId
    ? ok(`${getPublicDomain()}/c/${token}?suId=${singleUseId}`)
    : ok(`${getPublicDomain()}/c/${token}`);
};

// Validates and decrypts a contact survey JWT token
export const verifyContactSurveyToken = (
  token: string
): Result<{ contactId: string; surveyId: string }, ApiErrorResponseV2> => {
  if (!ENCRYPTION_KEY) {
    return err({
      type: "internal_server_error",
      message: "Encryption key not found - cannot verify survey token",
    });
  }

  try {
    // Verify the token
    const decoded = jwt.verify(token, ENCRYPTION_KEY) as {
      contactId: string;
      surveyId: string;
    };

    if (!decoded || !decoded.contactId || !decoded.surveyId) {
      throw err("Invalid token format");
    }

    // Decrypt the contact and survey IDs
    const contactId = symmetricDecrypt(decoded.contactId, ENCRYPTION_KEY);
    const surveyId = symmetricDecrypt(decoded.surveyId, ENCRYPTION_KEY);

    return ok({
      contactId,
      surveyId,
    });
  } catch (error) {
    logger.error(
      error instanceof Error ? error : new Error(String(error)),
      "Error verifying contact survey token"
    );

    // Check if the error is specifically a JWT expiration error
    if (error instanceof jwt.TokenExpiredError) {
      return err({
        type: "bad_request",
        message: "Survey link has expired",
        details: [{ field: "token", issue: "token_expired" }],
      });
    }

    // Handle other JWT errors or general validation errors
    return err({
      type: "bad_request",
      message: "Invalid survey token",
      details: [{ field: "token", issue: "invalid_token" }],
    });
  }
};
