<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use App\Services\ThumbnailService;
use Illuminate\Support\Facades\DB;
use Laravel\Scout\Searchable;

/**
 * App\Models\Artwork
 *
 * @property-read User $user
 * @property-read \Illuminate\Database\Eloquent\Collection|ArtworkTranslation[] $translations
 * @property-read ArtworkStats $stats
 * @property-read \Illuminate\Database\Eloquent\Collection|ArtworkComment[] $comments
 * @property-read \Illuminate\Database\Eloquent\Collection|ArtworkDownload[] $downloads
 */
class Artwork extends Model
{
	use HasFactory, SoftDeletes, Searchable;

	protected $table = 'artworks';

	protected $fillable = [
		'user_id',
		'title',
		'slug',
		'description',
		'file_name',
		'file_path',
		'hash',
		'file_ext',
		'thumb_ext',
		'file_size',
		'mime_type',
		'width',
		'height',
		'is_public',
		'is_approved',
		'published_at',
        'hash',
        'thumb_ext',
        'file_ext',
		// Versioning
		'current_version_id',
		'version_count',
		'version_updated_at',
		'requires_reapproval',
		// Scheduled publishing
		'publish_at',
		'artwork_status',
		'artwork_timezone',
	];

	protected $casts = [
		'is_public'          => 'boolean',
		'is_approved'        => 'boolean',
		'published_at'       => 'datetime',
		'publish_at'         => 'datetime',
		'version_updated_at' => 'datetime',
		'requires_reapproval' => 'boolean',
	];

	/**
	 * Thumbnail sizes and their options.
	 * Keys are the size dir used in the CDN URL.
	 */
	protected const THUMB_SIZES = [
		'sm' => ['height' => 240, 'quality' => 78, 'dir' => 'sm'],
		'md' => ['height' => 360, 'quality' => 82, 'dir' => 'md'],
		'lg' => ['height' => 1200, 'quality' => 85, 'dir' => 'lg'],
		'xl' => ['height' => 2400, 'quality' => 90, 'dir' => 'xl'],
	];

	/**
	 * Build the thumbnail URL for this artwork.
	 * Returns null when no hash or thumb_ext is available.
	 */
	public function thumbUrl(string $size = 'md'): ?string
	{
		if (empty($this->hash) || empty($this->thumb_ext)) {
			return null;
		}

		$sizeKey = array_key_exists($size, self::THUMB_SIZES) ? $size : 'md';
		return ThumbnailService::fromHash($this->hash, $this->thumb_ext, $sizeKey);
	}

	/**
	 * Accessor for `$art->thumb` used in legacy views (default medium size).
	 */
	public function getThumbAttribute(): string
	{
		return $this->thumbUrl('md') ?? 'https://files.skinbase.org/default/missing_md.webp';
	}

	/**
	 * Accessor for `$art->thumb_url` used in some views.
	 */
	public function getThumbUrlAttribute(): ?string
	{
		return $this->thumbUrl('md');
	}

	/**
	 * Backwards-compatible alias used by legacy views: `$art->thumbnail_url`.
	 * Prefer CDN thumbnail URL, then legacy `thumb` accessor, finally a placeholder.
	 */
	public function getThumbnailUrlAttribute(): ?string
	{
		$url = $this->getThumbUrlAttribute();
		if (!empty($url)) return $url;
		$thumb = $this->getThumbAttribute();
		if (!empty($thumb)) return $thumb;
		return '/images/placeholder.jpg';
	}

	/**
	 * Provide a responsive `srcset` for legacy views.
	 */
	public function getThumbSrcsetAttribute(): ?string
	{
		if (empty($this->hash) || empty($this->thumb_ext)) return null;
		$sm = $this->thumbUrl('sm');
		$md = $this->thumbUrl('md');
		if (!$sm || !$md) return null;
		return $sm . ' 320w, ' . $md . ' 600w';
	}

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

	public function translations(): HasMany
	{
		return $this->hasMany(ArtworkTranslation::class);
	}

	public function stats(): HasOne
	{
		return $this->hasOne(ArtworkStats::class, 'artwork_id');
	}

	public function categories(): BelongsToMany
	{
		return $this->belongsToMany(Category::class, 'artwork_category', 'artwork_id', 'category_id');
	}

	public function tags(): BelongsToMany
	{
		return $this->belongsToMany(Tag::class, 'artwork_tag', 'artwork_id', 'tag_id')
			->withPivot(['source', 'confidence']);
	}

	public function comments(): HasMany
	{
		return $this->hasMany(ArtworkComment::class);
	}

	public function downloads(): HasMany
	{
		return $this->hasMany(ArtworkDownload::class);
	}

	public function embeddings(): HasMany
	{
		return $this->hasMany(ArtworkEmbedding::class, 'artwork_id');
	}

	public function similarities(): HasMany
	{
		return $this->hasMany(ArtworkSimilarity::class, 'artwork_id');
	}

	public function features(): HasMany
	{
		return $this->hasMany(ArtworkFeature::class, 'artwork_id');
	}

	/** All favourite pivot rows for this artwork. */
	public function favourites(): HasMany
	{
		return $this->hasMany(ArtworkFavourite::class, 'artwork_id');
	}

	/** Users who have favourited this artwork (many-to-many shortcut). */
	public function favouritedBy(): BelongsToMany
	{
		return $this->belongsToMany(User::class, 'artwork_favourites', 'artwork_id', 'user_id')
			->withPivot('legacy_id')
			->withTimestamps();
	}

