<?php

declare(strict_types=1);

class Video
{
     
    public const VALID_TYPES = ['hetero', 'gay', 'hentai'];
    public const DEFAULT_TYPE = 'hetero';

    

    public static function localizeResults(array $videos): array
    {
        if (empty($videos) || Translator::locale() === Translator::getDefaultCode()) {
            return $videos;
        }

        $ids = array_column($videos, 'id');
        if (empty($ids)) {
            return $videos;
        }

        $placeholders = implode(',', array_fill(0, count($ids), '?'));
        $params = array_merge([Translator::locale()], $ids);

        $rows = Database::fetchAll(
            "SELECT video_id, title, description, slug FROM video_translations WHERE lang_code = ? AND video_id IN ({$placeholders})",
            $params
        );

        $map = [];
        foreach ($rows as $row) {
            $map[(int) $row['video_id']] = $row;
        }

        foreach ($videos as &$video) {
            $vid = (int) $video['id'];
            if (isset($map[$vid])) {
                $tr = $map[$vid];
                if ($tr['title'] !== null && $tr['title'] !== '') {
                    $video['title'] = $tr['title'];
                }
                if ($tr['description'] !== null && $tr['description'] !== '') {
                    $video['description'] = $tr['description'];
                }
                if ($tr['slug'] !== null && $tr['slug'] !== '') {
                    $video['slug'] = $tr['slug'];
                }
            }
        }
        unset($video);

        return $videos;
    }

    

    public static function localize(?array $video): ?array
    {
        if ($video === null || Translator::locale() === Translator::getDefaultCode()) {
            return $video;
        }

        $row = Database::fetchOne(
            'SELECT title, description, slug FROM video_translations WHERE video_id = ? AND lang_code = ?',
            [(int) $video['id'], Translator::locale()]
        );

        if ($row) {
            if ($row['title'] !== null && $row['title'] !== '') {
                $video['title'] = $row['title'];
            }
            if ($row['description'] !== null && $row['description'] !== '') {
                $video['description'] = $row['description'];
            }
            if ($row['slug'] !== null && $row['slug'] !== '') {
                $video['slug'] = $row['slug'];
            }
        }

        return $video;
    }

    private static ?array $_availableTypesCache = null;

    public static function resetAvailableTypesCache(): void
    {
        self::$_availableTypesCache = null;
    }

    

    public static function defaultType(): string
    {
        $configured = Setting::get('default_video_type', '');
        if ($configured !== '' && in_array($configured, self::VALID_TYPES, true)) {
            return $configured;
        }
        return self::DEFAULT_TYPE;
    }

    

    public static function typeLabel(string $code): string
    {
        $defaults = ['hetero' => 'Straight', 'gay' => 'Gay', 'hentai' => 'Hentai'];
        return Setting::get('seo_type_label_' . $code, $defaults[$code] ?? ucfirst($code));
    }

    

    public static function typeLabels(): array
    {
        $labels = [];
        foreach (self::VALID_TYPES as $code) {
            $labels[$code] = self::typeLabel($code);
        }
        return $labels;
    }

    

    public static function typeSlug(string $code): string
    {
        $slug = Setting::get('video_type_slug_' . $code, '');
        return ($slug !== '') ? $slug : $code;
    }

    

    public static function typeFromSlug(string $slug): string
    {
        foreach (self::VALID_TYPES as $code) {
            if (self::typeSlug($code) === $slug) {
                return $code;
            }
        }
        return '';
    }

    

    public static function typeFieldOrder(string $column = 'video_type'): string
    {
        $quoted = array_map(fn($t) => "'" . $t . "'", self::VALID_TYPES);
        return 'FIELD(' . $column . ', ' . implode(', ', $quoted) . ')';
    }

