/**
 * System Email Helpers
 *
 * One named function per transactional email the platform sends. Each maps to an
 * existing template and fills its `{{tokens}}`, so a caller writes a single line:
 *
 *     await sendPasswordResetEmail({ to, userName, resetLink });
 *
 * Every one routes through sendSystemEmail, which uses whatever SMTP provider
 * the Super Admin has configured. Switching provider therefore changes nothing
 * here — that is the point of the Email Configuration module.
 *
 * None of these throw: a failed notification must never break a signup, a
 * password reset, or a payment. Each returns a result the caller can inspect.
 */

import { sendSystemEmail, type SendEmailResult } from './transactionalMailer';
import { getPublicAppBaseUrl } from '../../config/appUrls';

/**
 * Links shared by the templates' footers. Overridable via environment so a
 * white-labelled deployment can point them elsewhere.
 *
 * The base comes from getPublicAppBaseUrl(), never from `FRONTEND_URL`
 * directly: every URL here is opened from a mail client on someone else's
 * machine, so a loopback origin would render the whole email unusable.
 */
function commonVariables(baseUrl?: string): Record<string, string> {
  // `baseUrl` lets a caller pin the whole email to the host the request came
  // from (see getRequestAppBaseUrl) so the footer cannot point somewhere else
  // than the button above it.
  const appUrl = baseUrl?.replace(/\/+$/, '') || getPublicAppBaseUrl();
  const supportEmail = process.env.SUPPORT_EMAIL || 'support@mengoengine.com';
  const social = (envKey: string, fallback: string): string =>
    process.env[envKey]?.trim() || fallback;

  return {
    app_url: appUrl,
    login_url: `${appUrl}/login`,
    dashboard_url: `${appUrl}/dashboard`,
    support_email: supportEmail,
    support_url: `${appUrl}/help-support`,
    contact_url: `${appUrl}/help-support`,
    docs_url: `${appUrl}/help-support`,
    faq_url: `${appUrl}/help-support`,
    privacy_url: `${appUrl}/privacy`,
    terms_url: `${appUrl}/terms`,
    // The footers link these icons. Unknown tokens render as an empty string,
    // which would leave `href=""` — a link back to the message itself.
    facebook_url: social('SOCIAL_FACEBOOK_URL', 'https://www.facebook.com/mengoengine'),
    linkedin_url: social('SOCIAL_LINKEDIN_URL', 'https://www.linkedin.com/company/mengoengine'),
    twitter_url: social('SOCIAL_TWITTER_URL', 'https://x.com/mengoengine'),
    youtube_url: social('SOCIAL_YOUTUBE_URL', 'https://www.youtube.com/@mengoengine'),
    instagram_url: social('SOCIAL_INSTAGRAM_URL', 'https://www.instagram.com/mengoengine'),
    year: String(new Date().getFullYear()),
  };
}

/** OTP templates render the code as six separate boxes plus the whole string. */
function otpDigits(code: string): Record<string, string> {
  const digits = String(code).padEnd(6, ' ').slice(0, 6).split('');
  return {
    otp_code: String(code),
    otp_1: digits[0]?.trim() || '',
    otp_2: digits[1]?.trim() || '',
    otp_3: digits[2]?.trim() || '',
    otp_4: digits[3]?.trim() || '',
    otp_5: digits[4]?.trim() || '',
    otp_6: digits[5]?.trim() || '',
  };
}

// ============================================
// Authentication
// ============================================

/** One-time passcode for login or step-up verification. */
export async function sendOtpEmail(params: {
  to: string;
  userName?: string;
  otpCode: string;
  expiryMinutes?: number;
}): Promise<SendEmailResult> {
  return sendSystemEmail({
    to: params.to,
    template: 'otp-verification',
    // No `subject` here on purpose: the subject comes from the configured
    // template (a static line). The OTP must appear ONLY in the email body,
    // never in the subject.
    variables: {
      ...commonVariables(),
      ...otpDigits(params.otpCode),
      user_name: params.userName || 'there',
      expiry_minutes: params.expiryMinutes ?? 10,
    },
  });
}

