<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Playlist extends Model
{
    use HasFactory;

    protected $fillable = [
        'user_id',
        'name',
        'description',
        'thumbnail',
        'visibility',
        'is_default',
        'share_token',
        'view_count',
    ];

    protected $casts = [
        'is_default' => 'boolean',
        'view_count' => 'integer',
    ];

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

    public function videos()
    {
        return $this->belongsToMany(Video::class, 'playlist_videos')
            ->withPivot('position', 'watched_seconds', 'watched', 'added_at', 'last_watched_at')
            ->orderBy('position');
    }

    // Get videos with their pivot data
    public function getVideosWithPivot()
    {
        return $this->videos()->orderBy('position')->get();
    }

    // Accessors
    public function getThumbnailUrlAttribute(): string
    {
        if ($this->thumbnail) {
            return route('media.thumbnail', $this->thumbnail);
        }

        return 'https://ui-avatars.com/api/?name='.urlencode($this->name).'&background=random&size=200';
    }

    // Get total video count
    public function getVideoCountAttribute()
    {
        return $this->videos()->count();
    }

    // Get total duration of all videos in playlist
    public function getTotalDurationAttribute()
    {
        return $this->videos()->sum('duration');
    }

    // Get formatted total duration (e.g., "2h 30m")
    public function getFormattedDurationAttribute()
    {
        $totalSeconds = $this->total_duration;

        if (! $totalSeconds) {
            return '0m';
        }

        $hours = floor($totalSeconds / 3600);
        $minutes = floor(($totalSeconds % 3600) / 60);

        if ($hours > 0) {
            return "{$hours}h {$minutes}m";
        }

        return "{$minutes}m";
    }

    // Total of every viewer-session aggregated across the playlist's videos.
    // Kept for the analytics-style "video time watched" metric — for the
    // playlist's OWN view counter (cards, share link), use $playlist->view_count
    // which is incremented per-device by bumpViewIfNew().
    public function getTotalViewsAttribute()
    {
        return $this->videos()->get()->sum('view_count');
    }

    /**
     * Record a playlist view if this viewer hasn't already counted within the
     * dedup window. Mirrors the video_views pattern: signed-in users dedup by
     * user_id, guests dedup by device fingerprint (preferred) or device cookie.
     *
     * Cheap and idempotent — runs as one EXISTS + (optionally) one INSERT +
     * one atomic increment, all on indexed columns. Called via
     * dispatchAfterResponse() so it never adds latency to the page render.
     */
    public function bumpViewIfNew(\Illuminate\Http\Request $request): void
    {
        $userId = \Illuminate\Support\Facades\Auth::id();
        $did    = $request->cookie('_did');
        $fp     = $request->cookie('_fp');
        $fp     = ($fp && preg_match('/^[a-f0-9]{64}$/', $fp)) ? $fp : null;

        // No identifier at all? Skip silently — a unidentifiable request would
        // count on every refresh and inflate the counter.
        if (! $userId && ! $did && ! $fp) return;

        $q = \DB::table('playlist_views')
            ->where('playlist_id', $this->id)
            ->where('viewed_at', '>', now()->subHour());

        if ($userId) {
            $q->where('user_id', $userId);
        } else {
            $q->whereNull('user_id')->where(function ($w) use ($fp, $did) {
                if ($fp)  $w->orWhere('device_hash', $fp);
                if ($did) $w->orWhere('device_id',   $did);
            });
        }

        if ($q->exists()) return;

        $ip  = $request->header('CF-Connecting-IP')
            ?? $request->header('X-Real-IP')
            ?? $request->ip();
        $geo = \App\Services\GeoIpService::lookup($ip);

        \DB::table('playlist_views')->insert([
            'playlist_id'  => $this->id,
            'user_id'      => $userId,
            'device_id'    => $did,
            'device_hash'  => $fp,
            'ip_address'   => $ip,
            'country'      => $geo['country']      ?? null,
            'country_name' => $geo['country_name'] ?? null,
            'user_agent'   => substr((string) $request->userAgent(), 0, 512),
            'viewed_at'    => now(),
        ]);

        \DB::table('playlists')->where('id', $this->id)->increment('view_count');
    }

    /**
     * Compute previous/next videos from an already-loaded ordered collection
     * (the playlist's videos in position order). Saves the 4+ extra queries
     * that getNextVideo() / getPreviousVideo() would each fire.
     */
    public function neighborsFromCollection(\Illuminate\Support\Collection $orderedVideos, Video $current): array
    {
        $idx = $orderedVideos->search(fn ($v) => $v->id === $current->id);
        if ($idx === false) {
            return [null, $orderedVideos->first()];
        }
        return [
            $idx > 0 ? $orderedVideos[$idx - 1] : null,
            $idx < $orderedVideos->count() - 1 ? $orderedVideos[$idx + 1] : null,
        ];
    }

    // Check if user owns this playlist
    public function isOwnedBy($user)
    {
        if (! $user) {
            return false;
        }

        return $this->user_id === $user->id;
    }

    // Check if video is in this playlist
    public function hasVideo(Video $video)
    {
        return $this->videos()->where('video_id', $video->id)->exists();
    }

    // Get video position in playlist
    public function getVideoPosition(Video $video)
    {
        $pivot = $this->videos()->where('video_id', $video->id)->first();

        return $pivot ? $pivot->pivot->position : null;
    }

    // Get next video in playlist
    public function getNextVideo(Video $currentVideo)
    {
        $currentPosition = $this->getVideoPosition($currentVideo);

        if ($currentPosition === null) {
            return $this->videos()->first();
        }

        return $this->videos()
            ->wherePivot('position', '>', $currentPosition)
            ->orderBy('position')
            ->first();
    }

    // Get previous video in playlist
    public function getPreviousVideo(Video $currentVideo)
    {
        $currentPosition = $this->getVideoPosition($currentVideo);

        if ($currentPosition === null) {
            return $this->videos()->last();
        }

        return $this->videos()
            ->wherePivot('position', '<', $currentPosition)
            ->orderByDesc('position')
            ->first();
    }

    // All share URLs use the unguessable token route
    public function getShareUrlAttribute()
    {
        return route('playlists.showByToken', $this->share_token);
    }

    // Scope for public playlists
    public function scopePublic($query)
    {
        return $query->where('visibility', 'public');
    }

    // Scope for user's playlists
    public function scopeForUser($query, $userId)
    {
        return $query->where('user_id', $userId);
    }

    // Scope for default playlists (Watch Later)
    public function scopeDefault($query)
    {
        return $query->where('is_default', true);
    }

    // Visibility helpers
    public function isPublic()
    {
        return $this->visibility === 'public';
    }

    public function isPrivate()
    {
        return $this->visibility === 'private';
    }

    public function isUnlisted()
    {
        return $this->visibility === 'unlisted';
    }

    // Check if user can view this playlist via the ID route
    public function canView($user = null)
    {
        // Owner can always view
        if ($user && $this->user_id === $user->id) {
            return true;
        }

        // Only public playlists are accessible via the ID route
        return $this->visibility === 'public';
    }

    // Check if user can view this playlist via the share-token route
    public function canViewViaToken($user = null)
    {
        if ($user && $this->user_id === $user->id) {
            return true;
        }

        return in_array($this->visibility, ['public', 'unlisted']);
    }

    // Check if user can edit this playlist
    public function canEdit($user = null)
    {
        return $user && $this->user_id === $user->id;
    }

    // Update video positions after reordering
    public function reorderVideos($videoIds)
    {
        foreach ($videoIds as $index => $videoId) {
            $this->videos()->updateExistingPivot($videoId, ['position' => $index]);
        }
    }

    // Add video to playlist
    public function addVideo(Video $video)
    {
        if ($this->hasVideo($video)) {
            return false;
        }

        $maxPosition = $this->videos()->max('position') ?? -1;

        $this->videos()->attach($video->id, [
            'position' => $maxPosition + 1,
            'added_at' => now(),
        ]);

        return true;
    }

    // Remove video from playlist
    public function removeVideo(Video $video)
    {
        if (! $this->hasVideo($video)) {
            return false;
        }

        $this->videos()->detach($video->id);

        // Reorder remaining videos
        $this->reorderPositions();

        return true;
    }

    // Reorder positions after removal
    protected function reorderPositions()
    {
        $videos = $this->videos()->orderBy('position')->get();
        $position = 0;

        foreach ($videos as $video) {
            $this->videos()->updateExistingPivot($video->id, ['position' => $position]);
            $position++;
        }
    }

    // Update watch progress for a video in playlist
    public function updateWatchProgress(Video $video, $seconds)
    {
        $pivot = $this->videos()->where('video_id', $video->id)->first()?->pivot;

        if ($pivot) {
            $pivot->watched_seconds = $seconds;
            $pivot->last_watched_at = now();

            // Mark as watched if 90% complete
            if ($video->duration && $seconds >= ($video->duration * 0.9)) {
                $pivot->watched = true;
            }

            $pivot->save();
        }
    }

    // Get watch progress for a video in playlist
    public function getWatchProgress(Video $video)
    {
        $pivot = $this->videos()->where('video_id', $video->id)->first()?->pivot;

        return $pivot ? [
            'watched_seconds' => $pivot->watched_seconds,
            'watched' => $pivot->watched,
            'last_watched_at' => $pivot->last_watched_at,
        ] : null;
    }

    // Get first unwatched video
    public function getFirstUnwatchedVideo()
    {
        return $this->videos()
            ->wherePivot('watched', false)
            ->orderBy('position')
            ->first();
    }

    // Get random video (for shuffle)
    public function getRandomVideo()
    {
        return $this->videos()->inRandomOrder()->first();
    }

    // Static method to create default "Watch Later" playlist
    public static function createWatchLater($userId)
    {
        return self::create([
            'user_id' => $userId,
            'name' => 'Watch Later',
            'description' => 'Save videos to watch later',
            'visibility' => 'private',
            'is_default' => true,
        ]);
    }

    // Get or create watch later playlist for user
    public static function getWatchLater($userId)
    {
        $playlist = self::where('user_id', $userId)
            ->where('is_default', true)
            ->first();

        if (! $playlist) {
            $playlist = self::createWatchLater($userId);
        }

        return $playlist;
    }
}
