<?php

namespace App\Listeners;

use App\Models\EmailLog;
use Illuminate\Notifications\Events\NotificationFailed;
use Illuminate\Support\Facades\Log;

class HandleFailedNotification
{
    /**
     * Handle the event.
     * 
     * Log failed notifications to EmailLog table and Laravel logs.
     * Does not throw exceptions so other channels can still be processed.
     */
    public function handle(NotificationFailed $event): void
    {
        $errorMessage = $this->extractErrorMessage($event);
        $notificationType = class_basename($event->notification);
        
        // Log to Laravel logs
        Log::warning('Notification failed', [
            'notification' => get_class($event->notification),
            'notifiable' => get_class($event->notifiable) . ':' . $event->notifiable->getKey(),
            'channel' => $event->channel,
            'error' => $errorMessage,
        ]);

        // Only log mail channel failures to EmailLog
        if ($event->channel === 'mail') {
            $this->logToEmailLog($event, $notificationType, $errorMessage);
        }
    }

    /**
     * Extract error message from event data
     */
    protected function extractErrorMessage(NotificationFailed $event): ?string
    {
        if (!is_array($event->data)) {
            return null;
        }

        if (isset($event->data['exception'])) {
            return $event->data['exception'] instanceof \Throwable 
                ? $event->data['exception']->getMessage() 
                : (string) $event->data['exception'];
        }
        
        if (isset($event->data['error'])) {
            return $event->data['error'];
        }

        return null;
    }

    /**
     * Log the failure to EmailLog table
     */
    protected function logToEmailLog(NotificationFailed $event, string $notificationType, ?string $errorMessage): void
    {
        try {
            // Get subject if possible
            $subject = $this->extractSubject($event);
            
            // Check if there's already a log entry for this notification
            $existingLog = EmailLog::where('notification_type', $notificationType)
                ->where('recipient_id', $event->notifiable->getKey())
                ->where('status', EmailLog::STATUS_PENDING)
                ->latest()
                ->first();

            if ($existingLog) {
                // Update existing log
                $existingLog->markAsFailed($errorMessage ?? 'Unknown error');
            } else {
                // Create new log entry and mark as failed
                $log = EmailLog::createForNotification(
                    notificationType: $notificationType,
                    notifiable: $event->notifiable,
                    subject: $subject,
                    channel: 'mail',
                    metadata: [
                        'notification_class' => get_class($event->notification),
                        'failed_via_listener' => true,
                    ]
                );
                
                $log->markAsFailed($errorMessage ?? 'Unknown error');
            }
        } catch (\Throwable $e) {
            // Don't let logging failures break the application
            Log::error('Failed to log email failure to EmailLog', [
                'error' => $e->getMessage(),
                'notification' => $notificationType,
            ]);
        }
    }

    /**
     * Try to extract the email subject from the notification
     */
    protected function extractSubject(NotificationFailed $event): string
    {
        try {
            if (method_exists($event->notification, 'toMail')) {
                $mail = $event->notification->toMail($event->notifiable);
                return $mail->subject ?? class_basename($event->notification);
            }
        } catch (\Throwable $e) {
            // Ignore - just use default subject
        }

        return class_basename($event->notification);
    }
}
