<?php

declare(strict_types=1);

namespace App\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

class NovaCard extends Model
{
    use HasFactory, SoftDeletes;

    public const FORMAT_SQUARE = 'square';
    public const FORMAT_PORTRAIT = 'portrait';
    public const FORMAT_STORY = 'story';
    public const FORMAT_LANDSCAPE = 'landscape';

    public const VISIBILITY_PUBLIC = 'public';
    public const VISIBILITY_UNLISTED = 'unlisted';
    public const VISIBILITY_PRIVATE = 'private';

    public const STATUS_DRAFT = 'draft';
    public const STATUS_SCHEDULED = 'scheduled';
    public const STATUS_PROCESSING = 'processing';
    public const STATUS_PUBLISHED = 'published';
    public const STATUS_HIDDEN = 'hidden';
    public const STATUS_REJECTED = 'rejected';

    public const MOD_PENDING = 'pending';
    public const MOD_APPROVED = 'approved';
    public const MOD_FLAGGED = 'flagged';
    public const MOD_REJECTED = 'rejected';

    protected $fillable = [
        'uuid',
        'user_id',
        'category_id',
        'title',
        'slug',
        'quote_text',
        'quote_author',
        'quote_source',
        'description',
        'format',
        'project_json',
        'schema_version',
        'render_version',
        'preview_path',
        'preview_width',
        'preview_height',
        'background_type',
        'background_image_id',
        'original_card_id',
        'root_card_id',
        'template_id',
        'visibility',
        'status',
        'moderation_status',
        'featured',
        'allow_download',
        'allow_remix',
        'views_count',
        'shares_count',
        'downloads_count',
        'likes_count',
        'favorites_count',
        'saves_count',
        'remixes_count',
        'comments_count',
        'challenge_entries_count',
        'trending_score',
        'featured_score',
        'style_family',
        'palette_family',
        'density_score',
        'editor_mode_last_used',
        'allow_background_reuse',
        'allow_export',
        'original_creator_id',
        'published_at',
        'scheduled_for',
        'scheduling_timezone',
        'last_engaged_at',
        'last_ranked_at',
        'last_rendered_at',
    ];

    protected $casts = [
        'project_json' => 'array',
        'schema_version' => 'integer',
        'render_version' => 'integer',
        'preview_width' => 'integer',
        'preview_height' => 'integer',
        'featured' => 'boolean',
        'allow_download' => 'boolean',
        'allow_remix' => 'boolean',
        'views_count' => 'integer',
        'shares_count' => 'integer',
        'downloads_count' => 'integer',
        'likes_count' => 'integer',
        'favorites_count' => 'integer',
        'saves_count' => 'integer',
        'remixes_count' => 'integer',
        'comments_count' => 'integer',
        'challenge_entries_count' => 'integer',
        'trending_score' => 'float',
        'featured_score' => 'float',
        'density_score' => 'integer',
        'allow_background_reuse' => 'boolean',
        'allow_export' => 'boolean',
        'published_at' => 'datetime',
        'scheduled_for' => 'datetime',
        'last_engaged_at' => 'datetime',
        'last_ranked_at' => 'datetime',
        'last_rendered_at' => 'datetime',
    ];

    protected static function booted(): void
    {
        static::creating(function (self $card): void {
            if (! $card->uuid) {
                $card->uuid = (string) Str::uuid();
            }
        });
    }

    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

    public function category(): BelongsTo
    {
        return $this->belongsTo(NovaCardCategory::class, 'category_id');
    }

    public function template(): BelongsTo
    {
        return $this->belongsTo(NovaCardTemplate::class, 'template_id');
    }

    public function backgroundImage(): BelongsTo
    {
        return $this->belongsTo(NovaCardBackground::class, 'background_image_id');
    }

    public function originalCard(): BelongsTo
    {
        return $this->belongsTo(self::class, 'original_card_id');
    }

    public function rootCard(): BelongsTo
    {
        return $this->belongsTo(self::class, 'root_card_id');
    }

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

    public function remixes(): HasMany
    {
        return $this->hasMany(self::class, 'original_card_id');
    }

