<?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');

/**
 * RESTful API System for Mobile Apps and Third-Party Integration
 */
class VAPI
{
    private $db;
    private $logger;
    private $rbac;
    private $version = 'v1';
    private $rateLimiter;
    
    // API response codes
    const SUCCESS = 200;
    const CREATED = 201;
    const BAD_REQUEST = 400;
    const UNAUTHORIZED = 401;
    const FORBIDDEN = 403;
    const NOT_FOUND = 404;
    const METHOD_NOT_ALLOWED = 405;
    const RATE_LIMITED = 429;
    const SERVER_ERROR = 500;
    
    public function __construct()
    {
        $this->db = VDatabase::getInstance();
        $this->logger = VLogger::getInstance();
        $this->rbac = VRBAC::getInstance();
        $this->rateLimiter = VSecurity::getInstance();
    }
    
    /**
     * Handle API request
     * @param string $method HTTP method
     * @param string $endpoint API endpoint
     * @param array $data Request data
     * @param array $headers Request headers
     * @return array API response
     */
    public function handleRequest($method, $endpoint, $data = [], $headers = [])
    {
        try {
            // Set CORS headers
            $this->setCORSHeaders();
            
            // Handle preflight requests
            if ($method === 'OPTIONS') {
                return $this->response(['message' => 'OK'], self::SUCCESS);
            }
            
            // Rate limiting
            $clientId = $this->getClientIdentifier($headers);
            if (!$this->checkRateLimit($clientId, $endpoint)) {
                return $this->response(['error' => 'Rate limit exceeded'], self::RATE_LIMITED);
            }
            
            // Authentication
            $user = $this->authenticateRequest($headers);
            
            // Route request
            $response = $this->routeRequest($method, $endpoint, $data, $user);
            
            // Log API request
            $this->logAPIRequest($method, $endpoint, $user['id'] ?? null, $response['status']);
            
            return $response;
            
        } catch (Exception $e) {
            $this->logger->error('API request failed', [
                'method' => $method,
                'endpoint' => $endpoint,
                'error' => $e->getMessage()
            ]);
            
            return $this->response(['error' => 'Internal server error'], self::SERVER_ERROR);
        }
    }
    
    /**
     * Route API request to appropriate handler
     * @param string $method HTTP method
     * @param string $endpoint API endpoint
     * @param array $data Request data
     * @param array|null $user Authenticated user
     * @return array API response
     */
    private function routeRequest($method, $endpoint, $data, $user)
    {
        $parts = explode('/', trim($endpoint, '/'));
        $resource = $parts[0] ?? '';
        $id = $parts[1] ?? null;
        $action = $parts[2] ?? null;
        
        switch ($resource) {
            case 'auth':
                return $this->handleAuth($method, $id, $data);
                
            case 'videos':
                return $this->handleVideos($method, $id, $action, $data, $user);
                
            case 'users':
                return $this->handleUsers($method, $id, $action, $data, $user);
                
            case 'live':
                return $this->handleLiveStreams($method, $id, $action, $data, $user);
                
            case 'search':
                return $this->handleSearch($method, $data, $user);
                
            case 'upload':
                return $this->handleUpload($method, $data, $user);
                
            case 'analytics':
                return $this->handleAnalytics($method, $id, $action, $data, $user);
                
            default:
                return $this->response(['error' => 'Endpoint not found'], self::NOT_FOUND);
        }
    }
    
    /**
     * Handle authentication endpoints
     * @param string $method HTTP method
     * @param string $action Action
     * @param array $data Request data
     * @return array API response
     */
    private function handleAuth($method, $action, $data)
    {
        switch ($action) {
            case 'login':
                if ($method !== 'POST') {
                    return $this->response(['error' => 'Method not allowed'], self::METHOD_NOT_ALLOWED);
                }
                return $this->login($data);
                
            case 'register':
                if ($method !== 'POST') {
                    return $this->response(['error' => 'Method not allowed'], self::METHOD_NOT_ALLOWED);
                }
                return $this->register($data);
                
            case 'refresh':
                if ($method !== 'POST') {
                    return $this->response(['error' => 'Method not allowed'], self::METHOD_NOT_ALLOWED);
                }
                return $this->refreshToken($data);
                
            case 'logout':
                if ($method !== 'POST') {
                    return $this->response(['error' => 'Method not allowed'], self::METHOD_NOT_ALLOWED);
                }
                return $this->logout($data);
                
            default:
                return $this->response(['error' => 'Auth action not found'], self::NOT_FOUND);
        }
    }
    
