<?php

declare(strict_types=1);

class FrontVideoController
{
    public static function routes(): void
    {
        Router::get('/video/{slug}', [self::class, 'show']);
        Router::get('/embed/{slug}', [self::class, 'embed']);
        Router::post('/video/{id}/like', [self::class, 'like']);
        Router::post('/video/{id}/dislike', [self::class, 'dislike']);
        Router::post('/video/{id}/comment', [self::class, 'comment']);
        Router::get('/video/{id}/recommended', [self::class, 'recommendedMore']);
    }

    

    public static function recommendedMore(): void
    {
        $videoId = (int) Router::param('id');
        if ($videoId <= 0) {
            Router::json(['error' => 'bad_request'], 400);
        }

         
         
        $GLOBALS['_cardShowDuration'] = Setting::get('card_show_duration', '1') === '1';
        $GLOBALS['_cardShowTitle']    = Setting::get('card_show_title', '1') === '1';
        $GLOBALS['_cardMetaLeft']     = Setting::get('card_meta_left', 'views');
        $GLOBALS['_cardMetaRight']    = Setting::get('card_meta_right', 'time');
        $GLOBALS['_cardBorderRadius'] = (int) Setting::get('card_border_radius', '8');
        $GLOBALS['_cardTitleLines']   = (int) Setting::get('card_title_lines', '1');

        $exclude = [];
        foreach (explode(',', (string) ($_GET['exclude'] ?? '')) as $e) {
            $e = (int) trim($e);
            if ($e > 0) {
                $exclude[$e] = true;
            }
        }
        $exclude = array_slice(array_keys($exclude), 0, 400);

        $batch  = 18;
        $videos = Video::recommended($videoId, $exclude, $batch);

        $html = '';
        $ids  = [];
        foreach ($videos as $v) {
            $ids[] = (int) ($v['id'] ?? 0);
            $html .= simplyVideoCard($v);
        }

        Router::json([
            'html'    => $html,
            'ids'     => $ids,
            'hasMore' => count($videos) >= $batch,
        ]);
    }

