import { prisma } from '@/lib/prisma'
import { emailService } from './email-service'
import type { DetectedError, ErrorDetectionRule, ContainerEvent, NotificationSetting } from '@prisma/client'

// ============================================================================
// Types
// ============================================================================

export interface NotificationResult {
  sent: boolean
  reason?: string
}

interface DetectedErrorWithRule extends DetectedError {
  rule: ErrorDetectionRule | null
  server: { nickname: string | null; netcupServerId: string } | null
}

interface ContainerEventWithServer extends ContainerEvent {
  server: { nickname: string | null; netcupServerId: string }
}

// ============================================================================
// Notification Service
// ============================================================================

class NotificationService {
  private static instance: NotificationService

  static getInstance(): NotificationService {
    if (!NotificationService.instance) {
      NotificationService.instance = new NotificationService()
    }
    return NotificationService.instance
  }

  // --------------------------------------------------------------------------
  // Notification Settings
  // --------------------------------------------------------------------------

  /**
   * Get or create notification settings for a client
   */
  async getNotificationSettings(clientId: string): Promise<NotificationSetting> {
    let settings = await prisma.notificationSetting.findUnique({
      where: { clientId },
    })

    if (!settings) {
      // Create default settings
      settings = await prisma.notificationSetting.create({
        data: {
          clientId,
          enabled: false,
          criticalErrorsOnly: true,
          containerCrashes: true,
          recipients: [],
          cooldownMinutes: 30,
        },
      })
    }

    return settings
  }

  /**
   * Update notification settings for a client
   */
  async updateNotificationSettings(
    clientId: string,
    data: Partial<{
      enabled: boolean
      criticalErrorsOnly: boolean
      containerCrashes: boolean
      recipients: string[]
      cooldownMinutes: number
    }>
  ): Promise<NotificationSetting> {
    // Ensure settings exist
    await this.getNotificationSettings(clientId)

    return prisma.notificationSetting.update({
      where: { clientId },
      data,
    })
  }

  // --------------------------------------------------------------------------
  // Cooldown Management
  // --------------------------------------------------------------------------

  /**
   * Check if notification should be sent based on cooldown
   */
  async shouldNotify(
    clientId: string,
    eventType: string,
    identifier: string
  ): Promise<boolean> {
    const settings = await this.getNotificationSettings(clientId)

    if (!settings.enabled || settings.recipients.length === 0) {
      return false
    }

    // Check last notification time
    if (settings.lastNotifiedAt) {
      const cooldownMs = settings.cooldownMinutes * 60 * 1000
      const elapsed = Date.now() - settings.lastNotifiedAt.getTime()
      if (elapsed < cooldownMs) {
        return false
      }
    }

    return true
  }

  /**
   * Update last notification time
   */
  private async updateLastNotified(clientId: string): Promise<void> {
    await prisma.notificationSetting.update({
      where: { clientId },
      data: { lastNotifiedAt: new Date() },
    })
  }

  // --------------------------------------------------------------------------
  // Critical Error Alerts
  // --------------------------------------------------------------------------

  /**
   * Send alert for critical error detection
   */
  async sendCriticalErrorAlert(
    clientId: string,
    error: DetectedErrorWithRule
  ): Promise<NotificationResult> {
    const settings = await this.getNotificationSettings(clientId)

    if (!settings.enabled) {
      return { sent: false, reason: 'Notifications disabled' }
    }

    if (settings.recipients.length === 0) {
      return { sent: false, reason: 'No recipients configured' }
    }

    // Check severity filter
    const severity = error.rule?.severity
    if (settings.criticalErrorsOnly && severity !== 'CRITICAL') {
      return { sent: false, reason: 'Only critical errors configured' }
    }

    // Check cooldown
    if (settings.lastNotifiedAt) {
      const cooldownMs = settings.cooldownMinutes * 60 * 1000
      const elapsed = Date.now() - settings.lastNotifiedAt.getTime()
      if (elapsed < cooldownMs) {
        return { sent: false, reason: 'Cooldown active' }
      }
    }

    // Get client name
    const client = await prisma.enterpriseClient.findUnique({
      where: { id: clientId },
      select: { name: true },
    })

    // Send email to all recipients
    const serverName = error.server?.nickname || error.server?.netcupServerId || 'Unknown'
    const ruleName = error.rule?.name || 'Unknown Rule'
    const subject = `[LetsBe Alert] Critical Error Detected - ${client?.name || 'Unknown Client'}`
    const body = this.buildErrorAlertEmail(
      client?.name || 'Unknown Client',
      serverName,
      ruleName,
      severity || 'WARNING',
      error.containerName || 'Unknown Container',
      error.logLine,
      error.timestamp
    )

    await this.sendToRecipients(settings.recipients, subject, body)
    await this.updateLastNotified(clientId)

    return { sent: true }
  }

