<?php
/*******************************************************************************************************************
| Software Name        : EasyStream
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
| Software Author      : (c) Sami Ahmed
|*******************************************************************************************************************
|
|*******************************************************************************************************************
| This source file is subject to the EasyStream Proprietary License Agreement.
|
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|*******************************************************************************************************************
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|*******************************************************************************************************************/

defined('_ISVALID') or header('Location: /error');

/**
 * Recommendation Engine Class
 *
 * Provides personalized content recommendations based on:
 * - User watch history
 * - Liked videos
 * - Subscribed channels
 * - Popular/trending content
 * - Similar content by category/tags
 */
class VRecommendations
{
    /**
     * Get personalized "For You" feed for logged-in users
     *
     * @param int $limit Number of recommendations to return
     * @param string $type Content type (video, short, live, image, audio, blog, document)
     * @return array Recommended content items
     */
    public static function getForYouFeed($limit = 20, $type = 'video')
    {
        global $class_database;

        $usr_id = isset($_SESSION['USER_ID']) ? (int) $_SESSION['USER_ID'] : 0;

        // For guests, return trending content
        if ($usr_id == 0) {
            return self::getTrending($limit, $type);
        }

        $recommendations = [];

        // 40% from subscribed channels (latest uploads)
        $subscribed = self::getFromSubscriptions($usr_id, ceil($limit * 0.4), $type);
        $recommendations = array_merge($recommendations, $subscribed);

        // 30% based on watch history (similar content)
        $similar = self::getBasedOnWatchHistory($usr_id, ceil($limit * 0.3), $type);
        $recommendations = array_merge($recommendations, $similar);

        // 20% based on likes (similar to liked content)
        $liked_similar = self::getBasedOnLikes($usr_id, ceil($limit * 0.2), $type);
        $recommendations = array_merge($recommendations, $liked_similar);

        // 10% trending content (discovery)
        $trending = self::getTrending(ceil($limit * 0.1), $type);
        $recommendations = array_merge($recommendations, $trending);

        // Remove duplicates and shuffle
        $recommendations = self::deduplicateAndShuffle($recommendations, $limit);

        return $recommendations;
    }

    /**
     * Get trending content (popular in last 7 days)
     *
     * @param int $limit Number of items
     * @param string $type Content type
     * @return array Trending items
     */
    public static function getTrending($limit = 20, $type = 'video')
    {
        global $class_database;

        $table_map = [
            'video' => 'db_videofiles',
            'short' => 'db_shortfiles',
            'live' => 'db_livefiles',
            'image' => 'db_imagefiles',
            'audio' => 'db_audiofiles',
            'document' => 'db_documentfiles',
            'blog' => 'db_blogfiles'
        ];

        if (!isset($table_map[$type])) {
            return [];
        }

        $table = $table_map[$type];

        // Calculate trending score: (views * 2 + likes * 5 + comments * 3) / age_in_days
        $sql = "SELECT
                    f.*,
                    u.usr_user, u.usr_dname, u.usr_key,
                    ((f.file_views * 2) + (f.file_like * 5) + (f.file_comments * 3)) /
                    GREATEST(DATEDIFF(NOW(), f.upload_date), 1) as trending_score
                FROM `{$table}` f
                JOIN `db_accountuser` u ON f.usr_id = u.usr_id
                WHERE f.active = 1
                    AND f.approved = 1
                    AND f.deleted = 0
                    AND f.privacy = 'public'
                    AND f.upload_date >= DATE_SUB(NOW(), INTERVAL 30 DAY)
                ORDER BY trending_score DESC
                LIMIT %d";

        $result = $class_database->doQuery($sql, $limit);

        $items = [];
        while ($row = $result->fetch_assoc()) {
            $items[] = $row;
        }

        return $items;
    }

