// tdd-guard:skip — Thin wrapper around the email_queue table; behaviour
// covered by the integration of email_queue plus the (sanitised) log line.
//
// Server-only — never import this in client components
import { createAdminClient } from '@/lib/supabase/admin'
import { logger } from '@/lib/logger'

type NotificationTemplate =
  | 'account_created'
  | 'password_changed'
  | 'email_changed'
  | 'account_deactivated'
  | 'password_reset'

// KAR-778 / R22: link-bearing templates carry a single-use recovery/magic
// link. Enqueuing the same one twice (e.g. a double-clicked "Einladung
// senden") would send the recipient two mails whose links invalidate each
// other on redemption. For these the enqueue is idempotent: if a pending row
// for the same recipient+template already exists, the second call is a no-op.
// Informational mails (password_changed, email_changed, account_deactivated)
// are NOT deduped — each event should notify.
const IDEMPOTENT_TEMPLATES: ReadonlySet<NotificationTemplate> = new Set([
  'account_created',
  'password_reset',
])

export async function queueEmail(
  toEmail: string,
  template: NotificationTemplate,
  templateData: Record<string, string>
): Promise<void> {
  // KAR-535: do not log the raw email. ASVS V13.9.1 + V14.x require PII
  // to stay out of log records. The domain (post-@) is enough to debug
  // delivery without identifying the user.
  const emailDomain = toEmail.split('@')[1] ?? 'unknown'
  const admin = createAdminClient()

  // KAR-778 / R22: idempotency guard for link-bearing templates. A pending
  // row for the same recipient+template means an unsent invite/reset is
  // already queued — re-enqueuing would duplicate it, so skip.
  if (IDEMPOTENT_TEMPLATES.has(template)) {
    const { data: existing, error: lookupError } = await admin
      .from('email_queue')
      .select('id')
      .eq('to_email', toEmail)
      .eq('template', template)
      .eq('status', 'pending')
      .limit(1)
      .maybeSingle()
    if (lookupError) {
      logger.error('email.queue_lookup_failed', lookupError, { template, to_email_domain: emailDomain })
      throw new Error(`Failed to check email queue (${template})`)
    }
    if (existing) {
      logger.debug('email.queue_idempotent_skip', { template, to_email_domain: emailDomain })
      return
    }
  }

  logger.debug('email.queued', { template, to_email_domain: emailDomain })
  // KAR-769 review (Finding 4): surface queue-insert failures instead of
  // silently succeeding — otherwise an invite/reset route returns { ok: true }
  // and writes an audit log while no email was ever queued.
  const { error } = await admin.from('email_queue').insert({
    to_email:      toEmail,
    template,
    template_data: templateData,
  })
  if (error) {
    // KAR-789: the partial UNIQUE index (idempotent templates, pending) closes
    // the TOCTOU race that the check-then-insert above leaves open. If a
    // concurrent enqueue won, our insert hits a unique violation (23505) — that
    // means the pending row already exists, so this is an idempotent no-op, not
    // a failure.
    if (IDEMPOTENT_TEMPLATES.has(template) && (error as { code?: string }).code === '23505') {
      logger.debug('email.queue_idempotent_skip', { template, to_email_domain: emailDomain })
      return
    }
    logger.error('email.queue_insert_failed', error, { template, to_email_domain: emailDomain })
    throw new Error(`Failed to queue email (${template})`)
  }
}

export async function notifyInvitation(
  toEmail: string,
  inviteLink: string
): Promise<void> {
  // KAR-769 / D3: never send a cleartext password. We send a single-use
  // recovery/magic link; the user sets their own password on first login.
  // `message` carries the full self-contained text incl. the link so the email
  // renders correctly even before the external mail worker template is updated.
  await queueEmail(toEmail, 'account_created', {
    email:   toEmail,
    link:    inviteLink,
    message: `Sie wurden zu SupplierPulse eingeladen. Bitte öffnen Sie folgenden Link, um Ihr persönliches Passwort festzulegen und sich anzumelden: ${inviteLink} — Der Link ist nur einmal gültig.`,
  })
}

export async function notifyPasswordChanged(toEmail: string): Promise<void> {
  await queueEmail(toEmail, 'password_changed', {
    message: 'Ihr Passwort wurde erfolgreich geändert.',
  })
}

export async function notifyEmailChanged(
  toEmail: string,
  oldEmail: string,
  newEmail: string
): Promise<void> {
  await queueEmail(toEmail, 'email_changed', {
    old:     oldEmail,
    new:     newEmail,
    message: `Ihre E-Mail-Adresse wurde von ${oldEmail} auf ${newEmail} geändert.`,
  })
}

export async function notifyAccountDeactivated(toEmail: string): Promise<void> {
  await queueEmail(toEmail, 'account_deactivated', {
    message: 'Ihr Konto wurde deaktiviert. Kontaktieren Sie Ihren Administrator.',
  })
}

export async function notifyPasswordReset(
  toEmail: string,
  resetLink: string
): Promise<void> {
  // KAR-769 / D3: send a single-use recovery link instead of a temporary
  // cleartext password. `message` is self-contained (see notifyInvitation).
  await queueEmail(toEmail, 'password_reset', {
    link:    resetLink,
    message: `Ein Passwort-Reset wurde für Ihr Konto ausgelöst. Bitte setzen Sie über folgenden Link ein neues Passwort: ${resetLink} — Der Link ist nur einmal gültig.`,
  })
}
