<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;

/**
 * One row per outbound notification attempt. The whole point of this table
 * is "did the alert actually go out, and if not, why" — answer that with a
 * single SELECT.
 */
class NotificationDelivery extends Model
{
    use HasFactory;

    public const STATUS_QUEUED = 'queued';
    public const STATUS_SENT = 'sent';
    public const STATUS_FAILED = 'failed';
    public const STATUS_RETRYING = 'retrying';
    public const STATUS_SUPPRESSED = 'suppressed'; // quiet hours / severity filter

    protected $fillable = [
        'channel_key', 'channel_provider', 'subject_type', 'subject_id',
        'rule_id', 'recipient_user_id', 'recipient_address',
        'status', 'attempts', 'error_message',
        'payload', 'response',
        'sent_at', 'failed_at', 'next_retry_at',
    ];

    protected $casts = [
        'payload' => 'array',
        'response' => 'array',
        'sent_at' => 'datetime',
        'failed_at' => 'datetime',
        'next_retry_at' => 'datetime',
    ];

    public function subject(): MorphTo
    {
        return $this->morphTo();
    }

    public function rule(): BelongsTo
    {
        return $this->belongsTo(AlertRule::class, 'rule_id');
    }

    public function recipient(): BelongsTo
    {
        return $this->belongsTo(User::class, 'recipient_user_id');
    }

    public function markSent(?array $response = null): void
    {
        $this->update([
            'status' => self::STATUS_SENT,
            'attempts' => $this->attempts + 1,
            'sent_at' => now(),
            'response' => $response,
            'error_message' => null,
            'next_retry_at' => null,
        ]);
    }

    public function markFailed(string $error, bool $shouldRetry = true): void
    {
        $attempts = $this->attempts + 1;
        $maxAttempts = 5;
        // Exponential backoff: 30s, 2m, 8m, 32m, 2h.
        $backoffSeconds = $shouldRetry && $attempts < $maxAttempts
            ? (int) (30 * pow(4, $attempts - 1))
            : null;

        $this->update([
            'status' => $backoffSeconds ? self::STATUS_RETRYING : self::STATUS_FAILED,
            'attempts' => $attempts,
            'error_message' => $error,
            'failed_at' => $backoffSeconds ? null : now(),
            'next_retry_at' => $backoffSeconds ? now()->addSeconds($backoffSeconds) : null,
        ]);
    }
}