    /**
     * Get latest content from subscribed channels
     *
     * @param int $usr_id User ID
     * @param int $limit Number of items
     * @param string $type Content type
     * @return array Items from subscriptions
     */
    public static function getFromSubscriptions($usr_id, $limit = 20, $type = 'video')
    {
        global $class_database;

        $table_map = [
            'video' => 'db_videofiles',
            'short' => 'db_shortfiles',
            'live' => 'db_livefiles',
            'image' => 'db_imagefiles',
            'audio' => 'db_audiofiles',
            'document' => 'db_documentfiles',
            'blog' => 'db_blogfiles'
        ];

        if (!isset($table_map[$type])) {
            return [];
        }

        $table = $table_map[$type];

        $sql = "SELECT DISTINCT
                    f.*,
                    u.usr_user, u.usr_dname, u.usr_key
                FROM `{$table}` f
                JOIN `db_accountuser` u ON f.usr_id = u.usr_id
                JOIN `db_subscribers` s ON s.subscriber_id = f.usr_id
                WHERE s.usr_id = %d
                    AND f.active = 1
                    AND f.approved = 1
                    AND f.deleted = 0
                    AND f.privacy = 'public'
                ORDER BY f.upload_date DESC
                LIMIT %d";

        $result = $class_database->doQuery($sql, $usr_id, $limit);

        $items = [];
        while ($row = $result->fetch_assoc()) {
            $items[] = $row;
        }

        return $items;
    }

    /**
     * Get content based on watch history (similar categories/tags)
     *
     * @param int $usr_id User ID
     * @param int $limit Number of items
     * @param string $type Content type
     * @return array Similar items
     */
    public static function getBasedOnWatchHistory($usr_id, $limit = 20, $type = 'video')
    {
        global $class_database;

        $table_map = [
            'video' => 'db_videofiles',
            'short' => 'db_shortfiles',
            'live' => 'db_livefiles',
            'image' => 'db_imagefiles',
            'audio' => 'db_audiofiles',
            'document' => 'db_documentfiles',
            'blog' => 'db_blogfiles'
        ];

        if (!isset($table_map[$type])) {
            return [];
        }

        $table = $table_map[$type];

        // Get categories from watch history
        $sql = "SELECT DISTINCT fcr.ct_id
                FROM `db_filehistory` fh
                JOIN `db_filecategories` fcr ON fcr.file_key = fh.file_key
                WHERE fh.usr_id = %d
                    AND fh.file_type = '%s'
                ORDER BY fh.db_id DESC
                LIMIT 10";

        $result = $class_database->doQuery($sql, $usr_id, $type);

        $category_ids = [];
        while ($row = $result->fetch_assoc()) {
            $category_ids[] = $row['ct_id'];
        }

        if (empty($category_ids)) {
            return self::getTrending($limit, $type);
        }

        $category_list = implode(',', $category_ids);

        // Get content from these categories, excluding already watched
        $sql = "SELECT DISTINCT
                    f.*,
                    u.usr_user, u.usr_dname, u.usr_key
                FROM `{$table}` f
                JOIN `db_accountuser` u ON f.usr_id = u.usr_id
                JOIN `db_filecategories` fc ON fc.file_key = f.file_key
                WHERE fc.ct_id IN ({$category_list})
                    AND f.active = 1
                    AND f.approved = 1
                    AND f.deleted = 0
                    AND f.privacy = 'public'
                    AND f.file_key NOT IN (
                        SELECT file_key FROM `db_filehistory` WHERE usr_id = %d
                    )
                ORDER BY f.file_views DESC, f.upload_date DESC
                LIMIT %d";

        $result = $class_database->doQuery($sql, $usr_id, $limit);

        $items = [];
        while ($row = $result->fetch_assoc()) {
            $items[] = $row;
        }

        return $items;
    }