    public static function availableTypes(): array
    {
        if (self::$_availableTypesCache !== null) {
            return self::$_availableTypesCache;
        }
        $rows = Database::fetchAll(
            "SELECT DISTINCT video_type FROM videos WHERE status = 'published'"
        );
        $found = array_column($rows, 'video_type');

         
        $default = self::defaultType();
        $ordered = [];
        if (in_array($default, $found, true)) {
            $ordered[] = $default;
        }
        foreach (self::VALID_TYPES as $t) {
            if ($t !== $default && in_array($t, $found, true)) {
                $ordered[] = $t;
            }
        }

        self::$_availableTypesCache = $ordered;
        return self::$_availableTypesCache;
    }

    public static function activeType(): string
    {
        $types = self::availableTypes();
        if (count($types) < 2) {
            return '';
        }
         
        $routerType = Router::typeCode();
        if ($routerType !== '' && in_array($routerType, $types, true)) {
            return $routerType;
        }
        $cookie = $_COOKIE['tp_video_type'] ?? '';
        if ($cookie !== '' && in_array($cookie, $types, true)) {
            return $cookie;
        }
        return $types[0] ?? '';
    }

    public static function find(int $id): ?array
    {
        $video = Database::fetchOne('SELECT * FROM videos WHERE id = ?', [$id]);
        if ($video) {
            self::loadRelations($video);
        }
        return $video;
    }

    public static function findBySlug(string $slug): ?array
    {
         
        if (Translator::locale() !== Translator::getDefaultCode()) {
            $trans = Database::fetchOne(
                'SELECT video_id FROM video_translations WHERE slug = ? AND lang_code = ?',
                [$slug, Translator::locale()]
            );
            if ($trans) {
                $video = self::find((int) $trans['video_id']);
                return $video ? self::localize($video) : null;
            }
        }

         
        $video = Database::fetchOne('SELECT * FROM videos WHERE slug = ?', [$slug]);
        if ($video) {
            self::loadRelations($video);
            $video = self::localize($video);
        }
        return $video;
    }

    

    private static function loadRelations(array &$video): void
    {
        $id = (int) $video['id'];
        $rows = Database::fetchAll(
            "SELECT 'category' as _type, c.id, c.name, c.slug, c.video_type, c.video_count, c.total_views, c.thumbnail, c.parent_id, c.sort_order, c.description, c.created_at FROM categories c JOIN video_categories vc ON c.id = vc.category_id WHERE vc.video_id = ?
             UNION ALL
             SELECT 'tag', t.id, t.name, t.slug, t.video_type, t.video_count, NULL, NULL, NULL, NULL, NULL, t.created_at FROM tags t JOIN video_tags vt ON t.id = vt.tag_id WHERE vt.video_id = ?
             UNION ALL
             SELECT 'performer', p.id, p.name, p.slug, p.video_type, p.video_count, p.total_views, p.photo, NULL, NULL, p.bio, p.created_at FROM performers p JOIN video_performers vp ON p.id = vp.performer_id WHERE vp.video_id = ?
             UNION ALL
             SELECT 'channel', s.id, s.name, s.slug, s.video_type, s.video_count, s.total_views, s.logo, NULL, NULL, s.description, s.created_at FROM channels s JOIN video_channels vs ON s.id = vs.channel_id WHERE vs.video_id = ?",
            [$id, $id, $id, $id]
        );

        $video['categories'] = [];
        $video['tags'] = [];
        $video['performers'] = [];
        $video['channels'] = [];

        foreach ($rows as $row) {
            $type = $row['_type'];
            unset($row['_type']);
            $video[$type === 'category' ? 'categories' : ($type === 'tag' ? 'tags' : ($type === 'performer' ? 'performers' : 'channels'))][] = $row;
        }

         
        $video['categories'] = Category::localizeResults($video['categories']);
        $video['tags']       = Tag::localizeResults($video['tags']);
        $video['performers'] = Performer::localizeResults($video['performers']);
        $video['channels']    = Channel::localizeResults($video['channels']);
    }

