<?php
/**
 * Shared configuration for Main VPS scripts
 * This file should be included in all PHP scripts to maintain consistency
 */

// Base configuration
$BASE_PATH = __DIR__ . '/..'; // Adjust this to your actual base path

// Allowed paths for security (can be customized per VPS)
$ALLOWED_PATHS = [
    '/'
];

// Video formats supported
$SUPPORTED_VIDEO_FORMATS = [
    'mp4', 'mkv', 'avi', 'mov', 'wmv', 'flv', 'webm', 'm4v', '3gp'
];

// Subtitle formats supported  
$SUPPORTED_SUBTITLE_FORMATS = [
    'srt', 'vtt', 'ass', 'ssa', 'sub', 'idx'
];

// Maximum file size to process (in bytes) - 50GB
$MAX_FILE_SIZE = 50 * 1024 * 1024 * 1024;

// Pagination settings
$DEFAULT_PAGE_SIZE = 25; // Default number of videos per page
$MAX_PAGE_SIZE = 100; // Maximum allowed page size to prevent abuse

// FFmpeg/FFprobe paths (adjust if needed)
$FFPROBE_PATH = 'ffprobe';
$FFMPEG_PATH = 'ffmpeg';
$MKVMERGE_PATH = 'mkvmerge';
$MKVEXTRACT_PATH = 'mkvextract';

// Cache settings
$CACHE_ENABLED = true;
$CACHE_DURATION = 3600; // 1 hour in seconds

// Video cache settings (unified approach)
$VIDEO_CACHE_ENABLED = true;
$VIDEO_CACHE_DIR = $BASE_PATH . '/cache';

// Initialize cache directory
if ($VIDEO_CACHE_ENABLED && !is_dir($VIDEO_CACHE_DIR)) {
    if (!mkdir($VIDEO_CACHE_DIR, 0755, true)) {
        error_log("Warning: Could not create video cache directory: $VIDEO_CACHE_DIR");
        $VIDEO_CACHE_ENABLED = false;
    }
}

// Security settings
$ENABLE_DETAILED_ERRORS = false; // Set to true for debugging, false for production

/**
 * Common utility functions
 */

/**
 * Format bytes to human readable format
 */
function formatBytes($bytes, $precision = 2) {
    $units = array('B', 'KB', 'MB', 'GB', 'TB');
    
    for ($i = 0; $bytes >= 1024 && $i < count($units) - 1; $i++) {
        $bytes /= 1024;
    }
    
    return round($bytes, $precision) . ' ' . $units[$i];
}

/**
 * Validate path for security (blocklist approach)
 */
function validatePath($path) {
    // Basic checks
    if (empty($path) || !is_string($path)) {
        return false;
    }
    
    // Must start with /
    if ($path[0] !== '/') {
        return false;
    }
    
    // Prevent directory traversal
    if (strpos($path, '..') !== false || strpos($path, '\\') !== false) {
        return false;
    }
    
    // Path length check (reasonable limit)
    if (strlen($path) > 1000) {
        return false;
    }
    
    // Block only truly dangerous patterns
    // We use escapeshellarg() before any shell execution, so special chars are safe
    // Only block NULL bytes and control characters that could cause filesystem issues
    $dangerousPatterns = [
        '/\0/',                              // NULL byte - can truncate strings
        '/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]/', // Control characters (excluding \t, \n, \r)
    ];
    
    foreach ($dangerousPatterns as $pattern) {
        if (preg_match($pattern, $path)) {
            return false;
        }
    }
    
    return true;
}

/**
 * Check if file extension is supported video format
 */
function isVideoFile($filename) {
    global $SUPPORTED_VIDEO_FORMATS;
    $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
    return in_array($extension, $SUPPORTED_VIDEO_FORMATS);
}

/**
 * Check if file extension is supported subtitle format
 */
function isSubtitleFile($filename) {
    global $SUPPORTED_SUBTITLE_FORMATS;
    $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
    return in_array($extension, $SUPPORTED_SUBTITLE_FORMATS);
}

/**
 * Send JSON response
 */