    public static function show(): void
    {
        $video = Video::findBySlug(Router::param('slug'));
         
        if (!$video || (($video['status'] ?? '') !== 'published' && !Auth::isAdmin())) {
            http_response_code(404);
            ThemeRenderer::render('404');
            return;
        }

         
        Router::enforceCanonicalType($video['video_type'], 'video', $video['slug']);

         
        $ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
        $isBot = $ua === '' || strlen($ua) < 10
            || preg_match('/bot|crawl|spider|slurp|facebookexternalhit|Mediapartners|Baiduspider|yandex|sogou|exabot|ia_archiver|semrush|ahref|mj12bot|dotbot|petalbot|bytespider/i', $ua);

        if (!$isBot && HookSystem::applyFilter('video.should_increment_views', true, $video)) {
            Video::incrementViews((int) $video['id']);
        }

        if (Auth::check()) {
            History::record(Auth::id(), (int) $video['id']);
        }

        $userVote = null;
        $isFavorited = false;
        if (Auth::check()) {
            $vote = Database::fetchOne('SELECT type FROM video_likes WHERE user_id = ? AND video_id = ?', [Auth::id(), $video['id']]);
            $userVote = $vote ? $vote['type'] : null;
            $isFavorited = Favorite::isFavorited(Auth::id(), (int) $video['id']);
        }

        $similar = Video::similar((int) $video['id'], (int) Setting::get('related_videos_count', '8'));
        $similarIds = array_column($similar, 'id');
        $recommended = Video::recommended((int) $video['id'], $similarIds, 24);

         
        $alternates = self::buildAlternates((int) $video['id']);
        Router::setAlternates($alternates);

         
        $prefix = ltrim(Router::langPrefix() . Router::typePrefix(), '/');
        $canonicalPath = $prefix !== '' ? $prefix . '/video/' . $video['slug'] : 'video/' . $video['slug'];
        $canonicalUrl = SEO::canonical($canonicalPath);

         
        $siteUrl = SEO::siteUrl();
        $sid = isset($video['storage_id']) ? (int) $video['storage_id'] : null;
        $thumbUrl = '';
        if (!empty($video['video_folder'])) {
            $thumbPath = VideoAsset::thumbUrlFromFolder($video['video_folder'], false, $sid);
            if ($thumbPath !== '') {
                 
                 
                $thumbUrl = (str_starts_with($thumbPath, 'http://') || str_starts_with($thumbPath, 'https://'))
                    ? $thumbPath : $siteUrl . $thumbPath;
            }
        } elseif (!empty($video['thumbnail'])) {
            $thumbUrl = (str_starts_with($video['thumbnail'], 'http://') || str_starts_with($video['thumbnail'], 'https://'))
                ? $video['thumbnail'] : $siteUrl . '/uploads/' . $video['thumbnail'];
        }

        $description = mb_substr(strip_tags($video['description'] ?? ''), 0, 160);

         
        $embedUrl = self::embedUrl($video);

         
        $jsonLd = SEO::videoJsonLd($video, $canonicalUrl);

         
        $siteUrl = SEO::siteUrl();
        $crumbs = [
            ['name' => Setting::get('site_name', 'TubePress'), 'url' => $siteUrl . '/'],
        ];
        $cats = $video['categories'] ?? [];
        if (is_array($cats) && !empty($cats)) {
            $firstCat = $cats[0];
            $catPrefix = ltrim(Router::langPrefix() . Router::typePrefix(), '/');
            $catPath = $catPrefix !== '' ? $catPrefix . '/category/' . $firstCat['slug'] : 'category/' . $firstCat['slug'];
            $crumbs[] = ['name' => $firstCat['name'], 'url' => $siteUrl . '/' . $catPath];
        }
        $crumbs[] = ['name' => $video['title'], 'url' => html_entity_decode($canonicalUrl)];
        $jsonLd .= SEO::breadcrumbJsonLd($crumbs);

         
         
         
        if (!empty($video['performers'])) {
            VideoAsset::assignUniqueThumbFolders($video['performers'], 'video_performers', 'performer_id', 'photo');
        }

        ThemeRenderer::render('video', [
            'pageTitle' => SEO::title($video['title']),
            'metaDescription' => $description,
            'canonicalUrl' => $canonicalUrl,
            'alternates' => $alternates,
            'ogTags' => [
                'og:title' => $video['title'],
                'og:description' => $description,
                'og:url' => $canonicalUrl,
                'og:type' => 'video.other',
                'og:image' => $thumbUrl,
                'og:image:width' => '1280',
                'og:image:height' => '720',
                'og:video' => $embedUrl,
                'og:video:secure_url' => $embedUrl,
                'og:video:type' => 'text/html',
                'og:video:width' => '1280',
                'og:video:height' => '720',
            ],
            'twitterCard' => [
                'twitter:card' => 'player',
                'twitter:title' => $video['title'],
                'twitter:description' => $description,
                'twitter:image' => $thumbUrl,
                'twitter:player' => $embedUrl,
                'twitter:player:width' => '1280',
                'twitter:player:height' => '720',
            ],
            'jsonLd' => $jsonLd,
            'video' => $video,
            'categories' => $video['categories'],
            'tags' => $video['tags'],
            'performers' => $video['performers'],
            'comments' => Comment::forVideo((int) $video['id']),
            'similar' => $similar,
            'recommended' => $recommended,
            'userVote' => $userVote,
            'isFavorited' => $isFavorited,
        ]);
    }

    private static function buildAlternates(int $videoId): array
    {
        $defaultLang = Translator::getDefaultCode();
        $baseVideo = Database::fetchOne('SELECT slug, video_type FROM videos WHERE id = ?', [$videoId]);
        if (!$baseVideo) {
            return [];
        }

        $siteUrl = SEO::siteUrl();
        $typePrefix = Router::typePrefixFor($baseVideo['video_type']);
        $translations = Video::translations($videoId);
        $alternates = [];

         
        $alternates[$defaultLang] = $siteUrl . $typePrefix . '/video/' . $baseVideo['slug'];

         
        foreach (Translator::available() as $lang) {
            $code = $lang['code'];
            if ($code === $defaultLang) continue;
            $slug = (!empty($translations[$code]['slug'])) ? $translations[$code]['slug'] : $baseVideo['slug'];
            $alternates[$code] = $siteUrl . '/' . $code . $typePrefix . '/video/' . $slug;
        }

        $alternates['x-default'] = $alternates[$defaultLang];
        return $alternates;
    }

    