    public static function all(array $filters = [], int $limit = 24, int $offset = 0): array
    {
        $where = ['1=1'];
        $whereParams = [];
        $joins = [];
        $joinParams = [];

         
        if (!empty($filters['admin'])) {
            if (!empty($filters['status'])) {
                $where[] = 'v.status = ?';
                $whereParams[] = $filters['status'];
            }
        } elseif (!empty($filters['status'])) {
            $where[] = 'v.status = ?';
            $whereParams[] = $filters['status'];
        } else {
            $where[] = 'v.status = ?';
            $whereParams[] = 'published';
        }

        if (!empty($filters['category_id'])) {
            $joins[] = 'INNER JOIN video_categories vc_filter ON v.id = vc_filter.video_id AND vc_filter.category_id = ?';
            $joinParams[] = $filters['category_id'];
        }

        if (!empty($filters['tag_id'])) {
            $joins[] = 'INNER JOIN video_tags vt_filter ON v.id = vt_filter.video_id AND vt_filter.tag_id = ?';
            $joinParams[] = $filters['tag_id'];
        }

        if (!empty($filters['performer_id'])) {
            $joins[] = 'INNER JOIN video_performers vp_filter ON v.id = vp_filter.video_id AND vp_filter.performer_id = ?';
            $joinParams[] = $filters['performer_id'];
        }

        if (!empty($filters['channel_id'])) {
            $joins[] = 'INNER JOIN video_channels vs_filter ON v.id = vs_filter.video_id AND vs_filter.channel_id = ?';
            $joinParams[] = $filters['channel_id'];
        }

        if (!empty($filters['search'])) {
            $where[] = 'MATCH(v.title, v.description) AGAINST(? IN BOOLEAN MODE)';
            $whereParams[] = $filters['search'];
        }

        if (!empty($filters['is_featured'])) {
            $where[] = 'v.is_featured = 1';
        }

        if (isset($filters['has_thumbnail'])) {
            if ($filters['has_thumbnail'] === 'yes') {
                $where[] = "(v.video_folder IS NOT NULL OR (v.thumbnail IS NOT NULL AND v.thumbnail != ''))";
            } elseif ($filters['has_thumbnail'] === 'no') {
                $where[] = "(v.video_folder IS NULL AND (v.thumbnail IS NULL OR v.thumbnail = ''))";
            }
        }

        if (!empty($filters['video_type'])) {
            $where[] = 'v.video_type = ?';
            $whereParams[] = $filters['video_type'];
        }

         
        $allowedSorts = [
            'newest' => 'v.published_at DESC, v.id DESC',
            'popular' => 'v.views DESC',
            'most_liked' => 'v.likes DESC',
            'top_rated' => 'v.likes DESC',
            'trending' => 'v.views DESC',
            'oldest' => 'v.created_at ASC',
            'most_favorited' => 'v.favorites_count DESC',
        ];

         
        $allowedSorts = HookSystem::applyFilter('video.sort_options', $allowedSorts);

        $sortKey = $filters['sort'] ?? 'newest';
        $sort = $allowedSorts[$sortKey] ?? $allowedSorts['newest'];

        $joinStr = implode("\n", $joins);
        $whereStr = implode(' AND ', $where);
        $sql = "SELECT v.*
                FROM videos v
                {$joinStr}
                WHERE {$whereStr}
                ORDER BY {$sort} LIMIT ? OFFSET ?";
        $params = array_merge($joinParams, $whereParams, [$limit, $offset]);

        $results = Database::fetchAll($sql, $params);
        return empty($filters['admin']) ? self::localizeResults($results) : $results;
    }

