<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Schema;

class Attachment extends Model
{
    use HasFactory;

    public const MAX_UPLOAD_KILOBYTES = 102400;
    private const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'svg', 'heic', 'heif'];
    private const VIDEO_EXTENSIONS = ['mp4', 'mov', 'avi', 'mkv', 'webm', 'm4v', 'mpeg', 'mpg', 'wmv', '3gp'];
    private const EXTENSION_MIME_MAP = [
        'jpg' => 'image/jpeg',
        'jpeg' => 'image/jpeg',
        'png' => 'image/png',
        'gif' => 'image/gif',
        'webp' => 'image/webp',
        'bmp' => 'image/bmp',
        'svg' => 'image/svg+xml',
        'heic' => 'image/heic',
        'heif' => 'image/heif',
        'mp4' => 'video/mp4',
        'mov' => 'video/quicktime',
        'avi' => 'video/x-msvideo',
        'mkv' => 'video/x-matroska',
        'webm' => 'video/webm',
        'm4v' => 'video/x-m4v',
        'mpeg' => 'video/mpeg',
        'mpg' => 'video/mpeg',
        'wmv' => 'video/x-ms-wmv',
        '3gp' => 'video/3gpp',
    ];

    protected static ?bool $commentIdColumnExists = null;
    protected static ?bool $uploadedByColumnExists = null;

    protected $fillable = [
        'ticket_id', 'comment_id', 'uploaded_by_id', 'filename', 'path', 'mime_type', 'size',
    ];

    protected $appends = ['mimeType', 'isVideo', 'isImage', 'url'];

    public function ticket()
    {
        return $this->belongsTo(Ticket::class);
    }

    public function comment()
    {
        return $this->belongsTo(Comment::class);
    }

    public function uploader()
    {
        return $this->belongsTo(User::class, 'uploaded_by_id');
    }

    public function getMimeTypeAttribute()
    {
        return $this->attributes['mime_type'] ?? null;
    }

    public function getIsVideoAttribute(): bool
    {
        return $this->matchesMediaType('video');
    }

    public function getIsImageAttribute(): bool
    {
        return $this->matchesMediaType('image');
    }

    public function getUrlAttribute(): ?string
    {
        $path = (string) ($this->attributes['path'] ?? '');

        if ($path === '') {
            return null;
        }

        if (preg_match('/^(https?:)?\/\//i', $path) === 1) {
            return $path;
        }

        return asset(ltrim($path, '/'));
    }

    public static function supportsUploadedBy(): bool
    {
        if (self::$uploadedByColumnExists === null) {
            self::$uploadedByColumnExists = Schema::hasColumn('attachments', 'uploaded_by_id');
        }

        return self::$uploadedByColumnExists;
    }

    public static function supportsCommentAttachments(): bool
    {
        if (self::$commentIdColumnExists === null) {
            self::$commentIdColumnExists = Schema::hasColumn('attachments', 'comment_id');
        }

        return self::$commentIdColumnExists;
    }

    public static function normalizeMimeType(?string $mimeType, ?string $filename = null): ?string
    {
        $normalizedMimeType = strtolower(trim((string) $mimeType));
        $extension = strtolower(pathinfo((string) $filename, PATHINFO_EXTENSION));
        $mappedMimeType = self::EXTENSION_MIME_MAP[$extension] ?? null;

        if ($normalizedMimeType === '') {
            return $mappedMimeType;
        }

        if (!$mappedMimeType) {
            return $normalizedMimeType;
        }

        if (str_starts_with($mappedMimeType, 'image/') && !str_starts_with($normalizedMimeType, 'image/')) {
            return $mappedMimeType;
        }

        if (str_starts_with($mappedMimeType, 'video/') && !str_starts_with($normalizedMimeType, 'video/')) {
            return $mappedMimeType;
        }

        return $normalizedMimeType;
    }

    private function matchesMediaType(string $expectedPrefix): bool
    {
        $mimeType = strtolower((string) ($this->attributes['mime_type'] ?? ''));
        if (str_starts_with($mimeType, $expectedPrefix . '/')) {
            return true;
        }

        $extension = strtolower(pathinfo((string) ($this->attributes['filename'] ?? ''), PATHINFO_EXTENSION));
        $allowedExtensions = $expectedPrefix === 'image' ? self::IMAGE_EXTENSIONS : self::VIDEO_EXTENSIONS;

        return $extension !== '' && in_array($extension, $allowedExtensions, true);
    }
}