    /**
     * Handle video endpoints
     * @param string $method HTTP method
     * @param string $id Video ID
     * @param string $action Action
     * @param array $data Request data
     * @param array|null $user Authenticated user
     * @return array API response
     */
    private function handleVideos($method, $id, $action, $data, $user)
    {
        switch ($method) {
            case 'GET':
                if ($id) {
                    if ($action === 'comments') {
                        return $this->getVideoComments($id, $data);
                    } elseif ($action === 'related') {
                        return $this->getRelatedVideos($id, $data);
                    } else {
                        return $this->getVideo($id, $user);
                    }
                } else {
                    return $this->getVideos($data, $user);
                }
                
            case 'POST':
                if ($id && $action === 'like') {
                    return $this->likeVideo($id, $user);
                } elseif ($id && $action === 'comment') {
                    return $this->commentVideo($id, $data, $user);
                } else {
                    return $this->createVideo($data, $user);
                }
                
            case 'PUT':
                if ($id) {
                    return $this->updateVideo($id, $data, $user);
                }
                break;
                
            case 'DELETE':
                if ($id) {
                    return $this->deleteVideo($id, $user);
                }
                break;
        }
        
        return $this->response(['error' => 'Method not allowed'], self::METHOD_NOT_ALLOWED);
    }
    
    /**
     * Handle user endpoints
     * @param string $method HTTP method
     * @param string $id User ID
     * @param string $action Action
     * @param array $data Request data
     * @param array|null $user Authenticated user
     * @return array API response
     */
    private function handleUsers($method, $id, $action, $data, $user)
    {
        switch ($method) {
            case 'GET':
                if ($id) {
                    if ($action === 'videos') {
                        return $this->getUserVideos($id, $data);
                    } elseif ($action === 'followers') {
                        return $this->getUserFollowers($id, $data);
                    } elseif ($action === 'following') {
                        return $this->getUserFollowing($id, $data);
                    } else {
                        return $this->getUser($id, $user);
                    }
                } else {
                    return $this->getUsers($data, $user);
                }
                
            case 'POST':
                if ($id && $action === 'follow') {
                    return $this->followUser($id, $user);
                }
                break;
                
            case 'PUT':
                if ($id === 'me' || ($user && $id == $user['id'])) {
                    return $this->updateProfile($data, $user);
                }
                break;
                
            case 'DELETE':
                if ($id && $action === 'follow') {
                    return $this->unfollowUser($id, $user);
                }
                break;
        }
        
        return $this->response(['error' => 'Method not allowed'], self::METHOD_NOT_ALLOWED);
    }
    
    /**
     * Handle live stream endpoints
     * @param string $method HTTP method
     * @param string $id Stream ID
     * @param string $action Action
     * @param array $data Request data
     * @param array|null $user Authenticated user
     * @return array API response
     */
    private function handleLiveStreams($method, $id, $action, $data, $user)
    {
        if (!$user) {
            return $this->response(['error' => 'Authentication required'], self::UNAUTHORIZED);
        }
        
        switch ($method) {
            case 'GET':
                if ($id) {
                    return $this->getLiveStream($id, $user);
                } else {
                    return $this->getLiveStreams($data, $user);
                }
                
            case 'POST':
                if ($id && $action === 'start') {
                    return $this->startLiveStream($id, $user);
                } elseif ($id && $action === 'stop') {
                    return $this->stopLiveStream($id, $user);
                } else {
                    return $this->createLiveStream($data, $user);
                }
                
            case 'PUT':
                if ($id) {
                    return $this->updateLiveStream($id, $data, $user);
                }
                break;
                
            case 'DELETE':
                if ($id) {
                    return $this->deleteLiveStream($id, $user);
                }
                break;
        }
        
        return $this->response(['error' => 'Method not allowed'], self::METHOD_NOT_ALLOWED);
    }
    
    /**
     * Authenticate API request
     * @param array $headers Request headers
     * @return array|null User data or null if not authenticated
     */
    private function authenticateRequest($headers)
    {
        $authHeader = $headers['Authorization'] ?? $headers['authorization'] ?? '';
        
        if (empty($authHeader)) {
            return null;
        }
        
        // Support Bearer token format
        if (strpos($authHeader, 'Bearer ') === 0) {
            $token = substr($authHeader, 7);
            return $this->validateJWTToken($token);
        }
        
        // Support API key format
        if (strpos($authHeader, 'ApiKey ') === 0) {
            $apiKey = substr($authHeader, 7);
            return $this->validateAPIKey($apiKey);
        }
        
        return null;
    }
    