    public static function count(array $filters = []): int
    {
        $where = ['1=1'];
        $whereParams = [];
        $joins = [];
        $joinParams = [];

        if (!empty($filters['status'])) {
            $where[] = 'v.status = ?';
            $whereParams[] = $filters['status'];
        } elseif (empty($filters['admin'])) {
            $where[] = 'v.status = ?';
            $whereParams[] = 'published';
        }

        if (!empty($filters['category_id'])) {
            $joins[] = 'INNER JOIN video_categories vc_filter ON v.id = vc_filter.video_id AND vc_filter.category_id = ?';
            $joinParams[] = $filters['category_id'];
        }

        if (!empty($filters['tag_id'])) {
            $joins[] = 'INNER JOIN video_tags vt_filter ON v.id = vt_filter.video_id AND vt_filter.tag_id = ?';
            $joinParams[] = $filters['tag_id'];
        }

        if (!empty($filters['performer_id'])) {
            $joins[] = 'INNER JOIN video_performers vp_filter ON v.id = vp_filter.video_id AND vp_filter.performer_id = ?';
            $joinParams[] = $filters['performer_id'];
        }

        if (!empty($filters['channel_id'])) {
            $joins[] = 'INNER JOIN video_channels vs_filter ON v.id = vs_filter.video_id AND vs_filter.channel_id = ?';
            $joinParams[] = $filters['channel_id'];
        }

        if (!empty($filters['search'])) {
            $where[] = 'MATCH(v.title, v.description) AGAINST(? IN BOOLEAN MODE)';
            $whereParams[] = $filters['search'];
        }

        if (!empty($filters['is_featured'])) {
            $where[] = 'v.is_featured = 1';
        }

        if (isset($filters['has_thumbnail'])) {
            if ($filters['has_thumbnail'] === 'yes') {
                $where[] = "(v.video_folder IS NOT NULL OR (v.thumbnail IS NOT NULL AND v.thumbnail != ''))";
            } elseif ($filters['has_thumbnail'] === 'no') {
                $where[] = "(v.video_folder IS NULL AND (v.thumbnail IS NULL OR v.thumbnail = ''))";
            }
        }

        if (!empty($filters['video_type'])) {
            $where[] = 'v.video_type = ?';
            $whereParams[] = $filters['video_type'];
        }

        $joinStr = implode("\n", $joins);
        $whereStr = implode(' AND ', $where);
        $sql = "SELECT COUNT(DISTINCT v.id) FROM videos v {$joinStr} WHERE {$whereStr}";
        $params = array_merge($joinParams, $whereParams);

        $result = Database::fetchOne($sql, $params);
        return (int) ($result ? reset($result) : 0);
    }

    public static function create(array $data): int
    {
        return Database::insert('videos', $data);
    }

    public static function update(int $id, array $data): void
    {
        Database::update('videos', $data, 'id = ?', [$id]);
    }

    public static function delete(int $id): void
    {
        $video = Database::fetchOne('SELECT video_folder, storage_id, thumbnail FROM videos WHERE id = ?', [$id]);

        Database::transaction(function () use ($id) {
             
            $categoryIds = array_column(
                Database::fetchAll('SELECT category_id FROM video_categories WHERE video_id = ?', [$id]),
                'category_id'
            );
            $tagIds = array_column(
                Database::fetchAll('SELECT tag_id FROM video_tags WHERE video_id = ?', [$id]),
                'tag_id'
            );
            $performerIds = array_column(
                Database::fetchAll('SELECT performer_id FROM video_performers WHERE video_id = ?', [$id]),
                'performer_id'
            );
            $channelIds = array_column(
                Database::fetchAll('SELECT channel_id FROM video_channels WHERE video_id = ?', [$id]),
                'channel_id'
            );

            Database::delete('reports', 'video_id = ?', [$id]);

             
            $activeWorkers = Database::fetchAll(
                "SELECT worker, COUNT(*) as cnt FROM transcode_jobs WHERE video_id = ? AND status = 'processing' AND worker IS NOT NULL GROUP BY worker",
                [$id]
            );
            foreach ($activeWorkers as $aw) {
                Database::query("UPDATE transcode_servers SET active_jobs = IF(active_jobs >= ?, active_jobs - ?, 0) WHERE name = ?", [(int)$aw['cnt'], (int)$aw['cnt'], $aw['worker']]);
            }
            Database::delete('transcode_jobs', 'video_id = ?', [$id]);

            Database::delete('videos', 'id = ?', [$id]);

             
            self::cleanupOrphansAndUpdateCounts('categories', 'video_categories', 'category_id', $categoryIds, Category::class);
            self::cleanupOrphansAndUpdateCounts('tags', 'video_tags', 'tag_id', $tagIds, Tag::class);
            self::cleanupOrphansAndUpdateCounts('performers', 'video_performers', 'performer_id', $performerIds, Performer::class);
            self::cleanupOrphansAndUpdateCounts('channels', 'video_channels', 'channel_id', $channelIds, Channel::class);
        });

         
        if ($video && !empty($video['video_folder'])) {
            VideoAsset::deleteFolder($video['video_folder'], $video['storage_id'] ? (int)$video['storage_id'] : null);
        }
    }

    