/** Confirm ownership of an email address at signup. */
export async function sendEmailVerification(params: {
  to: string;
  userName?: string;
  verificationLink: string;
}): Promise<SendEmailResult> {
  return sendSystemEmail({
    to: params.to,
    template: 'verify-email',
    variables: {
      ...commonVariables(),
      user_name: params.userName || 'there',
      verification_link: params.verificationLink,
      verify_email_link: params.verificationLink,
      verify_url: params.verificationLink,
    },
  });
}

/**
 * A URL that a mail client is allowed to wrap.
 *
 * A reset URL carries a 64-character hex token, so the whole thing is one
 * unbreakable "word" — and `word-break:break-all` is ignored by enough clients
 * that the long string forces the table wider than the 640px card and tears the
 * layout open. `<wbr>` is an explicit break opportunity honoured much more
 * widely, and unlike a zero-width space it inserts no character, so the URL
 * still copies and pastes intact.
 *
 * Used for the visible text only; the `href` always gets the untouched URL.
 */
function softWrapUrl(url: string): string {
  return url.replace(/(.{24})/g, '$1<wbr>');
}

/** Password reset link. */
export async function sendPasswordResetEmail(params: {
  to: string;
  userName?: string;
  resetLink: string;
  expiryMinutes?: number;
  /** Host to build the footer links from. Defaults to the configured public URL. */
  appBaseUrl?: string;
}): Promise<SendEmailResult> {
  const common = commonVariables(params.appBaseUrl);

  return sendSystemEmail({
    to: params.to,
    template: 'password-reset',
    variables: {
      ...common,
      user_name: params.userName || 'there',
      password_reset_link: params.resetLink,
      password_reset_link_display: softWrapUrl(params.resetLink),
      reset_link: params.resetLink,
      // The template's help links — without these the footer renders empty hrefs.
      password_support_url: common.support_url,
      reset_guide_url: common.support_url,
      expiry_minutes: params.expiryMinutes ?? 60,
    },
  });
}

/** Confirmation that a password was changed — a security notice. */
export async function sendPasswordChangedEmail(params: {
  to: string;
  userName?: string;
  changedAt?: Date;
}): Promise<SendEmailResult> {
  return sendSystemEmail({
    to: params.to,
    template: 'password-changed',
    variables: {
      ...commonVariables(),
      user_name: params.userName || 'there',
      changed_at: (params.changedAt || new Date()).toLocaleString(),
    },
  });
}

/**
 * Security notice about a change to someone's two-factor authentication.
 *
 * Sent when 2FA is switched on, and when a Super Admin resets it. Both are
 * changes to how an account is protected that the account holder must hear
 * about independently of whoever made the change — that is what makes an
 * unauthorised change noticeable.
 */
export async function sendTwoFactorStatusEmail(params: {
  to: string;
  kind: 'enabled' | 'reset';
  userName?: string;
  changedAt?: Date;
  /** Who performed the reset. Only meaningful for `kind: 'reset'`. */
  adminName?: string;
  recoveryCodeCount?: number;
}): Promise<SendEmailResult> {
  return sendSystemEmail({
    to: params.to,
    template: params.kind === 'reset' ? 'two-factor-reset' : 'two-factor-enabled',
    variables: {
      ...commonVariables(),
      user_name: params.userName || 'there',
      user_email: params.to,
      changed_at: (params.changedAt || new Date()).toLocaleString(),
      admin_name: params.adminName || 'An administrator',
      recovery_code_count: params.recoveryCodeCount ?? '',
    },
  });
}

// ============================================
// Onboarding
// ============================================