    /**
     * Validate JWT token
     * @param string $token JWT token
     * @return array|null User data or null if invalid
     */
    private function validateJWTToken($token)
    {
        try {
            // Simple JWT validation (in production, use a proper JWT library)
            $parts = explode('.', $token);
            if (count($parts) !== 3) {
                return null;
            }
            
            $payload = json_decode(base64_decode($parts[1]), true);
            
            if (!$payload || !isset($payload['user_id']) || $payload['exp'] < time()) {
                return null;
            }
            
            // Get user from database
            $query = "SELECT usr_id, usr_user, usr_email, usr_dname FROM db_accountuser WHERE usr_id = ? AND usr_status = 'active'";
            $result = $this->db->doQuery($query, [$payload['user_id']]);
            $user = $this->db->doFetch($result);
            
            if ($user) {
                return [
                    'id' => $user['usr_id'],
                    'username' => $user['usr_user'],
                    'email' => $user['usr_email'],
                    'display_name' => $user['usr_dname']
                ];
            }
            
            return null;
            
        } catch (Exception $e) {
            $this->logger->error('JWT validation failed', ['error' => $e->getMessage()]);
            return null;
        }
    }
    
    /**
     * Validate API key
     * @param string $apiKey API key
     * @return array|null User data or null if invalid
     */
    private function validateAPIKey($apiKey)
    {
        try {
            $query = "SELECT ak.user_id, ak.permissions, au.usr_user, au.usr_email, au.usr_dname 
                      FROM db_api_keys ak 
                      JOIN db_accountuser au ON ak.user_id = au.usr_id 
                      WHERE ak.api_key = ? AND ak.status = 'active' AND ak.expires_at > NOW()";
            $result = $this->db->doQuery($query, [$apiKey]);
            $keyData = $this->db->doFetch($result);
            
            if ($keyData) {
                return [
                    'id' => $keyData['user_id'],
                    'username' => $keyData['usr_user'],
                    'email' => $keyData['usr_email'],
                    'display_name' => $keyData['usr_dname'],
                    'api_permissions' => json_decode($keyData['permissions'], true) ?: []
                ];
            }
            
            return null;
            
        } catch (Exception $e) {
            $this->logger->error('API key validation failed', ['error' => $e->getMessage()]);
            return null;
        }
    }
    
    /**
     * Generate JWT token
     * @param array $user User data
     * @return string JWT token
     */
    private function generateJWTToken($user)
    {
        $header = json_encode(['typ' => 'JWT', 'alg' => 'HS256']);
        $payload = json_encode([
            'user_id' => $user['id'],
            'username' => $user['username'],
            'iat' => time(),
            'exp' => time() + (24 * 60 * 60) // 24 hours
        ]);
        
        $headerEncoded = base64_encode($header);
        $payloadEncoded = base64_encode($payload);
        
        $signature = hash_hmac('sha256', $headerEncoded . '.' . $payloadEncoded, 'your-secret-key', true);
        $signatureEncoded = base64_encode($signature);
        
        return $headerEncoded . '.' . $payloadEncoded . '.' . $signatureEncoded;
    }
    
    /**
     * Login user
     * @param array $data Login data
     * @return array API response
     */
    private function login($data)
    {
        $username = $data['username'] ?? '';
        $password = $data['password'] ?? '';
        
        if (empty($username) || empty($password)) {
            return $this->response(['error' => 'Username and password required'], self::BAD_REQUEST);
        }
        
        // Validate credentials
        $query = "SELECT usr_id, usr_user, usr_email, usr_dname, usr_password 
                  FROM db_accountuser 
                  WHERE (usr_user = ? OR usr_email = ?) AND usr_status = 'active'";
        $result = $this->db->doQuery($query, [$username, $username]);
        $user = $this->db->doFetch($result);
        
        if (!$user || !password_verify($password, $user['usr_password'])) {
            return $this->response(['error' => 'Invalid credentials'], self::UNAUTHORIZED);
        }
        
        // Generate token
        $userData = [
            'id' => $user['usr_id'],
            'username' => $user['usr_user'],
            'email' => $user['usr_email'],
            'display_name' => $user['usr_dname']
        ];
        
        $token = $this->generateJWTToken($userData);
        
        // Update last login
        $this->db->doUpdate('db_accountuser', 'usr_id', [
            'usr_lastlogin' => date('Y-m-d H:i:s')
        ], $user['usr_id']);
        
        return $this->response([
            'token' => $token,
            'user' => $userData,
            'expires_in' => 86400 // 24 hours
        ], self::SUCCESS);
    }
    