    private static function cleanupOrphansAndUpdateCounts(string $table, string $pivotTable, string $fkColumn, array $ids, string $modelClass): void
    {
        if (empty($ids)) {
            return;
        }

        $ids = array_map('intval', $ids);
        $ph = implode(',', array_fill(0, count($ids), '?'));

         
         
         
         
         
        Database::query(
            "DELETE FROM {$table} WHERE id IN ({$ph})
               AND id NOT IN (SELECT DISTINCT {$fkColumn} FROM {$pivotTable})",
            $ids
        );

         
        $survivors = Database::fetchAll(
            "SELECT id FROM {$table} WHERE id IN ({$ph})",
            $ids
        );
        foreach ($survivors as $row) {
            $modelClass::updateCounts((int) $row['id']);
        }
    }

    public static function syncCategories(int $id, array $categoryIds): void
    {
         
         
         
        $categoryIds = array_values(array_unique(array_map('intval', $categoryIds)));
        Database::transaction(function () use ($id, $categoryIds) {
            $oldIds = array_column(
                Database::fetchAll('SELECT category_id FROM video_categories WHERE video_id = ?', [$id]),
                'category_id'
            );
            Database::delete('video_categories', 'video_id = ?', [$id]);
            foreach ($categoryIds as $catId) {
                Database::insert('video_categories', ['video_id' => $id, 'category_id' => (int) $catId]);
            }
            $affected = array_unique(array_merge(array_map('intval', $oldIds), array_map('intval', $categoryIds)));
            self::batchUpdateEntityCounts($affected, Category::class);
        });
    }

    public static function syncTags(int $id, array $tagIds): void
    {
        $tagIds = array_values(array_unique(array_map('intval', $tagIds)));
        Database::transaction(function () use ($id, $tagIds) {
            $oldIds = array_column(
                Database::fetchAll('SELECT tag_id FROM video_tags WHERE video_id = ?', [$id]),
                'tag_id'
            );
            Database::delete('video_tags', 'video_id = ?', [$id]);
            foreach ($tagIds as $tagId) {
                Database::insert('video_tags', ['video_id' => $id, 'tag_id' => (int) $tagId]);
            }
            $affected = array_unique(array_merge(array_map('intval', $oldIds), array_map('intval', $tagIds)));
            self::batchUpdateEntityCounts($affected, Tag::class);
        });
    }

    

    private static function batchUpdateEntityCounts(array $ids, string $modelClass): void
    {
        foreach ($ids as $entityId) {
            $modelClass::updateCounts($entityId);
        }
    }

    public static function syncPerformers(int $id, array $performerIds): void
    {
        $performerIds = array_values(array_unique(array_map('intval', $performerIds)));
        Database::transaction(function () use ($id, $performerIds) {
            $oldIds = array_column(
                Database::fetchAll('SELECT performer_id FROM video_performers WHERE video_id = ?', [$id]),
                'performer_id'
            );
            Database::delete('video_performers', 'video_id = ?', [$id]);
            foreach ($performerIds as $perfId) {
                Database::insert('video_performers', ['video_id' => $id, 'performer_id' => (int) $perfId]);
            }
            $affected = array_unique(array_merge(array_map('intval', $oldIds), array_map('intval', $performerIds)));
            foreach ($affected as $pId) {
                Performer::updateCounts($pId);
            }
             
            self::refreshPerformerName($id);
        });
    }

    