function sendJsonResponse($data, $httpCode = 200) {
    http_response_code($httpCode);
    header('Content-Type: application/json');
    echo json_encode($data);
    exit;
}

/**
 * Send error response
 */
function sendError($message, $httpCode = 400) {
    global $ENABLE_DETAILED_ERRORS;
    
    $response = [
        'error' => $message,
        'success' => false
    ];
    
    if ($ENABLE_DETAILED_ERRORS) {
        $response['debug'] = [
            'timestamp' => date('Y-m-d H:i:s'),
            'script' => basename($_SERVER['PHP_SELF']),
            'request' => $_REQUEST
        ];
    }
    
    sendJsonResponse($response, $httpCode);
}

/**
 * Video Cache Management Functions
 * Simple approach: everything related to a video goes in its folder
 */

/**
 * Get video cache directory for a specific video file
 */
function getVideoCacheDir($videoPath) {
    global $VIDEO_CACHE_DIR;
    
    // Extract directory path and filename
    $dirPath = dirname($videoPath);
    $fileName = basename($videoPath);
    $fileNameWithoutExt = pathinfo($fileName, PATHINFO_FILENAME);
    
    // Create nested structure: cache + video directory path + video name folder
    $nestedPath = $VIDEO_CACHE_DIR . $dirPath . '/' . $fileNameWithoutExt;
    
    return $nestedPath;
}

/**
 * Check if video file has been modified since cache creation
 */
function isVideoCacheValid($videoPath, $cacheFile) {
    global $VIDEO_CACHE_ENABLED, $BASE_PATH;
    
    if (!$VIDEO_CACHE_ENABLED) {
        return false;
    }
    
    $fullVideoPath = $BASE_PATH . $videoPath;
    
    // Check if video file and cache file exist
    if (!file_exists($fullVideoPath) || !file_exists($cacheFile)) {
        return false;
    }
    
    // Check if video file has been modified since cache creation
    $videoModified = filemtime($fullVideoPath);
    $cacheCreated = filemtime($cacheFile);
    
    return $videoModified <= $cacheCreated;
}

/**
 * Get cached ffprobe data for a video file
 */
function getCachedFFProbeData($videoPath) {
    $cacheDir = getVideoCacheDir($videoPath);
    $ffprobeFile = $cacheDir . '/ffprobe.json';
    
    if (!isVideoCacheValid($videoPath, $ffprobeFile)) {
        return null;
    }
    
    $content = file_get_contents($ffprobeFile);
    if ($content === false) {
        return null;
    }
    
    return json_decode($content, true);
}

/**
 * Cache ffprobe data for a video file
 */
function cacheFFProbeData($videoPath, $ffprobeOutput) {
    $cacheDir = getVideoCacheDir($videoPath);
    
    // Create cache directory
    if (!is_dir($cacheDir)) {
        if (@!mkdir($cacheDir, 0755, true)) {
            error_log("Failed to create cache directory: $cacheDir");
            return false;
        }
    }
    
    $ffprobeFile = $cacheDir . '/ffprobe.json';
    return file_put_contents($ffprobeFile, $ffprobeOutput) !== false;
}

/**
 * Get cached subtitle tracks for a video file
 */
function getCachedSubtitleTracks($videoPath) {
    $cacheDir = getVideoCacheDir($videoPath);
    $metadataFile = $cacheDir . '/subtitle_tracks.json';
    
    if (!isVideoCacheValid($videoPath, $metadataFile)) {
        return null;
    }
    
    $content = file_get_contents($metadataFile);
    if ($content === false) {
        return null;
    }
    
    $data = json_decode($content, true);
    return $data ? $data['tracks'] : null;
}

/**
 * Cache subtitle tracks for a video file
 */
