<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\MassPrunable;

class EmailLog extends Model
{
    use MassPrunable;

    protected $fillable = [
        'notification_type',
        'recipient_type',
        'recipient_id',
        'recipient_email',
        'subject',
        'channel',
        'status',
        'attempts',
        'last_attempt_at',
        'sent_at',
        'failed_at',
        'error_message',
        'metadata',
    ];

    protected $casts = [
        'last_attempt_at' => 'datetime',
        'sent_at' => 'datetime',
        'failed_at' => 'datetime',
        'metadata' => 'array',
    ];

    // Status constants
    const STATUS_PENDING = 'pending';
    const STATUS_SENT = 'sent';
    const STATUS_FAILED = 'failed';
    const STATUS_RETRYING = 'retrying';

    /**
     * Get the recipient (polymorphic)
     */
    public function recipient(): BelongsTo
    {
        return $this->morphTo('recipient', 'recipient_type', 'recipient_id');
    }

    /**
     * Scope: pending emails
     */
    public function scopePending(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_PENDING);
    }

    /**
     * Scope: failed emails
     */
    public function scopeFailed(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_FAILED);
    }

    /**
     * Scope: sent emails
     */
    public function scopeSent(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_SENT);
    }

    /**
     * Scope: for retry (failed with attempts < max)
     */
    public function scopeForRetry(Builder $query, int $maxAttempts = 3): Builder
    {
        return $query->where('status', self::STATUS_FAILED)
            ->where('attempts', '<', $maxAttempts);
    }

    /**
     * Scope: recent logs (last 24 hours)
     */
    public function scopeRecent(Builder $query): Builder
    {
        return $query->where('created_at', '>=', now()->subDay());
    }

    /**
     * Mark as sent
     */
    public function markAsSent(): bool
    {
        return $this->update([
            'status' => self::STATUS_SENT,
            'sent_at' => now(),
            'error_message' => null,
        ]);
    }

    /**
     * Mark as failed
     */
    public function markAsFailed(string $errorMessage): bool
    {
        return $this->update([
            'status' => self::STATUS_FAILED,
            'failed_at' => now(),
            'error_message' => $errorMessage,
            'attempts' => $this->attempts + 1,
            'last_attempt_at' => now(),
        ]);
    }

    /**
     * Increment attempt counter
     */
    public function incrementAttempts(): bool
    {
        return $this->update([
            'attempts' => $this->attempts + 1,
            'last_attempt_at' => now(),
            'status' => self::STATUS_RETRYING,
        ]);
    }

    /**
     * Check if can retry
     */
    public function canRetry(int $maxAttempts = 3): bool
    {
        return $this->attempts < $maxAttempts && $this->status !== self::STATUS_SENT;
    }

    /**
     * Create a log entry for a notification
     */
    public static function createForNotification(
        string $notificationType,
        object $notifiable,
        string $subject,
        string $channel = 'mail',
        array $metadata = []
    ): self {
        return self::create([
            'notification_type' => $notificationType,
            'recipient_type' => get_class($notifiable),
            'recipient_id' => $notifiable->getKey(),
            'recipient_email' => $notifiable->email ?? $notifiable->routeNotificationForMail(),
            'subject' => $subject,
            'channel' => $channel,
            'status' => self::STATUS_PENDING,
            'attempts' => 0,
            'metadata' => $metadata,
        ]);
    }

    /**
     * Get failure rate for last 24 hours
     */
    public static function getRecentFailureRate(): float
    {
        $recent = self::recent();
        $total = $recent->count();
        
        if ($total === 0) {
            return 0.0;
        }
        
        $failed = $recent->clone()->failed()->count();
        
        return round(($failed / $total) * 100, 2);
    }

    /**
     * Get statistics for dashboard
     */
    public static function getStatistics(int $days = 7): array
    {
        $startDate = now()->subDays($days);
        
        $query = self::where('created_at', '>=', $startDate);
        
        return [
            'total' => $query->count(),
            'sent' => $query->clone()->sent()->count(),
            'failed' => $query->clone()->failed()->count(),
            'pending' => $query->clone()->pending()->count(),
            'failure_rate' => self::getRecentFailureRate(),
            'by_type' => $query->clone()
                ->selectRaw('notification_type, status, COUNT(*) as count')
                ->groupBy('notification_type', 'status')
                ->get()
                ->groupBy('notification_type')
                ->map(fn ($items) => $items->pluck('count', 'status'))
                ->toArray(),
        ];
    }

    /**
     * Get the prunable model query.
     * Removes logs older than 30 days.
     */
    public function prunable(): Builder
    {
        return static::where('created_at', '<=', now()->subDays(30));
    }
}