    public static function refreshPerformerName(int $id): void
    {
        $row = Database::fetchOne(
            'SELECT MIN(p.name) as pname FROM video_performers vp JOIN performers p ON vp.performer_id = p.id WHERE vp.video_id = ?',
            [$id]
        );
        Database::query('UPDATE videos SET performer_name = ? WHERE id = ?', [$row['pname'] ?? null, $id]);
    }

    

    public static function refreshFavoritesCount(int $id): void
    {
        $cnt = Database::count('user_favorites', 'video_id = ?', [$id]);
        Database::query('UPDATE videos SET favorites_count = ? WHERE id = ?', [$cnt, $id]);
    }

    

    public static function incrementViews(int $id): void
    {
        $ip = RateLimiter::ip();
        $fp = RateLimiter::fingerprint();
        $hash = md5($ip . $fp);
        $window = 30;  

         
         
         
        $stmt = Database::query(
            'INSERT INTO video_view_log (video_id, viewer_hash, viewed_at)
             VALUES (?, UNHEX(?), NOW())
             ON DUPLICATE KEY UPDATE
                viewed_at = IF(viewed_at <= DATE_SUB(NOW(), INTERVAL ? MINUTE), NOW(), viewed_at)',
            [$id, $hash, $window]
        );

         
        if ($stmt->rowCount() > 0) {
            Database::query('UPDATE videos SET views = views + 1, recent_views = recent_views + 1 WHERE id = ?', [$id]);
        }

         
        if (random_int(1, 100) === 1) {
            Database::query('DELETE FROM video_view_log WHERE viewed_at < DATE_SUB(NOW(), INTERVAL 2 HOUR) LIMIT 10000');
        }
    }

    public static function getCategories(int $id): array
    {
        return Database::fetchAll(
            'SELECT c.* FROM categories c JOIN video_categories vc ON c.id = vc.category_id WHERE vc.video_id = ?',
            [$id]
        );
    }

    public static function getTags(int $id): array
    {
        return Database::fetchAll(
            'SELECT t.* FROM tags t JOIN video_tags vt ON t.id = vt.tag_id WHERE vt.video_id = ?',
            [$id]
        );
    }

    public static function getPerformers(int $id): array
    {
        return Database::fetchAll(
            'SELECT p.* FROM performers p JOIN video_performers vp ON p.id = vp.performer_id WHERE vp.video_id = ?',
            [$id]
        );
    }

    public static function getChannels(int $id): array
    {
        return Database::fetchAll(
            'SELECT s.* FROM channels s JOIN video_channels vs ON s.id = vs.channel_id WHERE vs.video_id = ?',
            [$id]
        );
    }

    public static function syncChannels(int $id, array $channelIds): void
    {
        $channelIds = array_values(array_unique(array_map('intval', $channelIds)));
        Database::transaction(function () use ($id, $channelIds) {
            $oldIds = array_column(
                Database::fetchAll('SELECT channel_id FROM video_channels WHERE video_id = ?', [$id]),
                'channel_id'
            );
            Database::delete('video_channels', 'video_id = ?', [$id]);
            foreach ($channelIds as $channelId) {
                Database::insert('video_channels', ['video_id' => $id, 'channel_id' => (int) $channelId]);
            }
            $affected = array_unique(array_merge(array_map('intval', $oldIds), array_map('intval', $channelIds)));
            self::batchUpdateEntityCounts($affected, Channel::class);
        });
    }