	public function awards(): HasMany
	{
		return $this->hasMany(ArtworkAward::class);
	}

	/** All file versions for this artwork (oldest first). */
	public function versions(): HasMany
	{
		return $this->hasMany(ArtworkVersion::class)->orderBy('version_number');
	}

	/** The currently active version record. */
	public function currentVersion(): BelongsTo
	{
		return $this->belongsTo(ArtworkVersion::class, 'current_version_id');
	}

	public function awardStat(): HasOne
	{
		return $this->hasOne(ArtworkAwardStat::class);
	}

	/**
	 * Build the Meilisearch document for this artwork.
	 * Includes all fields required for search, filtering, sorting, and display.
	 */
	public function toSearchableArray(): array
	{
		$this->loadMissing(['user', 'tags', 'categories.contentType', 'stats', 'awardStat']);

		$stat     = $this->stats;
		$awardStat = $this->awardStat;

		// Orientation derived from pixel dimensions
		$orientation = 'square';
		if ($this->width && $this->height) {
			if ($this->width > $this->height) {
				$orientation = 'landscape';
			} elseif ($this->height > $this->width) {
				$orientation = 'portrait';
			}
		}

		// Resolution string e.g. "1920x1080"
		$resolution = ($this->width && $this->height)
			? $this->width . 'x' . $this->height
			: '';

		// Primary category slug (first attached category)
		$primaryCategory = $this->categories->first();
		$category     = $primaryCategory?->slug ?? '';
		$content_type = $primaryCategory?->contentType?->slug ?? '';

		// Tag slugs array
		$tags = $this->tags->pluck('slug')->values()->all();

		return [
			'id'          => $this->id,
			'slug'        => $this->slug,
			'title'       => $this->title,
			'description' => (string) ($this->description ?? ''),
			'author_id'   => $this->user_id,
			'author_name' => $this->user?->name ?? 'Skinbase',
			'category'     => $category,
			'content_type' => $content_type,
			'tags'         => $tags,
			'resolution'  => $resolution,
			'orientation' => $orientation,
			'downloads'   => (int) ($stat?->downloads ?? 0),
			'likes'       => (int) ($stat?->favorites ?? 0),
			'views'       => (int) ($stat?->views ?? 0),
			'created_at'  => $this->published_at?->toDateString() ?? $this->created_at?->toDateString() ?? '',
			'is_public'   => (bool) $this->is_public,
			'is_approved' => (bool) $this->is_approved,
			// ── Trending / discovery fields ────────────────────────────────────
			'trending_score_24h'    => (float) ($this->trending_score_24h ?? 0),
			'trending_score_7d'     => (float) ($this->trending_score_7d  ?? 0),
			'favorites_count'       => (int)   ($stat?->favorites         ?? 0),
			'awards_received_count' => (int)   ($awardStat?->score_total  ?? 0),
			'downloads_count'       => (int)   ($stat?->downloads         ?? 0),
			// ── Ranking V2 fields ───────────────────────────────────────────────
			'ranking_score'         => (float) ($stat?->ranking_score          ?? 0),
			'engagement_velocity'   => (float) ($stat?->engagement_velocity    ?? 0),
			'shares_count'          => (int)   ($stat?->shares_count           ?? 0),
			'comments_count'        => (int)   ($stat?->comments_count         ?? 0),
			// ── Rising / Heat fields ────────────────────────────────────────────────────
			'heat_score'            => (float) ($stat?->heat_score             ?? 0),
			'awards' => [
				'gold'   => $awardStat?->gold_count   ?? 0,
				'silver' => $awardStat?->silver_count ?? 0,
				'bronze' => $awardStat?->bronze_count ?? 0,
				'score'  => $awardStat?->score_total  ?? 0,
			],
		];
	}

	// Scopes
	public function scopePublic(Builder $query): Builder
	{
		// Compose approved() so behavior is consistent and composable
		$table = $this->getTable();
		return $query->approved()->where("{$table}.is_public", true);
	}

	public function scopeApproved(Builder $query): Builder
	{
		// Respect soft deletes and mark approved content
		$table = $this->getTable();
		return $query->whereNull("{$table}.deleted_at")->where("{$table}.is_approved", true);
	}

	public function scopePublished(Builder $query): Builder
	{
		// Respect soft deletes and only include published items up to now
		$table = $this->getTable();
		return $query->whereNull("{$table}.deleted_at")
			->whereNotNull("{$table}.published_at")
			->where("{$table}.published_at", '<=', now());
	}

	public function getRouteKeyName(): string
	{
		return 'slug';
	}

	protected static function booted(): void
	{
		static::deleting(function (Artwork $artwork): void {
			if (! method_exists($artwork, 'isForceDeleting') || ! $artwork->isForceDeleting()) {
				return;
			}

			// Cleanup pivot rows and decrement usage counts on force delete.
			$tagIds = DB::table('artwork_tag')->where('artwork_id', $artwork->id)->pluck('tag_id')->all();
			if ($tagIds === []) {
				return;
			}

			DB::table('artwork_tag')->where('artwork_id', $artwork->id)->delete();
			DB::table('tags')
				->whereIn('id', $tagIds)
				->update(['usage_count' => DB::raw('CASE WHEN usage_count > 0 THEN usage_count - 1 ELSE 0 END')]);
		});
	}
}
