<?php

namespace App\Http\Controllers\Frontend;

use App\Http\Controllers\Controller;
use App\Models\Anime;
use App\Models\Season;
use App\Models\Episode;
use App\Models\Setting;
use App\Models\Comment;
use App\Services\BunnyCdnSigner;
use App\Services\AniSkipService;

class PlayerController extends Controller
{
    // Public wrapper so API controller can call it
    public static function resolveDubSourcesPublic(?string $m3u8Url, ?array $availableDubs = null): array
    {
        return self::resolveDubSourcesFromM3u8($m3u8Url, $availableDubs);
    }

    public function watch(Anime $anime, int $season, int $episode)
    {
        abort_unless($anime->is_published, 404);

        $seasonModel = Season::where('anime_id', $anime->id)
            ->where('season_number', $season)
            ->firstOrFail();

        $ep = Episode::where('season_id', $seasonModel->id)
            ->where('episode_number', $episode)
            ->where('is_published', true)
            ->firstOrFail();

        $ep->increment('view_count');
        $ep->load('subtitles');

        // Tüm sezonlar + bölümler (playlist için) + türler (bilgi paneli)
        $anime->load([
            'seasons' => fn($q) => $q->orderBy('season_number'),
            'seasons.episodes' => fn($q) => $q->where('is_published', true)->orderBy('episode_number'),
            'genres',
        ]);

        // Önceki / sonraki bölüm
        $prev = Episode::with('season')
            ->where('season_id', $seasonModel->id)
            ->where('episode_number', $episode - 1)
            ->where('is_published', true)
            ->first();

        $next = Episode::with('season')
            ->where('season_id', $seasonModel->id)
            ->where('episode_number', $episode + 1)
            ->where('is_published', true)
            ->first();

        // Sonraki bölüm yoksa bir sonraki sezona geç
        if (!$next) {
            $nextSeason = Season::where('anime_id', $anime->id)
                ->where('season_number', $season + 1)
                ->first();
            if ($nextSeason) {
                $next = Episode::where('season_id', $nextSeason->id)
                    ->where('episode_number', 1)
                    ->where('is_published', true)
                    ->first();
            }
        }

        // JSON-safe subtitle data (proxy URL for CORS bypass)
        $subtitlesData = $ep->subtitles->map(fn($s) => [
            'label'      => $s->label,
            'lang'       => $s->language,
            'url'        => route('vtt.proxy', ['url' => $s->url]),
            'is_default' => (bool) $s->is_default,
        ])->values()->toArray();

        // Dublaj kaynakları — CDN’deki .../{720p|1080p}-{dub}[/master.m3u8] kalıbından türet
        // Embed modda URL video_url’de olabilir (m3u8_url null) — video_url’e fallback
        $raw = $ep->available_dubs;
        $availableDubs = is_array($raw) ? $raw : null; // null = unknown (show all), array = restrict to listed
        $sourceForDubs = $ep->m3u8_url ?: $ep->video_url;
        [$dubSources, $activeDub] = self::resolveDubSourcesFromM3u8($sourceForDubs, $availableDubs);

        // Tüm video URL’lerini imzala (BunnyCDN Token Auth)
        BunnyCdnSigner::signAll($dubSources);
        $ep->m3u8_url  = BunnyCdnSigner::sign($ep->m3u8_url);
        $ep->video_url = BunnyCdnSigner::sign($ep->video_url);

        // Proxy all external HLS streams through our server (CORS + SSL bypass).
        // anizium.co + aniziumserver.* CDN'leri doğrudan yükle (tarayıcı üzerinden).
        $isOwn = fn(?string $u) => !$u || str_contains($u, 'b-cdn.net') || str_contains($u, 'animexe.com')
                                       || str_contains($u, 'anizium.co') || str_contains($u, 'aniziumserver.sbs');
        if ($ep->m3u8_url && !$isOwn($ep->m3u8_url)) {
            $ep->m3u8_url = route("stream.proxy", ["u" => base64_encode((string) $ep->m3u8_url)]);
        }
        // video_url — embed modda anizium HLS URL olabilir; HLS ise proxy'den geçir, MP4 ise bırak.
        if ($ep->video_url && !$isOwn($ep->video_url)) {
            if (str_ends_with((string) $ep->video_url, '.m3u8')) {
                $ep->video_url = route("stream.proxy", ["u" => base64_encode((string) $ep->video_url)]);
            }
        }
        foreach (array_keys($dubSources) as $idx) {
            $dubUrl = (string) ($dubSources[$idx]["url"] ?? "");
            if ($dubUrl && !$isOwn($dubUrl)) {
                $dubSources[$idx]["url"] = route("stream.proxy", ["u" => base64_encode($dubUrl)]);
            }
        }

        // Video sources — Anizium 1080p → 720p → 4K/diğer, sonra AnimeCix
        $videoSourcesData = \App\Models\VideoSource::where('episode_id', $ep->id)
            ->orderBy('sort_order')
            ->get(['id', 'label', 'url', 'type', 'quality', 'translator_id', 'is_default', 'source', 'sort_order', 'is_hevc'])
            ->groupBy(fn($vs) => $vs->translator_id ?: $vs->label)
            ->map(function ($group) {
                $default = $group->firstWhere('is_default', true) ?? $group->first();
                return [
                    'id'         => $default->id,
                    'key'        => $default->translator_id ?: \Illuminate\Support\Str::slug($default->label),
                    'label'      => $default->label ?: 'Kaynak',
                    'url'        => $default->url,
                    'type'       => $default->type ?? 'hls',
                    'source'     => $default->source ?? 'animecix',
                    'quality'    => $default->quality ?? '',
                    'sort_order' => $default->sort_order ?? 99,
                    'is_hevc'    => (bool) $default->is_hevc,
                ];
            })
            ->sortBy(function ($item) {
                $isAnizium = ($item['source'] === 'anizium');
                $q = strtolower($item['quality'] ?? '');
                if ($isAnizium) {
                    if (str_contains($q, '1080')) return 0;
                    if (str_contains($q, '720'))  return 1;
                    return 1000; // 4K / H.265 / diğer → en sona
                }
                return 10 + ($item['sort_order'] ?? 99); // AnimeCix
            })
            ->values()
            ->toArray();

        // Sonraki bölüm URL'si
        $nextUrl = null;
        if ($next) {
            $nextSeasonNum = $next->season?->season_number ?? $seasonModel->season_number;
            if (!$next->season) {
                $nextSeason2 = Season::find($next->season_id);
                $nextSeasonNum = $nextSeason2?->season_number ?? $seasonModel->season_number;
            }
            $nextUrl = route('watch', [$anime->slug, $nextSeasonNum, $next->episode_number]);
        }

        // Intro video ayarları
        $introUrl           = Setting::get('intro_enabled') == '1' ? (Setting::get('intro_video_url') ?: null) : null;
        $introSkipAfter     = (int) Setting::get('intro_skip_after', 5);
        $mainVideoSkipSec   = (int) Setting::get('main_video_skip_seconds', 10);
        $wmCoverSeconds     = (int) Setting::get('watermark_cover_seconds', 11);

        // İntro atla: önce bölüme elle girilmiş zamanlar, yoksa AniSkip API
        $aniSkip = null;

        if ($ep->intro_start !== null && $ep->intro_end !== null && $ep->intro_end > $ep->intro_start) {
            // Manuel giriş — en güvenilir
            $aniSkip = ['op' => ['start' => (float)$ep->intro_start, 'end' => (float)$ep->intro_end]];
        } else {
            $seasonMalId = $seasonModel->mal_id;

            // season.mal_id yoksa akıllı fallback (Jikan'a gitme, bloke olur)
            if (!$seasonMalId && $anime->mal_id) {
                // Sezon 1 için anime.mal_id direkt kullanılabilir
                // Diğer sezonlar için background job yerine cache'li Jikan
                if ($seasonModel->season_number === 1) {
                    $seasonMalId = $anime->mal_id;
                    $seasonModel->update(['mal_id' => $seasonMalId]);
                } else {
                    // Sequel chain'i sadece cache'li olarak dene (timeout kısa, bloke etmez)
                    try {
                        $cacheKey = "jikan_chain_{$anime->mal_id}";
                        $chain = \Illuminate\Support\Facades\Cache::get($cacheKey);
                        if (!$chain) {
                            // Cache yoksa arka planda doldur, bu istek için atla
                            dispatch(function () use ($anime) {
                                $chain = (new \App\Services\JikanService())->fetchSeasonMalIds($anime->mal_id);
                                if ($chain) {
                                    \Illuminate\Support\Facades\Cache::put("jikan_chain_{$anime->mal_id}", $chain, 60 * 60 * 24 * 7);
                                    foreach ($anime->seasons()->orderBy('season_number')->get() as $i => $s) {
                                        if (!$s->mal_id && isset($chain[$i])) $s->update(['mal_id' => $chain[$i]]);
                                    }
                                }
                            })->afterResponse();
                        } else {
                            $idx = $seasonModel->season_number - 1;
                            $seasonMalId = $chain[$idx] ?? $chain[0] ?? null;
                            if ($seasonMalId) $seasonModel->update(['mal_id' => $seasonMalId]);
                        }
                    } catch (\Throwable) {}
                }
            }

            // anime.mal_id de yoksa Jikan title search (sadece bir kez, cache'lenir)
            if (!$seasonMalId && !$anime->mal_id) {
                try {
                    $found = (new AniSkipService())->searchByTitle($anime->title, $anime->title_en, $anime->title_jp);
                    if ($found) {
                        $anime->update(['mal_id' => $found]);
                        $seasonMalId = $found;
                        if ($seasonModel->season_number === 1) $seasonModel->update(['mal_id' => $found]);
                    }
                } catch (\Throwable) {}
            }

            if ($seasonMalId) {
                try {
                    $aniSkip = (new AniSkipService())->getSkipTimes((string)$seasonMalId, $ep->episode_number);
                } catch (\Throwable) {}
            }
        }

        // İzleme ilerlemeleri (sidebar progress bar için)
        $watchProgress = [];
        if (auth()->check()) {
            $progRows = \App\Models\ContinueWatching::where('user_id', auth()->id())
                ->where('anime_id', $anime->id)
                ->get(['episode_id', 'percent_complete']);
            foreach ($progRows as $row) {
                $watchProgress[$row->episode_id] = (int) $row->percent_complete;
            }
        }

        // Premium kullanıcı HİÇBİR reklam görmez (hem eski VAST hem yeni MP4 sistemi)
        $isPremiumUser = auth()->check() && auth()->user()->isPremium();

        $adsConfig = [
            'enabled'       => !$isPremiumUser && Setting::get('ads_enabled', '0') === '1',
            'vast_url'      => Setting::get('ads_vast_url', ''),
            'freq_episodes' => (int) Setting::get('ads_freq_episodes', 4),
            'freq_minutes'  => (int) Setting::get('ads_freq_minutes', 10),
        ];

        // ── Kendi MP4 pre-roll reklam sistemi ────────────────────────────────
        // Premium kullanıcı reklam görmez. mode: 'ad' | 'upsell' | null
        $vadConfig = ['mode' => null];
        if (!$isPremiumUser && Setting::get('vad_enabled', '0') === '1') {
            $upsellPercent = (int) Setting::get('vad_upsell_percent', 20);
            $ad = null;
            $mode = null;
            if (random_int(1, 100) <= $upsellPercent) {
                $mode = 'upsell';
            } else {
                $ad = \App\Models\Ad::pickVideo();
                if ($ad && $ad->media_url) {
                    $mode = 'ad';
                } elseif ($upsellPercent > 0) {
                    $mode = 'upsell'; // hiç video reklam yoksa upsell göster
                }
            }
            $vadConfig = [
                'mode'          => $mode,
                'ad'            => $mode === 'ad' ? [
                    'id'         => $ad->id,
                    'url'        => $ad->media_url,
                    'click_url'  => $ad->click_url,
                    'skip_after' => (int) $ad->skip_after,
                ] : null,
                'freq_episodes' => (int) Setting::get('vad_freq_episodes', 2),
                'freq_minutes'  => (int) Setting::get('vad_freq_minutes', 5),
                'premium_url'   => route('premium.plans'),
            ];
        }

        // Kendi reklamımız gösterilecekse IMA/VAST devreye girmesin
        if (!empty($vadConfig['mode'])) {
            $adsConfig['enabled'] = false;
        }

        return response()
            ->view('frontend.player', compact(
                'anime', 'ep', 'seasonModel', 'prev', 'next',
                'subtitlesData', 'nextUrl', 'dubSources', 'activeDub',
                'introUrl', 'introSkipAfter', 'mainVideoSkipSec', 'wmCoverSeconds',
                'aniSkip', 'watchProgress', 'videoSourcesData', 'adsConfig', 'vadConfig'
            ))
            ->header('Cache-Control', 'private, no-store, no-cache, must-revalidate')
            ->header('Pragma', 'no-cache')
            ->header('X-Player-Version', '3');
    }

