import { createTransport } from "nodemailer";
import type SMTPTransport from "nodemailer/lib/smtp-transport";
import {
  renderEmailCustomizationPreviewEmail,
  renderEmbedSurveyPreviewEmail,
  renderForgotPasswordEmail,
  renderInviteAcceptedEmail,
  renderInviteEmail,
  renderLinkSurveyEmail,
  renderNewEmailVerification,
  renderPasswordResetNotifyEmail,
  renderResponseFinishedEmail,
  renderVerificationEmail,
} from "@formbricks/email";
import { TEmailTemplateLegalProps } from "@formbricks/email/src/types/email";
import { logger } from "@formbricks/logger";
import type { TLinkSurveyEmailData } from "@formbricks/types/email";
import { InvalidInputError, ResourceNotFoundError } from "@formbricks/types/errors";
import type { TResponse } from "@formbricks/types/responses";
import { TSurveyElementTypeEnum } from "@formbricks/types/surveys/elements";
import type { TSurvey } from "@formbricks/types/surveys/types";
import { TUserEmail, TUserLocale } from "@formbricks/types/user";
import {
  DEBUG,
  IMPRINT_ADDRESS,
  IMPRINT_URL,
  MAIL_FROM,
  MAIL_FROM_NAME,
  PRIVACY_URL,
  SMTP_AUTHENTICATED,
  SMTP_HOST,
  SMTP_PASSWORD,
  SMTP_PORT,
  SMTP_REJECT_UNAUTHORIZED_TLS,
  SMTP_SECURE_ENABLED,
  SMTP_USER,
  TERMS_URL,
  WEBAPP_URL,
} from "@/lib/constants";
import { getPublicDomain } from "@/lib/getPublicUrl";
import { createEmailChangeToken, createInviteToken, createToken, createTokenForLinkSurvey } from "@/lib/jwt";
import { getOrganizationByEnvironmentId } from "@/lib/organization/service";
import { getElementResponseMapping } from "@/lib/responses";
import { getTranslate } from "@/lingodotdev/server";
import { buildVerificationLinks } from "@/modules/auth/lib/verification-links";
import { resolveStorageUrl } from "@/modules/storage/utils";

export const IS_SMTP_CONFIGURED = Boolean(SMTP_HOST && SMTP_PORT);

const legalProps: TEmailTemplateLegalProps = {
  privacyUrl: PRIVACY_URL || undefined,
  termsUrl: TERMS_URL || undefined,
  imprintUrl: IMPRINT_URL || undefined,
  imprintAddress: IMPRINT_ADDRESS || undefined,
};

interface SendEmailDataProps {
  to: string;
  replyTo?: string;
  subject: string;
  text?: string;
  html: string;
}

export const sendEmail = async (emailData: SendEmailDataProps): Promise<boolean> => {
  if (!IS_SMTP_CONFIGURED) {
    logger.info("SMTP is not configured, skipping email sending");
    return false;
  }
  try {
    const transporter = createTransport({
      host: SMTP_HOST,
      port: SMTP_PORT,
      secure: SMTP_SECURE_ENABLED, // true for 465, false for other ports
      ...(SMTP_AUTHENTICATED
        ? {
            auth: {
              type: "LOGIN",
              user: SMTP_USER,
              pass: SMTP_PASSWORD,
            },
          }
        : {}),
      tls: {
        rejectUnauthorized: SMTP_REJECT_UNAUTHORIZED_TLS,
      },
      logger: DEBUG,
      debug: DEBUG,
    } as SMTPTransport.Options);

    const emailDefaults = {
      from: `${MAIL_FROM_NAME ?? "Formbricks"} <${MAIL_FROM ?? "noreply@formbricks.com"}>`,
    };
    await transporter.sendMail({ ...emailDefaults, ...emailData });

    return true;
  } catch (error) {
    logger.error(error, "Error in sendEmail");
    throw new InvalidInputError("Incorrect SMTP credentials");
  }
};

