<?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\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;

#[Fillable([
    'uuid',
    'bucket_id',
    'uploaded_by',
    'original_name',
    'stored_name',
    'path',
    'disk',
    'mime_type',
    'extension',
    'size',
    'checksum',
    'visibility',
    'metadata',
    'thumbnail_path',
    'virus_scan_status',
    'virus_scanned_at',
    'expires_at',
])]
class File extends Model
{
    /** @use HasFactory<\Database\Factories\FileFactory> */
    use HasFactory, SoftDeletes;

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

    /**
     * Get the bucket this file belongs to.
     */
    public function bucket(): BelongsTo
    {
        return $this->belongsTo(FileBucket::class, 'bucket_id');
    }

    /**
     * Get the user who uploaded this file.
     */
    public function uploader(): BelongsTo
    {
        return $this->belongsTo(User::class, 'uploaded_by');
    }

    /**
     * Get the permissions for this file.
     */
    public function permissions(): HasMany
    {
        return $this->hasMany(FilePermission::class, 'file_id');
    }

    /**
     * Get the signed URLs for this file.
     */
    public function signedUrls(): HasMany
    {
        return $this->hasMany(SignedUrl::class, 'file_id');
    }

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'metadata' => 'array',
            'expires_at' => 'datetime',
            'virus_scanned_at' => 'datetime',
            'size' => 'integer',
        ];
    }

    /**
     * Check if the file is public.
     */
    public function isPublic(): bool
    {
        return $this->visibility === 'public';
    }

    /**
     * Check if the file is private.
     */
    public function isPrivate(): bool
    {
        return $this->visibility === 'private';
    }

    /**
     * Check if the file requires signed URLs.
     */
    public function requiresSignedUrl(): bool
    {
        return $this->visibility === 'signed' || $this->visibility === 'private';
    }

    /**
     * Check if the file has been scanned for viruses.
     */
    public function isVirusScanned(): bool
    {
        return in_array($this->virus_scan_status, ['clean', 'infected', 'skipped']);
    }

    /**
     * Check if the file is clean from viruses.
     */
    public function isClean(): bool
    {
        return $this->virus_scan_status === 'clean' || $this->virus_scan_status === 'skipped';
    }

    /**
     * Check if the file is infected.
     */
    public function isInfected(): bool
    {
        return $this->virus_scan_status === 'infected';
    }

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

    /**
     * Check if the file supports thumbnail generation.
     */
    public function supportsThumbnail(): bool
    {
        return in_array($this->mime_type, config('file_upload.thumbnails.supported_mime_types', []));
    }

    /**
     * Get the human-readable file size.
     */
    public function getHumanSizeAttribute(): string
    {
        $bytes = $this->size;
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];
        $i = 0;

        while ($bytes >= 1024 && $i < count($units) - 1) {
            $bytes /= 1024;
            $i++;
        }

        return round($bytes, 2).' '.$units[$i];
    }

    /**
     * Get the file extension from the original name.
     */
    public function getExtensionFromName(): string
    {
        return strtolower(pathinfo($this->original_name, PATHINFO_EXTENSION));
    }

    /**
     * Route key for model binding.
     */
    public function getRouteKeyName(): string
    {
        return 'uuid';
    }
}