    /**
     * m3u8 URL içinden kalite+dublaj klasörünü bulup diğer dublaj varyantlarının URL'lerini üretir.
     * Örnekler:
     * - https://f.aniziumserver.sbs/85937/1/1/1080p-original/master.m3u8
     * - https://host/cdn/x/1/01/720p-trdub/
     * - https://xxx.b-cdn.net/.../1080p_endub/index.m3u8
     *
     * @return array{0: array<int, array{key:string,label:string,url:string,active:bool}>, 1: ?string}
     */
    protected static function resolveDubSourcesFromM3u8(?string $m3u8Url, ?array $availableDubs = null): array
    {
        if (!$m3u8Url || ! is_string($m3u8Url)) {
            return [[], null];
        }

        $u = rtrim(preg_replace('/[?#].*$/', '', trim($m3u8Url)), '/');
        if ($u === '') {
            return [[], null];
        }

        // Sondaki playlist dosyasını çıkar (master.m3u8, index.m3u8, video.m3u8, …)
        if (preg_match('#/[^/]+\.m3u8$#i', $u)) {
            $u = rtrim(preg_replace('#/[^/]+\.m3u8$#i', '', $u), '/');
        }

        // Son segment: 720p-original, 1080p_trdub, 480p-endub
        if (! preg_match('#^(.*)/(\d{3,4}p)([-_])([a-zA-Z0-9_-]+)$#', $u, $m)) {
            return [[], null];
        }

        $parent = $m[1];
        $quality = $m[2];
        $sep = $m[3];
        $activeDub = strtolower($m[4]);

        $dubLabels = [
            'trdub'    => 'Türkçe Dublaj',
            'original' => 'Japonca (Orijinal)',
            'endub'    => 'İngilizce Dublaj',
            // Dynamic: any unrecognised key gets a generic label below
        ];

        $raw = rtrim(preg_replace('/[?#].*$/', '', trim($m3u8Url)), '/');
        $suffix = '';
        if (preg_match('#/(\d{3,4}p)([-_])([a-zA-Z0-9_-]+)(/.*)$#i', $raw, $tail)) {
            $suffix = $tail[4];
        }

        // Which dub keys to include:
        // • null → column not yet migrated (legacy): show all standard dubs
        // • [] empty array → no dub info, show only active
        // • ['trdub','original',...] → restrict to listed keys
        if ($availableDubs === null) {
            // Bilinmiyor: aktif dub trdub/endub ise orijinal (JP) de büyük ihtimalle var.
            // Aktif dub zaten original ise başka dub olmadığını varsay (false positive önle).
            $keys = $activeDub !== 'original' ? [$activeDub, 'original'] : [$activeDub];
        } elseif (count($availableDubs) === 0) {
            // Bot açıkça "dub bilgisi yok" dedi — sadece aktif
            $keys = [$activeDub];
        } else {
            $keys = $availableDubs;
        }

        // Tekrarları at, aktif dub'ı öne al
        $seen    = [];
        $sources = [];
        // Aktif dub her zaman ilk sıraya
        if (!in_array($activeDub, $keys)) array_unshift($keys, $activeDub);
        foreach ($keys as $key) {
            if (isset($seen[$key])) continue;
            $seen[$key] = true;
            $label = $dubLabels[$key] ?? ucfirst($key) . ' Dublaj';
            $url   = $parent . '/' . $quality . $sep . $key . $suffix;
            $sources[] = [
                'key'    => $key,
                'label'  => $label,
                'url'    => $url,
                'active' => $key === $activeDub,
            ];
        }

        return [$sources, $activeDub];
    }
}