    public static function embed(): void
    {
        $video = Video::findBySlug(Router::param('slug'));
        if (!$video || (($video['status'] ?? '') !== 'published' && !Auth::isAdmin())) {
            http_response_code(404);
            echo '<!DOCTYPE html><html><body><p>Video not found</p></body></html>';
            return;
        }

         
        header_remove('X-Frame-Options');
        header('Content-Type: text/html; charset=UTF-8');

        $title = htmlspecialchars($video['title'] ?? 'Video');

         
        $playerHtml = '';
        if (!empty($video['embed_code'])) {
            $code = Sanitizer::embedCode($video['embed_code']);
            $playerHtml = '<div style="position:absolute;top:0;left:0;width:100%;height:100%">' . $code . '</div>';
        } elseif (!empty($video['video_folder'])) {
            $folder = $video['video_folder'];
            $sid = isset($video['storage_id']) ? (int)$video['storage_id'] : null;
            $src720 = htmlspecialchars(VideoAsset::videoUrl($folder, $sid));
            $src480 = VideoAsset::video480Url($folder, $sid);
            $posterUrl = VideoAsset::thumbUrl($video);
            $poster = $posterUrl !== '' ? ' poster="' . htmlspecialchars($posterUrl) . '"' : '';

            $sources = '<source src="' . $src720 . '" title="720p" type="video/mp4" data-fluid-hd>';
            if ($src480 !== '') {
                $sources .= '<source src="' . htmlspecialchars($src480) . '" title="480p" type="video/mp4">';
            }
            $playerHtml = '<video id="mainVideo" style="width:100%;height:100%"' . $poster . '>' . $sources . '</video>';
        }

         
        echo '<!DOCTYPE html>' . "\n";
        echo '<html><head>' . "\n";
        echo '<meta charset="UTF-8">' . "\n";
        echo '<meta name="robots" content="noindex, nofollow">' . "\n";
        echo '<meta name="viewport" content="width=device-width, initial-scale=1.0">' . "\n";
        echo '<title>' . $title . '</title>' . "\n";
        echo '<style>*{margin:0;padding:0;box-sizing:border-box}html,body{width:100%;height:100%;overflow:hidden;background:#000}video{width:100%;height:100%;object-fit:contain}.fluid_video_wrapper{width:100%!important;height:100%!important}</style>' . "\n";

         
        if (!empty($video['video_folder'])) {
            echo '<link rel="stylesheet" href="https://cdn.fluidplayer.com/v3/current/fluidplayer.min.css">' . "\n";
            echo '<script src="https://cdn.fluidplayer.com/v3/current/fluidplayer.min.js"></script>' . "\n";
        }

        echo '</head><body>' . "\n";
        echo $playerHtml . "\n";

         
        if (!empty($video['video_folder'])) {
            echo '<script>'
                . '(function(){'
                . 'var el=document.getElementById("mainVideo");'
                . 'if(!el||typeof fluidPlayer!=="function")return;'
                . 'fluidPlayer("mainVideo",{layoutControls:{fillToContainer:true,primaryColor:"#2563eb",playButtonShowing:true,playPauseAnimation:true,autoPlay:false,mute:false,allowTheatre:false,miniPlayer:{enabled:false},controlBar:{autoHide:true,autoHideTimeout:3,animated:true}}});'
                . '})();'
                . '</script>' . "\n";
        }

        echo '</body></html>';
        exit;
    }

    

    public static function embedUrl(array $video): string
    {
        if (empty($video['slug'])) return '';
        return SEO::siteUrl() . '/embed/' . $video['slug'];
    }

    public static function like(): void
    {
        self::handleVote('like');
    }

    public static function dislike(): void
    {
        self::handleVote('dislike');
    }