    /**
     * Get content based on liked videos
     *
     * @param int $usr_id User ID
     * @param int $limit Number of items
     * @param string $type Content type
     * @return array Similar items
     */
    public static function getBasedOnLikes($usr_id, $limit = 20, $type = 'video')
    {
        global $class_database;

        $table_map = [
            'video' => 'db_videofiles',
            'short' => 'db_shortfiles',
            'live' => 'db_livefiles',
            'image' => 'db_imagefiles',
            'audio' => 'db_audiofiles',
            'document' => 'db_documentfiles',
            'blog' => 'db_blogfiles'
        ];

        if (!isset($table_map[$type])) {
            return [];
        }

        $table = $table_map[$type];

        // Get categories from liked content
        $sql = "SELECT DISTINCT fcr.ct_id
                FROM `db_filelike` fl
                JOIN `db_filecategories` fcr ON fcr.file_key = fl.file_key
                WHERE fl.usr_id = %d
                    AND fl.file_type = '%s'
                    AND fl.vote = 1
                ORDER BY fl.db_id DESC
                LIMIT 10";

        $result = $class_database->doQuery($sql, $usr_id, $type);

        $category_ids = [];
        while ($row = $result->fetch_assoc()) {
            $category_ids[] = $row['ct_id'];
        }

        if (empty($category_ids)) {
            return [];
        }

        $category_list = implode(',', $category_ids);

        // Get content from these categories, excluding already liked
        $sql = "SELECT DISTINCT
                    f.*,
                    u.usr_user, u.usr_dname, u.usr_key
                FROM `{$table}` f
                JOIN `db_accountuser` u ON f.usr_id = u.usr_id
                JOIN `db_filecategories` fc ON fc.file_key = f.file_key
                WHERE fc.ct_id IN ({$category_list})
                    AND f.active = 1
                    AND f.approved = 1
                    AND f.deleted = 0
                    AND f.privacy = 'public'
                    AND f.file_key NOT IN (
                        SELECT file_key FROM `db_filelike` WHERE usr_id = %d AND vote = 1
                    )
                ORDER BY f.file_like DESC, f.upload_date DESC
                LIMIT %d";

        $result = $class_database->doQuery($sql, $usr_id, $limit);

        $items = [];
        while ($row = $result->fetch_assoc()) {
            $items[] = $row;
        }

        return $items;
    }

    /**
     * Get "Continue Watching" section
     *
     * @param int $usr_id User ID
     * @param int $limit Number of items
     * @return array Partially watched items
     */
    public static function getContinueWatching($usr_id, $limit = 10)
    {
        global $class_database;

        // Get recently watched items that aren't fully watched
        $sql = "SELECT DISTINCT
                    fh.*,
                    CASE
                        WHEN fh.file_type = 'video' THEN v.file_title
                        WHEN fh.file_type = 'short' THEN s.file_title
                        WHEN fh.file_type = 'live' THEN l.file_title
                        WHEN fh.file_type = 'audio' THEN a.file_title
                    END as file_title,
                    u.usr_user, u.usr_dname, u.usr_key
                FROM `db_filehistory` fh
                LEFT JOIN `db_videofiles` v ON fh.file_key = v.file_key AND fh.file_type = 'video'
                LEFT JOIN `db_shortfiles` s ON fh.file_key = s.file_key AND fh.file_type = 'short'
                LEFT JOIN `db_livefiles` l ON fh.file_key = l.file_key AND fh.file_type = 'live'
                LEFT JOIN `db_audiofiles` a ON fh.file_key = a.file_key AND fh.file_type = 'audio'
                JOIN `db_accountuser` u ON
                    COALESCE(v.usr_id, s.usr_id, l.usr_id, a.usr_id) = u.usr_id
                WHERE fh.usr_id = %d
                    AND fh.watch_progress > 0
                    AND fh.watch_progress < 90
                ORDER BY fh.db_id DESC
                LIMIT %d";

        $result = $class_database->doQuery($sql, $usr_id, $limit);

        $items = [];
        while ($row = $result->fetch_assoc()) {
            $items[] = $row;
        }

        return $items;
    }

    /**
     * Remove duplicates and shuffle results
     *
     * @param array $items Array of items
     * @param int $limit Maximum items to return
     * @return array Deduplicated and shuffled items
     */
    private static function deduplicateAndShuffle($items, $limit)
    {
        // Remove duplicates based on file_key
        $seen = [];
        $unique = [];

        foreach ($items as $item) {
            if (!isset($seen[$item['file_key']])) {
                $seen[$item['file_key']] = true;
                $unique[] = $item;
            }
        }

        // Shuffle for variety
        shuffle($unique);

        // Return limited number
        return array_slice($unique, 0, $limit);
    }
}