export async function sendWelcomeEmail(params: {
  to: string;
  userName?: string;
  companyName?: string;
}): Promise<SendEmailResult> {
  return sendSystemEmail({
    to: params.to,
    template: 'welcome',
    variables: {
      ...commonVariables(),
      user_name: params.userName || 'there',
      company_name: params.companyName || '',
    },
  });
}

export async function sendAccountCreatedEmail(params: {
  to: string;
  userName?: string;
  companyName?: string;
  temporaryPassword?: string;
  /** Role the account was given — the template's "Account type" row. */
  accountType?: string;
  /** Administrator who created it — the template's "Created by" row. */
  createdBy?: string;
  /** When it was created. Defaults to now. */
  createdAt?: Date;
}): Promise<SendEmailResult> {
  const common = commonVariables();
  const createdAt = params.createdAt || new Date();

  return sendSystemEmail({
    to: params.to,
    template: 'account-created',
    variables: {
      ...common,
      user_name: params.userName || 'there',
      company_name: params.companyName || '',
      temporary_password: params.temporaryPassword || '',
      user_email: params.to,
      // The shipped account-created.html asks for these by name. Unknown tokens
      // render as an empty string, so without them the details table in the
      // email arrives blank.
      email: params.to,
      workspace: params.companyName || '',
      account_type: params.accountType || '',
      created_by: params.createdBy || '',
      registration_date: createdAt.toLocaleDateString(),
      registration_time: createdAt.toLocaleTimeString(),
      dashboard_link: common.dashboard_url,
    },
  });
}

/** Invite someone to a workspace/team. */
export async function sendInvitationEmail(params: {
  to: string;
  inviterName?: string;
  workspaceName?: string;
  invitationLink: string;
  /** Which invitation template to use. */
  kind?: 'team' | 'workspace' | 'admin';
}): Promise<SendEmailResult> {
  const template =
    params.kind === 'admin' ? 'admin-invitation'
      : params.kind === 'workspace' ? 'workspace-invitation'
        : 'team-invitation';

  return sendSystemEmail({
    to: params.to,
    template,
    variables: {
      ...commonVariables(),
      inviter_name: params.inviterName || 'A teammate',
      workspace_name: params.workspaceName || '',
      company_name: params.workspaceName || '',
      invitation_link: params.invitationLink,
      invite_link: params.invitationLink,
      accept_url: params.invitationLink,
    },
  });
}

// ============================================
// Billing
// ============================================

export async function sendPaymentSuccessEmail(params: {
  to: string;
  userName?: string;
  amount: string;
  planName?: string;
  invoiceUrl?: string;
}): Promise<SendEmailResult> {
  return sendSystemEmail({
    to: params.to,
    template: 'payment-success',
    variables: {
      ...commonVariables(),
      user_name: params.userName || 'there',
      amount: params.amount,
      plan_name: params.planName || '',
      invoice_url: params.invoiceUrl || '',
    },
  });
}

export async function sendPaymentFailedEmail(params: {
  to: string;
  userName?: string;
  amount?: string;
  planName?: string;
  retryUrl?: string;
}): Promise<SendEmailResult> {
  return sendSystemEmail({
    to: params.to,
    template: 'payment-failed',
    variables: {
      ...commonVariables(),
      user_name: params.userName || 'there',
      amount: params.amount || '',
      plan_name: params.planName || '',
      retry_url: params.retryUrl || `${commonVariables().app_url}/subscription`,
    },
  });
}

export async function sendSubscriptionExpiringEmail(params: {
  to: string;
  userName?: string;
  planName?: string;
  expiryDate: string;
  daysRemaining?: number;
}): Promise<SendEmailResult> {
  return sendSystemEmail({
    to: params.to,
    template: 'subscription-expiring',
    variables: {
      ...commonVariables(),
      user_name: params.userName || 'there',
      plan_name: params.planName || '',
      expiry_date: params.expiryDate,
      days_remaining: params.daysRemaining ?? '',
      renew_url: `${commonVariables().app_url}/subscription`,
    },
  });
}