    public function tags(): BelongsToMany
    {
        return $this->belongsToMany(NovaCardTag::class, 'nova_card_tag_relation', 'card_id', 'tag_id')
            ->withTimestamps();
    }

    public function reactions(): HasMany
    {
        return $this->hasMany(NovaCardReaction::class, 'card_id');
    }

    public function versions(): HasMany
    {
        return $this->hasMany(NovaCardVersion::class, 'card_id');
    }

    public function collectionItems(): HasMany
    {
        return $this->hasMany(NovaCardCollectionItem::class, 'card_id');
    }

    public function challengeEntries(): HasMany
    {
        return $this->hasMany(NovaCardChallengeEntry::class, 'card_id');
    }

    public function comments(): HasMany
    {
        return $this->hasMany(NovaCardComment::class, 'card_id');
    }

    public function scopePublished(Builder $query): Builder
    {
        return $query
            ->where('status', self::STATUS_PUBLISHED)
            ->where('moderation_status', '!=', self::MOD_REJECTED)
            ->whereIn('visibility', [self::VISIBILITY_PUBLIC, self::VISIBILITY_UNLISTED])
            ->whereNotNull('published_at');
    }

    public function scopePubliclyVisible(Builder $query): Builder
    {
        return $query
            ->published()
            ->where('visibility', self::VISIBILITY_PUBLIC)
            ->whereNotIn('moderation_status', [self::MOD_FLAGGED, self::MOD_REJECTED])
            ->where('status', self::STATUS_PUBLISHED);
    }

    public function scopeRising(Builder $query): Builder
    {
        return $query
            ->publiclyVisible()
            ->where('published_at', '>=', now()->subHours(96))
            ->where(function (Builder $q): void {
                $q->where('saves_count', '>', 0)
                    ->orWhere('remixes_count', '>', 0)
                    ->orWhere('likes_count', '>', 1);
            });
    }

    public function scopeFeaturedEditorial(Builder $query): Builder
    {
        return $query
            ->publiclyVisible()
            ->where(function (Builder $q): void {
                $q->whereNotNull('featured_score')
                    ->orWhere('featured', true);
            });
    }

    public function previewUrl(): ?string
    {
        if (! $this->preview_path) {
            return null;
        }

        // Prefer an explicit CDN URL so images are served through the CDN edge layer.
        $cdnBase = (string) env('FILES_CDN_URL', '');
        if ($cdnBase !== '') {
            return rtrim($cdnBase, '/') . '/' . ltrim($this->preview_path, '/');
        }

        return Storage::disk((string) config('nova_cards.storage.public_disk', 'public'))->url($this->preview_path);
    }

    public function ogPreviewUrl(): ?string
    {
        if (! $this->preview_path) {
            return null;
        }

        $ogPath = preg_replace('/\.webp$/', '-og.jpg', $this->preview_path) ?: null;
        if (! $ogPath) {
            return $this->previewUrl();
        }

        $cdnBase = (string) env('FILES_CDN_URL', '');
        if ($cdnBase !== '') {
            return rtrim($cdnBase, '/') . '/' . ltrim($ogPath, '/');
        }

        return Storage::disk((string) config('nova_cards.storage.public_disk', 'public'))->url($ogPath);
    }

    public function publicUrl(): string
    {
        return route('cards.show', ['slug' => $this->slug, 'id' => $this->id]);
    }

    public function isOwnedBy(?User $user): bool
    {
        return $user !== null && (int) $user->id === (int) $this->user_id;
    }

    public function canBeViewedBy(?User $user): bool
    {
        if ($this->isOwnedBy($user)) {
            return true;
        }

        if ($this->status !== self::STATUS_PUBLISHED) {
            return false;
        }

        if ($this->moderation_status === self::MOD_REJECTED || $this->moderation_status === self::MOD_FLAGGED) {
            return false;
        }

        return in_array($this->visibility, [self::VISIBILITY_PUBLIC, self::VISIBILITY_UNLISTED], true);
    }

    public function isRemix(): bool
    {
        return $this->original_card_id !== null;
    }

    public function canReceiveCommentsFrom(?User $user): bool
    {
        return $user !== null && $this->canBeViewedBy($user);
    }
}