function cacheSubtitleTracks($videoPath, $tracks) {
    global $BASE_PATH;
    
    $fullVideoPath = $BASE_PATH . $videoPath;
    $cacheDir = getVideoCacheDir($videoPath);
    
    // Create cache directory
    if (!is_dir($cacheDir)) {
        if (!mkdir($cacheDir, 0755, true)) {
            error_log("Failed to create cache directory: $cacheDir");
            return false;
        }
    }
    
    // Prepare metadata
    $metadata = [
        'video_file' => $videoPath,
        'video_size' => filesize($fullVideoPath),
        'video_modified' => date('c', filemtime($fullVideoPath)),
        'extracted_at' => date('c'),
        'tracks' => []
    ];
    
    // Extract and cache each track
    foreach ($tracks as $trackIndex => $track) {
        $trackData = $track;
        $trackData['files'] = [];
        
        // Extract in original format first
        $originalContent = directExtractSubtitle($fullVideoPath, $track['mkvTrackId']);
        if ($originalContent) {
            // Determine original format based on codec (using actual mkvmerge codec names)
            $originalFormat = 'srt'; // Default to SRT, the most common format
            if (isset($track['codec'])) {
                switch (strtolower($track['codec'])) {
                    case 'subrip/srt':
                    case 'subrip':
                    case 'srt':
                        $originalFormat = 'srt';
                        break;
                    case 'webvtt':
                        $originalFormat = 'vtt';
                        break;
                    case 'substationalpha':
                    case 'ass':
                    case 'ssa':
                        $originalFormat = 'ass';
                        break;
                    default:
                        // Try to detect format from content
                        if (strpos($originalContent, '[Script Info]') !== false) {
                            $originalFormat = 'ass';
                        } elseif (strpos($originalContent, 'WEBVTT') !== false) {
                            $originalFormat = 'vtt';
                        } else {
                            $originalFormat = 'srt'; // Default fallback
                        }
                        break;
                }
            } else {
                // Try to detect format from content when codec is unknown
                if (strpos($originalContent, '[Script Info]') !== false) {
                    $originalFormat = 'ass';
                } elseif (strpos($originalContent, 'WEBVTT') !== false) {
                    $originalFormat = 'vtt';
                } else {
                    $originalFormat = 'srt'; // Default fallback
                }
            }
            
            // Save original format
            $originalFile = $cacheDir . "/track_{$trackIndex}.{$originalFormat}";
            if (file_put_contents($originalFile, $originalContent) !== false) {
                $trackData['files'][$originalFormat] = basename($originalFile);
                
                // Convert to VTT if not already VTT
                if ($originalFormat !== 'vtt') {
                    $vttContent = convertSubtitleToVTT($originalContent, $originalFormat);
                    if ($vttContent) {
                        $vttFile = $cacheDir . "/track_{$trackIndex}.vtt";
                        if (file_put_contents($vttFile, $vttContent) !== false) {
                            $trackData['files']['vtt'] = basename($vttFile);
                        }
                    }
                }
                
                // Convert to ASS if not already ASS
                if ($originalFormat !== 'ass') {
                    $assContent = convertSubtitleToASS($originalContent, $originalFormat);
                    if ($assContent) {
                        $assFile = $cacheDir . "/track_{$trackIndex}.ass";
                        if (file_put_contents($assFile, $assContent) !== false) {
                            $trackData['files']['ass'] = basename($assFile);
                        }
                    }
                }
            }
        }
        
        $metadata['tracks'][] = $trackData;
    }
    
    // Save subtitle tracks metadata
    $metadataFile = $cacheDir . '/subtitle_tracks.json';
    return file_put_contents($metadataFile, json_encode($metadata, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)) !== false;
}

/**
 * Get cached subtitle content for a specific track
 */