export const sendVerificationNewEmail = async (
  id: string,
  email: string,
  locale: TUserLocale
): Promise<boolean> => {
  try {
    const t = await getTranslate(locale);
    const token = createEmailChangeToken(id, email);
    const verifyLink = `${WEBAPP_URL}/verify-email-change?token=${encodeURIComponent(token)}`;

    const html = await renderNewEmailVerification({ verifyLink, t, ...legalProps });

    return await sendEmail({
      to: email,
      subject: t("emails.verification_new_email_subject"),
      html,
    });
  } catch (error) {
    logger.error(error, "Error in sendVerificationNewEmail");
    throw error;
  }
};

export const sendVerificationEmail = async ({
  id,
  email,
  locale,
  callbackUrl,
}: {
  id: string;
  email: TUserEmail;
  locale: TUserLocale;
  callbackUrl?: string;
}): Promise<boolean> => {
  try {
    const t = await getTranslate(locale);
    const token = createToken(id, {
      expiresIn: "1d",
    });
    const { verifyLink, verificationRequestLink } = buildVerificationLinks({
      token,
      webAppUrl: WEBAPP_URL,
      callbackUrl,
    });

    const html = await renderVerificationEmail({
      verificationRequestLink,
      verifyLink,
      t,
      ...legalProps,
    });

    return await sendEmail({
      to: email,
      subject: t("emails.verification_email_subject"),
      html,
    });
  } catch (error) {
    logger.error(error, "Error in sendVerificationEmail");
    throw error; // Re-throw the error to maintain the original behavior
  }
};

export const sendPasswordResetLinkEmail = async (user: {
  email: TUserEmail;
  locale: TUserLocale;
  verifyLink: string;
  linkValidityInMinutes: number;
}): Promise<boolean> => {
  const t = await getTranslate(user.locale);
  const html = await renderForgotPasswordEmail({
    verifyLink: user.verifyLink,
    linkValidityInMinutes: user.linkValidityInMinutes,
    t,
    ...legalProps,
  });
  return await sendEmail({
    to: user.email,
    subject: t("emails.forgot_password_email_subject"),
    html,
  });
};

export const sendPasswordResetNotifyEmail = async (user: {
  email: string;
  locale: TUserLocale;
}): Promise<boolean> => {
  const t = await getTranslate(user.locale);
  const html = await renderPasswordResetNotifyEmail({ t, ...legalProps });
  return await sendEmail({
    to: user.email,
    subject: t("emails.password_reset_notify_email_subject"),
    html,
  });
};

export const sendInviteMemberEmail = async (
  inviteId: string,
  email: string,
  inviterName: string,
  inviteeName: string
): Promise<boolean> => {
  const token = createInviteToken(inviteId, email, {
    expiresIn: "7d",
  });
  const t = await getTranslate();

  const verifyLink = `${WEBAPP_URL}/invite?token=${encodeURIComponent(token)}`;

  const html = await renderInviteEmail({ inviteeName, inviterName, verifyLink, t, ...legalProps });
  return await sendEmail({
    to: email,
    subject: t("emails.invite_member_email_subject"),
    html,
  });
};

export const sendInviteAcceptedEmail = async (
  inviterName: string,
  inviteeName: string,
  email: string,
  inviterLocale?: TUserLocale
): Promise<void> => {
  const t = await getTranslate(inviterLocale);
  const html = await renderInviteAcceptedEmail({ inviteeName, inviterName, t, ...legalProps });
  await sendEmail({
    to: email,
    subject: t("emails.invite_accepted_email_subject"),
    html,
  });
};