    /**
     * Get videos
     * @param array $params Query parameters
     * @param array|null $user Authenticated user
     * @return array API response
     */
    private function getVideos($params, $user)
    {
        $page = max(1, (int)($params['page'] ?? 1));
        $limit = min(50, max(1, (int)($params['limit'] ?? 20)));
        $offset = ($page - 1) * $limit;
        
        $category = $params['category'] ?? '';
        $sort = $params['sort'] ?? 'recent';
        
        $where = ["vf.file_type = 'video'", "vf.privacy = 'public'"];
        $queryParams = [];
        
        if ($category) {
            $where[] = "vf.file_category = ?";
            $queryParams[] = $category;
        }
        
        $orderBy = match($sort) {
            'popular' => 'vf.file_views DESC',
            'recent' => 'vf.upload_date DESC',
            'rating' => 'vf.file_rating DESC',
            default => 'vf.upload_date DESC'
        };
        
        $whereClause = implode(' AND ', $where);
        
        $query = "SELECT vf.file_key, vf.file_title, vf.file_description, vf.file_views, 
                         vf.file_rating, vf.upload_date, vf.file_duration, vf.file_size,
                         au.usr_user, au.usr_dname
                  FROM db_videofiles vf
                  JOIN db_accountuser au ON vf.usr_id = au.usr_id
                  WHERE {$whereClause}
                  ORDER BY {$orderBy}
                  LIMIT {$limit} OFFSET {$offset}";
        
        $result = $this->db->doQuery($query, $queryParams);
        
        $videos = [];
        while ($row = $this->db->doFetch($result)) {
            $videos[] = [
                'id' => $row['file_key'],
                'title' => $row['file_title'],
                'description' => $row['file_description'],
                'views' => (int)$row['file_views'],
                'rating' => (float)$row['file_rating'],
                'duration' => (int)$row['file_duration'],
                'size' => (int)$row['file_size'],
                'uploaded_at' => $row['upload_date'],
                'uploader' => [
                    'username' => $row['usr_user'],
                    'display_name' => $row['usr_dname']
                ],
                'thumbnail_url' => "/thumbnails/{$row['file_key']}_medium.jpg",
                'video_url' => "/watch/{$row['file_key']}"
            ];
        }
        
        return $this->response([
            'videos' => $videos,
            'pagination' => [
                'page' => $page,
                'limit' => $limit,
                'total' => $this->getVideoCount($where, $queryParams)
            ]
        ], self::SUCCESS);
    }
    
    /**
     * Create API response
     * @param array $data Response data
     * @param int $status HTTP status code
     * @return array API response
     */
    private function response($data, $status = self::SUCCESS)
    {
        return [
            'status' => $status,
            'data' => $data,
            'timestamp' => time(),
            'version' => $this->version
        ];
    }
    
    /**
     * Set CORS headers
     */
    private function setCORSHeaders()
    {
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
        header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With');
        header('Access-Control-Max-Age: 86400');
    }
    
    /**
     * Check rate limit
     * @param string $clientId Client identifier
     * @param string $endpoint Endpoint
     * @return bool True if within limits
     */
    private function checkRateLimit($clientId, $endpoint)
    {
        // Different limits for different endpoints
        $limits = [
            'auth' => ['requests' => 10, 'window' => 300], // 10 requests per 5 minutes
            'upload' => ['requests' => 5, 'window' => 3600], // 5 uploads per hour
            'default' => ['requests' => 100, 'window' => 3600] // 100 requests per hour
        ];
        
        $endpointType = explode('/', $endpoint)[0] ?? 'default';
        $limit = $limits[$endpointType] ?? $limits['default'];
        
        return $this->rateLimiter->checkRateLimit(
            "api_{$clientId}_{$endpointType}",
            $limit['requests'],
            $limit['window'],
            "api_{$endpointType}"
        );
    }
    
    /**
     * Get client identifier
     * @param array $headers Request headers
     * @return string Client identifier
     */
    private function getClientIdentifier($headers)
    {
        // Use API key if available, otherwise IP address
        $authHeader = $headers['Authorization'] ?? $headers['authorization'] ?? '';
        
        if (strpos($authHeader, 'ApiKey ') === 0) {
            return 'key_' . substr($authHeader, 7, 10);
        }
        
        return 'ip_' . ($_SERVER['REMOTE_ADDR'] ?? 'unknown');
    }
    
    /**
     * Log API request
     * @param string $method HTTP method
     * @param string $endpoint Endpoint
     * @param int|null $userId User ID
     * @param int $status Response status
     */
    private function logAPIRequest($method, $endpoint, $userId, $status)
    {
        try {
            $logData = [
                'method' => $method,
                'endpoint' => $endpoint,
                'user_id' => $userId,
                'status' => $status,
                'ip_address' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
                'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
                'created_at' => date('Y-m-d H:i:s')
            ];
            
            $this->db->doInsert('db_api_logs', $logData);
            
        } catch (Exception $e) {
            $this->logger->error('Failed to log API request', [
                'error' => $e->getMessage()
            ]);
        }
    }
    
    /**
     * Get video count for pagination
     * @param array $where Where conditions
     * @param array $params Query parameters
     * @return int Total count
     */
    private function getVideoCount($where, $params)
    {
        $whereClause = implode(' AND ', $where);
        $query = "SELECT COUNT(*) as total FROM db_videofiles vf WHERE {$whereClause}";
        $result = $this->db->doQuery($query, $params);
        $row = $this->db->doFetch($result);
        
        return (int)($row['total'] ?? 0);
    }
}