    private static function handleVote(string $type): void
    {
         
        if (!in_array($type, ['like', 'dislike'], true)) {
            Router::json(['error' => 'invalid_type'], 400);
        }

        if (!Router::verifyCsrf()) {
            Router::json(['error' => 'csrf_failed', 'message' => 'Your session has expired. Please refresh the page.'], 403);
        }

        if (!RateLimiter::throttle('vote', 60, 300)) {
            Router::json(['error' => 'rate_limit', 'message' => 'Too many votes. Please try again later.'], 429);
        }

        if (!Auth::check()) {
            Router::json(['error' => 'auth_required', 'message' => 'Please log in to perform this action.'], 401);
        }

        $videoId = (int) Router::param('id');
        $userId = Auth::id();
        $ip = RateLimiter::ip();

         
        $col = $type . 's';  

        $existing = Database::fetchOne('SELECT type FROM video_likes WHERE user_id = ? AND video_id = ?', [$userId, $videoId]);

        if ($existing && $existing['type'] === $type) {
            Database::transaction(function () use ($userId, $videoId, $col) {
                Database::delete('video_likes', 'user_id = ? AND video_id = ?', [$userId, $videoId]);
                Database::query("UPDATE videos SET {$col} = GREATEST(0, {$col} - 1) WHERE id = ?", [$videoId]);
            });
            $userVote = null;
        } elseif ($existing) {
            $oldType = $existing['type'];
             
            $oldCol = in_array($oldType, ['like', 'dislike'], true) ? $oldType . 's' : null;
            if ($oldCol) {
                Database::transaction(function () use ($userId, $videoId, $type, $ip, $oldCol, $col) {
                    Database::update('video_likes', ['type' => $type, 'ip_address' => $ip], 'user_id = ? AND video_id = ?', [$userId, $videoId]);
                    Database::query("UPDATE videos SET {$oldCol} = GREATEST(0, {$oldCol} - 1), {$col} = {$col} + 1 WHERE id = ?", [$videoId]);
                });
            }
            $userVote = $type;
        } else {
            Database::transaction(function () use ($userId, $videoId, $type, $ip, $col) {
                Database::insert('video_likes', ['user_id' => $userId, 'video_id' => $videoId, 'type' => $type, 'ip_address' => $ip]);
                Database::query("UPDATE videos SET {$col} = {$col} + 1 WHERE id = ?", [$videoId]);
            });
            $userVote = $type;
        }

        $video = Database::fetchOne('SELECT likes, dislikes FROM videos WHERE id = ?', [$videoId]);

        Router::json([
            'likes' => (int) $video['likes'],
            'dislikes' => (int) $video['dislikes'],
            'userVote' => $userVote,
        ]);
    }

    public static function comment(): void
    {
        $refPath = parse_url($_SERVER['HTTP_REFERER'] ?? '/', PHP_URL_PATH);
         
        $referer = ($refPath && $refPath[0] === '/' && ($refPath[1] ?? '') !== '/' && ord($refPath[1] ?? '') !== 92) ? $refPath : '/';
        $registrationEnabled = Setting::get('registration_enabled', '1') === '1';

         
        if ($registrationEnabled && !Auth::check()) {
            Router::redirect('/login');
        }

        if (!Router::verifyCsrf()) {
            Router::redirect($referer);
        }

        $videoId = (int) Router::param('id');
        $body = trim($_POST['body'] ?? '');
        $ip = SpamGuard::ip();

         
        $spamError = SpamGuard::validate($body, $ip, $videoId);
        if ($spamError !== null) {
            $_SESSION['flash'] = ['type' => 'error', 'message' => __($spamError)];
            Router::redirect($referer);
        }

         
        if (!Captcha::verify('comment')) {
            $_SESSION['flash'] = ['type' => 'error', 'message' => __('error_captcha_failed')];
            Router::redirect($referer);
        }

        $sanitizedBody = Sanitizer::text($body);
        $status = Setting::get('comments_moderation', '0') === '1' ? 'pending' : 'approved';

        $parentId = !empty($_POST['parent_id']) ? (int) $_POST['parent_id'] : null;
        if ($parentId !== null) {
            $parent = Comment::find($parentId);
            if (!$parent || (int) $parent['video_id'] !== $videoId || $parent['parent_id'] !== null) {
                $parentId = null;  
            }
        }

        $commentData = [
            'video_id'   => $videoId,
            'parent_id'  => $parentId,
            'body'       => $sanitizedBody,
            'body_hash'  => SpamGuard::bodyHash($body),
            'ip_address' => $ip,
            'status'     => $status,
        ];

        if (Auth::check()) {
            $commentData['user_id'] = Auth::id();
        } else {
            $guestName = trim($_POST['guest_name'] ?? '');
            $commentData['guest_name'] = $guestName !== '' ? Sanitizer::text($guestName) : 'Anonymous';
        }

        Comment::create($commentData);

         
        Telegram::notifyComment([
            'pending'     => (($commentData['status'] ?? '') === 'pending'),
            'author'      => Auth::check() ? (string) (Auth::user()['username'] ?? 'User') : (string) ($commentData['guest_name'] ?? 'Anonymous'),
            'video_id'    => $commentData['video_id'] ?? '',
            'body'        => $commentData['body'] ?? '',
        ]);

        Router::redirect($referer);
    }
}
