import nodemailer from 'nodemailer'
import type { Transporter } from 'nodemailer'
import type { Payload } from 'payload'
import type { Tenant } from '../../payload-types'

export interface EmailOptions {
  to: string | string[]
  subject: string
  html?: string
  text?: string
  replyTo?: string
  attachments?: Array<{
    filename: string
    content: Buffer | string
    contentType?: string
  }>
}

export interface SendEmailResult {
  success: boolean
  messageId?: string
  error?: string
  logId?: number
}

export type EmailSource = 'manual' | 'form' | 'system' | 'newsletter'

interface SendEmailOptions extends EmailOptions {
  source?: EmailSource
  metadata?: Record<string, unknown>
}

// Cache für SMTP-Transporter
const transporterCache = new Map<string, Transporter>()

/**
 * Prüft ob der SMTP-Versand übersprungen werden soll
 * - wird automatisch in Test-Umgebungen deaktiviert
 * - kann via EMAIL_DELIVERY_DISABLED explizit deaktiviert werden
 */
function isEmailDeliveryDisabled(): boolean {
  return process.env.NODE_ENV === 'test' || process.env.EMAIL_DELIVERY_DISABLED === 'true'
}

/**
 * Globaler Fallback-Transporter aus .env Variablen
 */
function getGlobalTransporter(): Transporter {
  const cacheKey = 'global'

  if (!transporterCache.has(cacheKey)) {
    const transporter = nodemailer.createTransport({
      host: process.env.SMTP_HOST,
      port: parseInt(process.env.SMTP_PORT || '587'),
      secure: process.env.SMTP_SECURE === 'true',
      auth: {
        user: process.env.SMTP_USER,
        pass: process.env.SMTP_PASS,
      },
    })
    transporterCache.set(cacheKey, transporter)
  }

  return transporterCache.get(cacheKey)!
}

/**
 * Tenant-spezifischer Transporter (falls eigener SMTP konfiguriert)
 */
function getTenantTransporter(tenant: Tenant): Transporter {
  const smtp = tenant.email?.smtp

  if (!smtp?.host || !tenant.email?.useCustomSmtp) {
    return getGlobalTransporter()
  }

  const cacheKey = `tenant:${tenant.id}`

  if (!transporterCache.has(cacheKey)) {
    const transporter = nodemailer.createTransport({
      host: smtp.host,
      port: smtp.port || 587,
      secure: smtp.secure || false,
      auth: {
        user: smtp.user || '',
        pass: smtp.pass || '',
      },
    })
    transporterCache.set(cacheKey, transporter)
  }

  return transporterCache.get(cacheKey)!
}

/**
 * Cache invalidieren wenn Tenant-E-Mail-Config geändert wird
 */
export function invalidateTenantEmailCache(tenantId: string | number): void {
  transporterCache.delete(`tenant:${tenantId}`)
}

/**
 * Globalen Cache invalidieren
 */
export function invalidateGlobalEmailCache(): void {
  transporterCache.delete('global')
}

/**
 * E-Mail-Log erstellen
 */
async function createEmailLog(
  payload: Payload,
  tenantId: string | number,
  data: {
    to: string
    from: string
    subject: string
    status: 'pending' | 'sent' | 'failed'
    messageId?: string
    error?: string
    source: EmailSource
    metadata?: Record<string, unknown>
  },
): Promise<number | null> {
  try {
    const log = await payload.create({
      collection: 'email-logs',
      data: {
        tenant: Number(tenantId),
        to: data.to,
        from: data.from,
        subject: data.subject,
        status: data.status,
        messageId: data.messageId,
        error: data.error,
        source: data.source,
        metadata: data.metadata,
      },
      overrideAccess: true,
    })
    return log.id
  } catch (error) {
    console.error('[EmailLog] Failed to create log:', error)
    return null
  }
}

/**
 * E-Mail-Log aktualisieren
 */
async function updateEmailLog(
  payload: Payload,
  logId: number,
  data: {
    status: 'sent' | 'failed'
    messageId?: string
    error?: string
  },
): Promise<void> {
  try {
    await payload.update({
      collection: 'email-logs',
      id: logId,
      data,
      overrideAccess: true,
    })
  } catch (error) {
    console.error('[EmailLog] Failed to update log:', error)
  }
}

/**
 * Haupt-Funktion: E-Mail für einen Tenant senden mit Logging
 *
 * @param payload - Payload Instanz
 * @param tenantId - ID des Tenants
 * @param options - E-Mail-Optionen (to, subject, html/text, etc.)
 */