    public static function similar(int $id, int $limit = 8): array
    {
        $override = HookSystem::applyFilter('video.similar', null, $id, $limit);
        if ($override !== null) {
            return self::localizeResults($override);
        }

         
        $catIds = array_column(
            Database::fetchAll('SELECT category_id FROM video_categories WHERE video_id = ?', [$id]),
            'category_id'
        );
        $tagIds = array_column(
            Database::fetchAll('SELECT tag_id FROM video_tags WHERE video_id = ?', [$id]),
            'tag_id'
        );

        if (empty($catIds) && empty($tagIds)) {
            return [];
        }

         
        $joins = [];
        $joinParams = [];

        if (!empty($catIds)) {
            $ph = implode(',', array_fill(0, count($catIds), '?'));
            $joins[] = "LEFT JOIN video_categories vc_sim ON v.id = vc_sim.video_id AND vc_sim.category_id IN ({$ph})";
            $joinParams = array_merge($joinParams, $catIds);
        }
        if (!empty($tagIds)) {
            $ph = implode(',', array_fill(0, count($tagIds), '?'));
            $joins[] = "LEFT JOIN video_tags vt_sim ON v.id = vt_sim.video_id AND vt_sim.tag_id IN ({$ph})";
            $joinParams = array_merge($joinParams, $tagIds);
        }

        $joinStr = implode("\n", $joins);
        $having = [];
        if (!empty($catIds)) $having[] = 'COUNT(DISTINCT vc_sim.category_id)';
        if (!empty($tagIds)) $having[] = 'COUNT(DISTINCT vt_sim.tag_id)';
        $relevance = implode(' + ', $having);
         
         
        $params = array_merge($joinParams, ['published', $id], [$limit]);

        return self::localizeResults(Database::fetchAll(
            "SELECT v.*, ({$relevance}) as _relevance
             FROM videos v
             {$joinStr}
             WHERE v.status = ? AND v.id != ?
             GROUP BY v.id
             HAVING _relevance > 0
             ORDER BY _relevance DESC, v.views DESC LIMIT ?",
            $params
        ));
    }

    public static function recommended(int $excludeId, array $excludeIds = [], int $limit = 12): array
    {
        $override = HookSystem::applyFilter('video.recommended', null, $excludeId, $excludeIds, $limit);
        if ($override !== null) {
            return self::localizeResults($override);
        }

        $allExclude = array_merge([$excludeId], $excludeIds);
        $placeholders = implode(',', array_fill(0, count($allExclude), '?'));

         
        $range = Database::fetchOne(
            "SELECT MIN(id) as min_id, MAX(id) as max_id FROM videos WHERE status = ?",
            ['published']
        );

        if (!$range || !$range['min_id']) {
            return [];
        }

         
        $randId = random_int((int) $range['min_id'], (int) $range['max_id']);
        $params = array_merge($allExclude, ['published', $randId, $limit]);

        $results = Database::fetchAll(
            "SELECT v.*
             FROM videos v
             WHERE v.id NOT IN ({$placeholders}) AND v.status = ? AND v.id >= ?
             ORDER BY v.id LIMIT ?",
            $params
        );

         
        if (count($results) < $limit) {
            $gotIds = array_column($results, 'id');
            $allExclude2 = array_merge($allExclude, $gotIds);
            $ph2 = implode(',', array_fill(0, count($allExclude2), '?'));
            $remaining = $limit - count($results);
            $params2 = array_merge($allExclude2, ['published', $remaining]);
            $more = Database::fetchAll(
                "SELECT v.*
                 FROM videos v
                 WHERE v.id NOT IN ({$ph2}) AND v.status = ?
                 ORDER BY v.id LIMIT ?",
                $params2
            );
            $results = array_merge($results, $more);
        }

        return self::localizeResults($results);
    }

    public static function translations(int $videoId): array
    {
        $rows = Database::fetchAll(
            'SELECT lang_code, title, slug FROM video_translations WHERE video_id = ? ORDER BY lang_code',
            [$videoId]
        );
        $result = [];
        foreach ($rows as $row) {
            $result[$row['lang_code']] = $row;
        }
        return $result;
    }

    public static function trending(int $limit = 12): array
    {
        $override = HookSystem::applyFilter('video.trending', null, $limit);
        if ($override !== null) {
            return self::localizeResults($override);
        }

        return self::localizeResults(Database::fetchAll(
            "SELECT v.*
             FROM videos v
             WHERE v.status = ? AND v.published_at > DATE_SUB(NOW(), INTERVAL 7 DAY)
             ORDER BY v.views DESC LIMIT ?",
            ['published', $limit]
        ));
    }
}