export const sendResponseFinishedEmail = async (
  email: string,
  locale: TUserLocale,
  environmentId: string,
  survey: TSurvey,
  response: TResponse,
  responseCount: number
): Promise<void> => {
  const t = await getTranslate(locale);
  const personEmail = response.contactAttributes?.email;
  const organization = await getOrganizationByEnvironmentId(environmentId);

  if (!organization) {
    throw new ResourceNotFoundError("Organization", null);
  }

  // Pre-process the element response mapping before passing to email
  const elements = getElementResponseMapping(survey, response);

  // Resolve relative storage URLs to absolute URLs for email rendering
  const elementsWithResolvedUrls = elements.map((element) => {
    if (
      (element.type === TSurveyElementTypeEnum.PictureSelection ||
        element.type === TSurveyElementTypeEnum.FileUpload) &&
      Array.isArray(element.response)
    ) {
      return {
        ...element,
        response: element.response.map((url) => resolveStorageUrl(url)),
      };
    }

    return element;
  });

  const html = await renderResponseFinishedEmail({
    survey,
    responseCount,
    response,
    WEBAPP_URL,
    environmentId,
    organization,
    elements: elementsWithResolvedUrls,
    t,
    ...legalProps,
  });

  await sendEmail({
    to: email,
    subject: personEmail
      ? t("emails.response_finished_email_subject_with_email", {
          personEmail,
          surveyName: survey.name,
        })
      : t("emails.response_finished_email_subject", {
          surveyName: survey.name,
        }),
    replyTo: personEmail?.toString() ?? MAIL_FROM,
    html,
  });
};

export const sendEmbedSurveyPreviewEmail = async (
  to: string,
  innerHtml: string,
  environmentId: string,
  locale: TUserLocale,
  logoUrl?: string
): Promise<boolean> => {
  const t = await getTranslate(locale);
  // Resolve relative storage URLs to absolute URLs for email rendering
  const resolvedLogoUrl = logoUrl ? resolveStorageUrl(logoUrl) : undefined;
  const html = await renderEmbedSurveyPreviewEmail({
    html: innerHtml,
    environmentId,
    logoUrl: resolvedLogoUrl,
    t,
    ...legalProps,
  });
  return await sendEmail({
    to,
    subject: t("emails.embed_survey_preview_email_subject"),
    html,
  });
};

export const sendEmailCustomizationPreviewEmail = async (
  to: string,
  userName: string,
  locale: TUserLocale,
  logoUrl?: string
): Promise<boolean> => {
  const t = await getTranslate(locale);
  // Resolve relative storage URLs to absolute URLs for email rendering
  const resolvedLogoUrl = logoUrl ? resolveStorageUrl(logoUrl) : undefined;
  const emailHtmlBody = await renderEmailCustomizationPreviewEmail({
    userName,
    logoUrl: resolvedLogoUrl,
    t,
    ...legalProps,
  });

  return await sendEmail({
    to,
    subject: t("emails.email_customization_preview_email_subject"),
    html: emailHtmlBody,
  });
};

export const sendLinkSurveyToVerifiedEmail = async (data: TLinkSurveyEmailData): Promise<boolean> => {
  const surveyId = data.surveyId;
  const email = data.email;
  const surveyName = data.surveyName;
  const singleUseId = data.suId;
  // Resolve relative storage URLs to absolute URLs for email rendering
  const logoUrl = data.logoUrl ? resolveStorageUrl(data.logoUrl) : "";
  const token = createTokenForLinkSurvey(surveyId, email);
  const t = await getTranslate(data.locale);
  const getSurveyLink = (): string => {
    if (singleUseId) {
      return `${getPublicDomain()}/s/${surveyId}?verify=${encodeURIComponent(token)}&suId=${singleUseId}`;
    }
    return `${getPublicDomain()}/s/${surveyId}?verify=${encodeURIComponent(token)}`;
  };
  const surveyLink = getSurveyLink();

  const html = await renderLinkSurveyEmail({ surveyName, surveyLink, logoUrl, t, ...legalProps });
  return await sendEmail({
    to: data.email,
    subject: t("emails.verified_link_survey_email_subject"),
    html,
  });
};