export async function sendTrialEndingEmail(params: {
  to: string;
  userName?: string;
  daysRemaining?: number;
  endDate?: string;
}): Promise<SendEmailResult> {
  return sendSystemEmail({
    to: params.to,
    template: 'trial-ending',
    variables: {
      ...commonVariables(),
      user_name: params.userName || 'there',
      days_remaining: params.daysRemaining ?? '',
      end_date: params.endDate || '',
      upgrade_url: `${commonVariables().app_url}/subscription/plans`,
    },
  });
}

export async function sendInvoiceReadyEmail(params: {
  to: string;
  userName?: string;
  invoiceNumber: string;
  amount: string;
  invoiceUrl?: string;
}): Promise<SendEmailResult> {
  return sendSystemEmail({
    to: params.to,
    template: 'invoice-ready',
    variables: {
      ...commonVariables(),
      user_name: params.userName || 'there',
      invoice_number: params.invoiceNumber,
      amount: params.amount,
      invoice_url: params.invoiceUrl || '',
    },
  });
}

// ============================================
// Notifications
// ============================================

/**
 * Generic notification — for anything without a dedicated template.
 * `template` accepts any system template slug.
 */
export async function sendNotificationEmail(params: {
  to: string | string[];
  template: string;
  subject?: string;
  variables?: Record<string, string | number | undefined>;
}): Promise<SendEmailResult> {
  return sendSystemEmail({
    to: params.to,
    template: params.template,
    subject: params.subject,
    variables: { ...commonVariables(), ...(params.variables || {}) },
  });
}

// ============================================
// Referral Tracking Emails
// ============================================

/**
 * Send a referral invitation email to a potential referred person.
 */
export async function sendReferralInvitationEmail(params: {
  to: string;
  referrerName: string;
  referralLink: string;
  referralCode: string;
  companyName?: string;
}): Promise<SendEmailResult> {
  return sendSystemEmail({
    to: params.to,
    template: 'referral-invitation',
    subject: `${params.referrerName} has invited you to join${params.companyName ? ' ' + params.companyName : ''}`,
    variables: {
      ...commonVariables(),
      referrer_name: params.referrerName,
      referral_link: params.referralLink,
      referral_code: params.referralCode,
      company_name: params.companyName || '',
    },
  });
}

/**
 * Send a reminder email to someone who was referred but hasn't registered yet.
 */
export async function sendReferralReminderEmail(params: {
  to: string;
  referrerName: string;
  referralLink: string;
  referralCode: string;
  reminderNumber: number;
  companyName?: string;
}): Promise<SendEmailResult> {
  return sendSystemEmail({
    to: params.to,
    template: 'referral-reminder',
    subject: `Reminder: ${params.referrerName} invited you to join${params.companyName ? ' ' + params.companyName : ''}`,
    variables: {
      ...commonVariables(),
      referrer_name: params.referrerName,
      referral_link: params.referralLink,
      referral_code: params.referralCode,
      reminder_number: String(params.reminderNumber),
      company_name: params.companyName || '',
    },
  });
}

/**
 * Send a notification to the referrer when their referral registers, purchases, or earns a reward.
 */
export async function sendReferralNotificationEmail(params: {
  to: string | string[];
  eventType: 'registration' | 'subscription_purchase' | 'reward_earned';
  referredName: string;
  details?: string;
}): Promise<SendEmailResult> {
  const subjectMap: Record<string, string> = {
    registration: `${params.referredName} has registered!`,
    subscription_purchase: `${params.referredName} has subscribed!`,
    reward_earned: `You earned a referral reward!`,
  };

  return sendSystemEmail({
    to: params.to,
    template: 'referral-notification',
    subject: subjectMap[params.eventType] || 'Referral update',
    variables: {
      ...commonVariables(),
      event_type: params.eventType,
      referred_name: params.referredName,
      details: params.details || '',
    },
  });
}

export { sendSystemEmail } from './transactionalMailer';
