<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Str;

#[Fillable([
    'file_id',
    'token',
    'created_by',
    'ip_address',
    'expires_at',
    'access_count',
    'max_access_count',
])]
class SignedUrl extends Model
{
    /** @use HasFactory<\Database\Factories\SignedUrlFactory> */
    use HasFactory;

    public $timestamps = false;

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'expires_at' => 'datetime',
        'created_at' => 'datetime',
        'access_count' => 'integer',
        'max_access_count' => 'integer',
    ];

    /**
     * The "booted" method of the model.
     */
    protected static function booted(): void
    {
        static::creating(function (self $signedUrl) {
            if (empty($signedUrl->token)) {
                $signedUrl->token = Str::random(64);
            }

            if (empty($signedUrl->created_at)) {
                $signedUrl->created_at = now();
            }
        });
    }

    /**
     * Get the file this signed URL belongs to.
     */
    public function file(): BelongsTo
    {
        return $this->belongsTo(File::class);
    }

    /**
     * Get the user who created this signed URL.
     */
    public function creator(): BelongsTo
    {
        return $this->belongsTo(User::class, 'created_by');
    }

    /**
     * Check if the signed URL has expired.
     */
    public function isExpired(): bool
    {
        return $this->expires_at->isPast();
    }

    /**
     * Check if the signed URL has reached its maximum access count.
     */
    public function isExhausted(): bool
    {
        return $this->max_access_count !== null && $this->access_count >= $this->max_access_count;
    }

    /**
     * Check if the signed URL is valid.
     */
    public function isValid(?string $ipAddress = null): bool
    {
        if ($this->isExpired()) {
            return false;
        }

        if ($this->isExhausted()) {
            return false;
        }

        if ($this->ip_address && $ipAddress && $this->ip_address !== $ipAddress) {
            return false;
        }

        return true;
    }

    /**
     * Increment the access count.
     */
    public function recordAccess(): void
    {
        $this->increment('access_count');
    }

    /**
     * Revoke this signed URL.
     */
    public function revoke(): void
    {
        $this->delete();
    }

    /**
     * Scope to get only valid (non-expired, non-exhausted) URLs.
     */
    public function scopeValid($query)
    {
        return $query->where('expires_at', '>', now())
            ->where(function ($q) {
                $q->whereNull('max_access_count')
                    ->orWhereColumn('access_count', '<', 'max_access_count');
            });
    }

    /**
     * Scope to get expired URLs.
     */
    public function scopeExpired($query)
    {
        return $query->where('expires_at', '<=', now());
    }
}