  /**
   * Build error alert email body
   */
  private buildErrorAlertEmail(
    clientName: string,
    serverName: string,
    ruleName: string,
    severity: string,
    containerName: string,
    logLine: string,
    timestamp: Date
  ): string {
    return `
CRITICAL ERROR DETECTED

Client: ${clientName}
Server: ${serverName}
Container: ${containerName}
Rule: ${ruleName}
Severity: ${severity}
Time: ${timestamp.toISOString()}

Log Line:
${logLine}

---
Please investigate this issue in the LetsBe Hub dashboard.

If this is a false positive, consider adjusting the error detection rules.

---
LetsBe Cloud Platform
    `.trim()
  }

  // --------------------------------------------------------------------------
  // Container Crash Alerts
  // --------------------------------------------------------------------------

  /**
   * Send alert for container crash
   */
  async sendContainerCrashAlert(
    clientId: string,
    event: ContainerEventWithServer
  ): Promise<NotificationResult> {
    const settings = await this.getNotificationSettings(clientId)

    if (!settings.enabled) {
      return { sent: false, reason: 'Notifications disabled' }
    }

    if (settings.recipients.length === 0) {
      return { sent: false, reason: 'No recipients configured' }
    }

    if (!settings.containerCrashes) {
      return { sent: false, reason: 'Container crash notifications disabled' }
    }

    // Check cooldown
    if (settings.lastNotifiedAt) {
      const cooldownMs = settings.cooldownMinutes * 60 * 1000
      const elapsed = Date.now() - settings.lastNotifiedAt.getTime()
      if (elapsed < cooldownMs) {
        return { sent: false, reason: 'Cooldown active' }
      }
    }

    // Get client name
    const client = await prisma.enterpriseClient.findUnique({
      where: { id: clientId },
      select: { name: true },
    })

    // Determine event type text
    let eventTypeText = 'Crash'
    if (event.eventType === 'OOM_KILLED') {
      eventTypeText = 'Out of Memory Kill'
    } else if (event.eventType === 'RESTART') {
      eventTypeText = 'Unexpected Restart'
    }

    const serverName = event.server?.nickname || event.server?.netcupServerId || 'Unknown'
    const subject = `[LetsBe Alert] Container ${eventTypeText} - ${client?.name || 'Unknown Client'}`
    const body = this.buildCrashAlertEmail(
      client?.name || 'Unknown Client',
      serverName,
      event.containerName,
      eventTypeText,
      event.exitCode,
      event.details,
      event.timestamp
    )

    await this.sendToRecipients(settings.recipients, subject, body)
    await this.updateLastNotified(clientId)

    return { sent: true }
  }

  /**
   * Build crash alert email body
   */
  private buildCrashAlertEmail(
    clientName: string,
    serverName: string,
    containerName: string,
    eventType: string,
    exitCode: number | null,
    details: string | null,
    timestamp: Date
  ): string {
    return `
CONTAINER ${eventType.toUpperCase()}

Client: ${clientName}
Server: ${serverName}
Container: ${containerName}
Event: ${eventType}
Exit Code: ${exitCode ?? 'N/A'}
Time: ${timestamp.toISOString()}

${details ? `Details:\n${details}` : ''}

---
Please investigate this issue in the LetsBe Hub dashboard.

Container crashes may indicate memory issues, application bugs, or configuration problems.

---
LetsBe Cloud Platform
    `.trim()
  }

  // --------------------------------------------------------------------------
  // Email Sending
  // --------------------------------------------------------------------------

  /**
   * Send email to all recipients via the centralized email service
   */
  private async sendToRecipients(
    recipients: string[],
    subject: string,
    body: string
  ): Promise<void> {
    const isConfigured = await emailService.isConfigured()

    if (!isConfigured) {
      console.log('[Notification] Email not configured, logging notification:')
      console.log(`To: ${recipients.join(', ')} | Subject: ${subject}`)
      console.log(body)
      return
    }

    const result = await emailService.sendEmail({
      to: recipients,
      subject,
      html: `<pre style="font-family: monospace; white-space: pre-wrap;">${body.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</pre>`,
      text: body,
    })

    if (result.success) {
      console.log(`[Notification] Email sent to ${recipients.length} recipient(s)`)
    } else {
      console.error('[Notification] Failed to send email:', result.error)
      throw new Error('Failed to send notification email')
    }
  }
}

export const notificationService = NotificationService.getInstance()