export async function sendTenantEmail(
  payload: Payload,
  tenantId: string | number,
  options: SendEmailOptions,
): Promise<SendEmailResult> {
  const source = options.source || 'manual'
  let logId: number | null = null

  try {
    // Tenant laden mit Admin-Zugriff (für SMTP-Pass)
    const tenant = (await payload.findByID({
      collection: 'tenants',
      id: tenantId,
      depth: 0,
      overrideAccess: true,
    })) as Tenant

    if (!tenant) {
      throw new Error(`Tenant ${tenantId} nicht gefunden`)
    }

    // E-Mail-Konfiguration mit Fallbacks
    const fromAddress =
      tenant.email?.fromAddress || process.env.SMTP_FROM_ADDRESS || 'noreply@c2sgmbh.de'
    const fromName = tenant.email?.fromName || tenant.name || 'Payload CMS'
    const from = `"${fromName}" <${fromAddress}>`
    const replyTo = options.replyTo || tenant.email?.replyTo || fromAddress
    const toAddress = Array.isArray(options.to) ? options.to.join(', ') : options.to

    // Log erstellen (status: pending)
    logId = await createEmailLog(payload, tenantId, {
      to: toAddress,
      from,
      subject: options.subject,
      status: 'pending',
      source,
      metadata: options.metadata,
    })

    // E-Mail-Versand in Tests oder wenn explizit deaktiviert: sofort Erfolg melden
    if (isEmailDeliveryDisabled()) {
      const mockMessageId = `test-message-${Date.now()}`
      if (logId) {
        await updateEmailLog(payload, logId, {
          status: 'sent',
          messageId: mockMessageId,
        })
      }

      console.info('[Email] Delivery disabled - skipping SMTP send')
      return {
        success: true,
        messageId: mockMessageId,
        logId: logId || undefined,
      }
    }

    // Transporter wählen (Tenant-spezifisch oder global)
    const transporter = getTenantTransporter(tenant)

    // E-Mail senden
    const result = await transporter.sendMail({
      from,
      to: toAddress,
      replyTo,
      subject: options.subject,
      html: options.html,
      text: options.text,
      attachments: options.attachments,
    })

    // Log aktualisieren (status: sent)
    if (logId) {
      await updateEmailLog(payload, logId, {
        status: 'sent',
        messageId: result.messageId,
      })
    }

    console.log(`[Email] Sent to ${options.to} for tenant ${tenant.slug}: ${result.messageId}`)

    return { success: true, messageId: result.messageId, logId: logId || undefined }
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : 'Unknown error'

    // Log aktualisieren (status: failed)
    if (logId) {
      try {
        await updateEmailLog(payload, logId, {
          status: 'failed',
          error: errorMessage,
        })
      } catch (logError) {
        console.error(`[Email] Failed to update email log ${logId}:`, logError)
      }
    }

    console.error(`[Email] Error for tenant ${tenantId}:`, error)
    return {
      success: false,
      error: errorMessage,
      logId: logId || undefined,
    }
  }
}

/**
 * Tenant aus Request ermitteln (via Header oder Host)
 */
export async function getTenantFromRequest(
  payload: Payload,
  req: Request,
): Promise<Tenant | null> {
  // Aus X-Tenant-Slug Header
  const tenantSlug = req.headers.get('x-tenant-slug')
  if (tenantSlug) {
    const result = await payload.find({
      collection: 'tenants',
      where: { slug: { equals: tenantSlug } },
      limit: 1,
    })
    return (result.docs[0] as Tenant) || null
  }

  // Aus Host-Header (Domain)
  const host = req.headers.get('host')?.replace(/:\d+$/, '')
  if (host) {
    const result = await payload.find({
      collection: 'tenants',
      where: { 'domains.domain': { equals: host } },
      limit: 1,
    })
    return (result.docs[0] as Tenant) || null
  }

  return null
}

/**
 * Test-E-Mail senden um SMTP-Konfiguration zu verifizieren
 */
export async function sendTestEmail(
  payload: Payload,
  tenantId: string | number,
  recipientEmail: string,
): Promise<SendEmailResult> {
  return sendTenantEmail(payload, tenantId, {
    to: recipientEmail,
    subject: 'Test E-Mail - SMTP Konfiguration',
    html: `
      <h2>SMTP-Konfiguration erfolgreich!</h2>
      <p>Diese Test-E-Mail bestätigt, dass die E-Mail-Konfiguration für diesen Tenant funktioniert.</p>
      <p><small>Gesendet am ${new Date().toLocaleString('de-DE')}</small></p>
    `,
    text: `SMTP-Konfiguration erfolgreich!\n\nDiese Test-E-Mail bestätigt, dass die E-Mail-Konfiguration für diesen Tenant funktioniert.\n\nGesendet am ${new Date().toLocaleString('de-DE')}`,
    source: 'manual',
    metadata: { type: 'test' },
  })
}