function getCachedSubtitleContent($videoPath, $trackIndex, $format = 'vtt') {
    $cacheDir = getVideoCacheDir($videoPath);
    $metadataFile = $cacheDir . '/subtitle_tracks.json';
    
    if (!isVideoCacheValid($videoPath, $metadataFile)) {
        return null;
    }
    
    $content = file_get_contents($metadataFile);
    if ($content === false) {
        return null;
    }
    
    $metadata = json_decode($content, true);
    if (!$metadata || !isset($metadata['tracks'][$trackIndex])) {
        return null;
    }
    
    $track = $metadata['tracks'][$trackIndex];
    
    // If requested format is cached, return it
    if (isset($track['files'][$format])) {
        $subtitleFile = $cacheDir . '/' . $track['files'][$format];
        if (file_exists($subtitleFile)) {
            $content = file_get_contents($subtitleFile);
            // Process cached content (normalize + sanitize) in case cache was created before processing
            return processSubtitleContent($content);
        }
    }
    
    // If ASS format is requested but not cached, try to convert from available format
    if ($format === 'ass' && !isset($track['files']['ass'])) {
        // Find an available format to convert from
        $sourceFormat = null;
        $sourceContent = null;
        
        // Prefer original format first
        foreach (['srt', 'vtt', 'ass'] as $fmt) {
            if (isset($track['files'][$fmt])) {
                $sourceFile = $cacheDir . '/' . $track['files'][$fmt];
                if (file_exists($sourceFile)) {
                    $sourceFormat = $fmt;
                    $sourceContent = file_get_contents($sourceFile);
                    break;
                }
            }
        }
        
        // Convert to ASS if we found source content
        if ($sourceContent && $sourceFormat !== 'ass') {
            $assContent = convertSubtitleToASS($sourceContent, $sourceFormat);
            if ($assContent) {
                // Cache the converted ASS content
                $assFile = $cacheDir . "/track_{$trackIndex}.ass";
                if (file_put_contents($assFile, $assContent) !== false) {
                    // Update metadata to include new ASS file
                    $metadata['tracks'][$trackIndex]['files']['ass'] = basename($assFile);
                    file_put_contents($metadataFile, json_encode($metadata, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
                }
                return $assContent;
            }
        }
    }
    
    return null;
}

/**
 * Direct subtitle extraction for caching purposes
 */
function directExtractSubtitle($videoPath, $actualTrackId) {
    if (!function_exists('shell_exec')) {
        return null;
    }
    
    $escapedPath = escapeshellarg($videoPath);
    
    // Use web-accessible temp directory
    $tempDir = __DIR__ . '/temp';
    if (!is_dir($tempDir)) {
        if (!mkdir($tempDir, 0755, true)) {
            $tempDir = __DIR__;
        }
    }
    
    // Extract subtitle with mkvextract
    $originalFile = $tempDir . '/' . uniqid('cache_sub_');
    $escapedOriginalFile = escapeshellarg($originalFile);
    
    $extractCommand = "mkvextract tracks $escapedPath $actualTrackId:$escapedOriginalFile 2>/dev/null";
    shell_exec($extractCommand);
    
    if (!file_exists($originalFile)) {
        return null;
    }
    
    $content = file_get_contents($originalFile);
    unlink($originalFile); // Clean up
    
    // Process subtitle content (normalize + sanitize)
    return processSubtitleContent($content);
}

/**
 * Sanitize subtitle content by replacing unwanted text
 */
function sanitizeSubtitleContent($content) {
    if (empty($content)) {
        return $content;
    }
    
    // Replace aiofilm and aiofilm.com with MixFlixTop (case-insensitive)
    $content = preg_replace('/aiofilm\.com/i', 'MixFlixTop', $content);
    $content = preg_replace('/aiofilm/i', 'MixFlixTop', $content);
    
    return $content;
}

/**
 * Process subtitle content: normalize Persian characters + sanitize branding
 * This should be the final step before returning subtitle content
 */
function processSubtitleContent($content) {
    if (empty($content)) {
        return $content;
    }
    
    // Load Persian normalizer if not already loaded
    if (!class_exists('PersianNormalizer')) {
        require_once __DIR__ . '/PersianNormalizer.php';
    }
    
    // Step 1: Normalize Arabic to Persian characters
    $content = PersianNormalizer::normalizeToPersian($content);
    
    // Step 2: Sanitize branding (replace aiofilm with MixFlixTop)
    $content = sanitizeSubtitleContent($content);
    
    return $content;
}

/**
 * Convert subtitle content to VTT format using ffmpeg
 */
function convertSubtitleToVTT($content, $sourceFormat) {
    if (!function_exists('shell_exec')) {
        return null;
    }
    
    $tempDir = __DIR__ . '/temp';
    if (!is_dir($tempDir)) {
        if (!mkdir($tempDir, 0755, true)) {
            $tempDir = __DIR__;
        }
    }
    
    $inputFile = $tempDir . '/' . uniqid('sub_input_') . '.' . $sourceFormat;
    $outputFile = $tempDir . '/' . uniqid('sub_output_') . '.vtt';
    
    // Write input content
    if (file_put_contents($inputFile, $content) === false) {
        return null;
    }
    
    $escapedInputFile = escapeshellarg($inputFile);
    $escapedOutputFile = escapeshellarg($outputFile);
    
    // Convert using ffmpeg
    $convertCommand = "ffmpeg -i $escapedInputFile -c:s webvtt $escapedOutputFile -y 2>/dev/null";
    shell_exec($convertCommand);
    
    $result = null;
    if (file_exists($outputFile)) {
        $result = file_get_contents($outputFile);
        unlink($outputFile);
        
        // Process the converted content (normalize + sanitize)
        $result = processSubtitleContent($result);
    }
    
    unlink($inputFile);
    return $result;
}

/**
 * Convert subtitle content to ASS format using ffmpeg with Vazir font
 */
function convertSubtitleToASS($content, $sourceFormat) {
    if (!function_exists('shell_exec')) {
        return null;
    }
    
    $tempDir = __DIR__ . '/temp';
    if (!is_dir($tempDir)) {
        if (!mkdir($tempDir, 0755, true)) {
            $tempDir = __DIR__;
        }
    }
    
    $inputFile = $tempDir . '/' . uniqid('sub_input_') . '.' . $sourceFormat;
    $outputFile = $tempDir . '/' . uniqid('sub_output_') . '.ass';
    
    // Write input content
    if (file_put_contents($inputFile, $content) === false) {
        return null;
    }
    
    $escapedInputFile = escapeshellarg($inputFile);
    $escapedOutputFile = escapeshellarg($outputFile);
    
    // Convert using ffmpeg to ASS format
    $convertCommand = "ffmpeg -i $escapedInputFile -c:s ass $escapedOutputFile -y 2>/dev/null";
    shell_exec($convertCommand);
    
    $result = null;
    if (file_exists($outputFile)) {
        $assContent = file_get_contents($outputFile);
        
        // Set Vazir font in ASS style definitions
        if (strpos($assContent, '[V4+ Styles]') !== false || strpos($assContent, '[V4 Styles]') !== false) {
            // Find and modify the Default style to use Vazir font
            $assContent = preg_replace(
                '/Style: Default,([^,]*),(\d+),/',
                'Style: Default,Vazirmatn RD ExtraBold,23,',
                $assContent
            );

            // If no Default style found, ensure any style uses Vazirmatn RD ExtraBold as font
            if (strpos($assContent, 'Vazirmatn RD ExtraBold') === false) {
                // Replace the font name in any Style line
                $assContent = preg_replace(
                    '/Style: ([^,]*),([^,]*),(\d+),/',
                    'Style: $1,Vazirmatn RD ExtraBold,23,',
                    $assContent
                );
            }
        }
        
        // Process the converted content (normalize + sanitize)
        $assContent = processSubtitleContent($assContent);
        
        $result = $assContent;
        unlink($outputFile);
    }
    
    unlink($inputFile);
    return $result;
}

/**
 * Enhanced FFprobe with caching
 */
function getVideoInfoWithCache($videoPath) {
    global $FFPROBE_PATH;
    
    if (!function_exists('shell_exec')) {
        return null;
    }
    
    // Check if we have cached ffprobe data
    $cachedData = getCachedFFProbeData($videoPath);
    if ($cachedData) {
        return $cachedData;
    }
    
    // Execute ffprobe
    $fullPath = $GLOBALS['BASE_PATH'] . $videoPath;
    $escapedPath = escapeshellarg($fullPath);
    $command = "$FFPROBE_PATH -v quiet -print_format json -show_format -show_streams $escapedPath 2>/dev/null";
    
    $output = shell_exec($command);
    if (!$output) {
        return null;
    }
    
    $data = json_decode($output, true);
    if ($data) {
        // Cache the result
        cacheFFProbeData($videoPath, $output);
    }
    
    return $data;
}

/**
 * Enhanced MKVMerge with caching
 */
function getSubtitleTracksWithCache($videoPath) {
    global $MKVMERGE_PATH;
    
    if (!function_exists('shell_exec')) {
        return [];
    }
    
    // Check if we have cached subtitle tracks
    $cachedTracks = getCachedSubtitleTracks($videoPath);
    if ($cachedTracks) {
        return $cachedTracks;
    }
    
    // Execute mkvmerge
    $fullPath = $GLOBALS['BASE_PATH'] . $videoPath;
    $escapedPath = escapeshellarg($fullPath);
    $command = "export LANG=en_US.UTF-8; export LC_ALL=en_US.UTF-8; $MKVMERGE_PATH --output-charset 'UTF-8' -J $escapedPath";
    
    $output = shell_exec($command);
    if (!$output) {
        return [];
    }
    
    $data = json_decode($output, true);
    if (!$data || !isset($data['tracks'])) {
        return [];
    }
    
    $tracks = [];
    $subtitleTrackIndex = 0;
    
    foreach ($data['tracks'] as $track) {
        // Only process subtitle tracks
        if ($track['type'] !== 'subtitles') {
            continue;
        }
        
        $trackId = $track['id'];
        $codec = $track['codec'] ?? 'unknown';
        
        // Get language from properties
        $language = 'unknown';
        if (isset($track['properties']['language'])) {
            $language = $track['properties']['language'];
        }
        
        // Get track name/title
        $title = "Subtitle Track $subtitleTrackIndex";
        if (isset($track['properties']['track_name'])) {
            $title = $track['properties']['track_name'];
        } elseif (isset($track['properties']['language']) && $track['properties']['language'] !== 'und') {
            $title = "Subtitle Track $subtitleTrackIndex (" . strtoupper($track['properties']['language']) . ")";
        }
        
        // Process title (normalize + sanitize)
        $title = processSubtitleContent($title);
        
        // Get additional properties
        $isDefault = isset($track['properties']['default_track']) ? (bool)$track['properties']['default_track'] : false;
        $isForced = isset($track['properties']['forced_track']) ? (bool)$track['properties']['forced_track'] : false;
        
        $tracks[] = [
            'index' => $subtitleTrackIndex,  // Frontend-friendly sequential index
            'mkvTrackId' => $trackId,        // Actual track ID for mkvextract
            'codec' => $codec,
            'language' => $language,
            'title' => $title,
            'isDefault' => $isDefault,
            'isForced' => $isForced
        ];
        
        $subtitleTrackIndex++;
    }
    
    // Cache the tracks for future use
    if (!empty($tracks)) {
        cacheSubtitleTracks($videoPath, $tracks);
    }
    
    return $tracks;
}

/**
 * Helper function to get video info from cache for use in other scripts
 */
function getVideoInfoFromCache($fullPath, $videoPath) {
    $data = getVideoInfoWithCache($videoPath);
    if (!$data) {
        return null;
    }
    
    $info = [];
    
    // Get duration
    if (isset($data['format']['duration'])) {
        $info['duration'] = floatval($data['format']['duration']);
    }
    
    // Get video stream info
    foreach ($data['streams'] as $stream) {
        if ($stream['codec_type'] === 'video') {
            if (isset($stream['width']) && isset($stream['height'])) {
                $info['resolution'] = $stream['width'] . 'x' . $stream['height'];
                $info['width'] = intval($stream['width']);
                $info['height'] = intval($stream['height']);
            }
            if (isset($stream['codec_name'])) {
                $info['video_codec'] = $stream['codec_name'];
            }
            if (isset($stream['bit_rate'])) {
                $info['video_bitrate'] = intval($stream['bit_rate']);
            }
            break;
        }
    }
    
    return $info;
}

?>