<?php
ini_set('display_errors', 0);
error_reporting(0);
setlocale(LC_CTYPE, "pl_PL.UTF-8", "en_US.UTF-8", "C.UTF-8");

require_once 'functions.php';
checkAuth();

set_time_limit(15);

$folderKey = $_GET['folder'] ?? '';
$subPath = $_GET['path'] ?? '';
$fileName = $_GET['file'] ?? '';
$statusOnly = isset($_GET['status']) && $_GET['status'] === '1';

$resolved = resolveAllowedFile($folderKey, $subPath, $fileName);
if ($resolved === false) {
    if ($statusOnly) {
        header('Content-Type: application/json; charset=utf-8');
        http_response_code(404);
        echo json_encode(['ready' => false, 'error' => 'Plik nie istnieje.']);
    } else {
        http_response_code(404);
    }
    exit;
}

$ext = strtolower(pathinfo($resolved['file'], PATHINFO_EXTENSION));
$allowedVideoExts = ['mp4', 'webm', 'mkv', 'avi', 'ts', 'mts'];
if (!in_array($ext, $allowedVideoExts, true)) {
    if ($statusOnly) {
        header('Content-Type: application/json; charset=utf-8');
        http_response_code(415);
        echo json_encode(['ready' => false, 'error' => 'Nieobsługiwany format filmu.']);
    } else {
        http_response_code(415);
    }
    exit;
}

$thumbDir = __DIR__ . '/data/video_thumbs';
if (!is_dir($thumbDir)) @mkdir($thumbDir, 0777, true);

$sourceSize = @filesize($resolved['full']);
$sourceMtime = @filemtime($resolved['full']);
if ($sourceSize === false || $sourceMtime === false) {
    if ($statusOnly) {
        header('Content-Type: application/json; charset=utf-8');
        http_response_code(500);
        echo json_encode(['ready' => false, 'error' => 'Nie można odczytać filmu.']);
    } else {
        http_response_code(500);
    }
    exit;
}

$thumbHash = md5('video-thumb-v3|' . $resolved['full'] . '|' . $sourceSize . '|' . $sourceMtime);
$thumbFile = $thumbDir . '/' . $thumbHash . '.jpg';
$thumbPart = $thumbDir . '/' . $thumbHash . '.part.jpg';
$thumbLock = $thumbDir . '/' . $thumbHash . '.lock';
$thumbLog = $thumbDir . '/' . $thumbHash . '.log';

function videoThumbIsValid($file) {
    if (!is_file($file) || @filesize($file) <= 0) return false;
    return @getimagesize($file) !== false;
}

function videoThumbCleanupCache($thumbDir) {
    $now = time();
    $lastCleanFile = $thumbDir . '/.last_clean';
    $cleanLockFile = $thumbDir . '/.clean.lock';
    $cleanLock = @fopen($cleanLockFile, 'c+');
    if ($cleanLock === false || !flock($cleanLock, LOCK_EX | LOCK_NB)) {
        if (is_resource($cleanLock)) fclose($cleanLock);
        return;
    }

    $lastClean = is_file($lastCleanFile) ? (int)@file_get_contents($lastCleanFile) : 0;
    if ($now - $lastClean < 1800) {
        flock($cleanLock, LOCK_UN);
        fclose($cleanLock);
        return;
    }
    @file_put_contents($lastCleanFile, (string)$now, LOCK_EX);

    foreach (array_merge(
        glob($thumbDir . '/*.part.jpg') ?: [],
        glob($thumbDir . '/*.lock') ?: [],
        glob($thumbDir . '/*.log') ?: []
    ) as $garbage) {
        if ($garbage === $cleanLockFile) continue;
        $mtime = @filemtime($garbage);
        if ($mtime !== false && $now - $mtime > 3600) @unlink($garbage);
    }

    foreach (glob($thumbDir . '/.worker_*.lock') ?: [] as $workerDir) {
        if (!is_dir($workerDir)) continue;
        $mtime = @filemtime($workerDir);
        if ($mtime !== false && $now - $mtime > 900) @rmdir($workerDir);
    }

    $cacheFiles = glob($thumbDir . '/*.jpg') ?: [];
    $cacheLimit = 1024 * 1024 * 1024;
    $totalSize = 0;
    $entries = [];

    foreach ($cacheFiles as $file) {
        $size = @filesize($file);
        if ($size === false) continue;
        $totalSize += $size;
        $entries[] = [
            'path' => $file,
            'size' => $size,
            'time' => @filemtime($file) ?: 0
        ];
    }

    if ($totalSize > $cacheLimit) {
        usort($entries, function($a, $b) { return $a['time'] <=> $b['time']; });
        foreach ($entries as $entry) {
            if ($totalSize <= $cacheLimit) break;
            if (@unlink($entry['path'])) $totalSize -= $entry['size'];
        }
    }

    flock($cleanLock, LOCK_UN);
    fclose($cleanLock);
}

function videoThumbAcquireWorkerSlot($thumbDir) {
    for ($i = 1; $i <= 2; $i++) {
        $slot = $thumbDir . '/.worker_' . $i . '.lock';
        if (is_dir($slot)) {
            $mtime = @filemtime($slot);
            if ($mtime !== false && time() - $mtime > 900) @rmdir($slot);
        }
        if (@mkdir($slot, 0777)) return $slot;
    }
    return false;
}

function videoThumbExecAvailable() {
    if (!function_exists('exec')) return false;
    $disabled = array_filter(array_map('trim', explode(',', (string)ini_get('disable_functions'))));
    return !in_array('exec', $disabled, true);
}

function videoThumbStartGeneration($sourceFile, $thumbFile, $thumbPart, $thumbLock, $thumbLog, $thumbDir) {
    if (!videoThumbExecAvailable()) return false;

    if (is_file($thumbLock)) {
        $mtime = @filemtime($thumbLock);
        if ($mtime !== false && time() - $mtime > 900) @unlink($thumbLock);
    }

    $lockHandle = @fopen($thumbLock, 'x');
    if ($lockHandle === false) return false;
    fclose($lockHandle);

    $workerSlot = videoThumbAcquireWorkerSlot($thumbDir);
    if ($workerSlot === false) {
        @unlink($thumbLock);
        return false;
    }

    @unlink($thumbPart);

    $ioNiceCmd = '';
    if (is_executable('/usr/bin/ionice')) {
        $testOutput = [];
        $testCode = 1;
        @exec('/usr/bin/ionice -c3 /bin/true >/dev/null 2>&1', $testOutput, $testCode);
        if ($testCode === 0) $ioNiceCmd = '/usr/bin/ionice -c3 ';
    }

    $filter = 'scale=320:180:force_original_aspect_ratio=decrease,pad=320:180:(ow-iw)/2:(oh-ih)/2:color=black';
    $common = '/usr/bin/timeout --kill-after=5 120 ' . $ioNiceCmd . '/usr/bin/nice -n 19 /usr/bin/ffmpeg -y -nostdin -hide_banner -loglevel error ';
    $firstTry = $common . '-ss 40 -i ' . escapeshellarg($sourceFile)
        . ' -map 0:v:0 -an -sn -threads 1 -vf ' . escapeshellarg($filter)
        . ' -frames:v 1 -q:v 6 ' . escapeshellarg($thumbPart);
    $fallbackTry = $common . '-ss 0 -i ' . escapeshellarg($sourceFile)
        . ' -map 0:v:0 -an -sn -threads 1 -vf ' . escapeshellarg($filter)
        . ' -frames:v 1 -q:v 6 ' . escapeshellarg($thumbPart);

    $backgroundCmd = $firstTry . ' 2>' . escapeshellarg($thumbLog)
        . '; if [ ! -s ' . escapeshellarg($thumbPart) . ' ]; then rm -f ' . escapeshellarg($thumbPart)
        . '; ' . $fallbackTry . ' 2>>' . escapeshellarg($thumbLog) . '; fi'
        . '; if [ -s ' . escapeshellarg($thumbPart) . ' ]; then mv -f ' . escapeshellarg($thumbPart) . ' ' . escapeshellarg($thumbFile)
        . '; rm -f ' . escapeshellarg($thumbLog)
        . '; else rm -f ' . escapeshellarg($thumbPart) . '; fi'
        . '; rm -f ' . escapeshellarg($thumbLock)
        . '; rmdir ' . escapeshellarg($workerSlot) . ' 2>/dev/null || true';

    @exec('nohup /bin/sh -c ' . escapeshellarg($backgroundCmd) . ' >/dev/null 2>&1 < /dev/null &');
    return true;
}

function videoThumbSendPlaceholder() {
    header('Content-Type: image/svg+xml; charset=utf-8');
    header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
    header('X-Content-Type-Options: nosniff');
    echo '<svg xmlns="http://www.w3.org/2000/svg" width="320" height="180" viewBox="0 0 320 180">'
        . '<rect width="320" height="180" fill="#20242a"/>'
        . '<rect x="118" y="56" width="84" height="62" rx="5" fill="#3a414b"/>'
        . '<path d="M118 56h84l-10-20h-84z" fill="#58616e"/>'
        . '<path d="M121 40l16 16m12-20l16 20m12-20l16 20" stroke="#d6d9de" stroke-width="7"/>'
        . '<path d="M148 72l32 15-32 15z" fill="#f2f2f2"/>'
        . '<text x="160" y="145" text-anchor="middle" font-family="Arial,sans-serif" font-size="14" fill="#b9bec7">Tworzenie miniatury...</text>'
        . '</svg>';
    exit;
}

if (!videoThumbIsValid($thumbFile) && is_file($thumbFile)) @unlink($thumbFile);

if (videoThumbIsValid($thumbFile)) {
    @touch($thumbFile);
    videoThumbCleanupCache($thumbDir);

    if ($statusOnly) {
        $params = 'folder=' . rawurlencode($resolved['folder'])
            . '&path=' . rawurlencode($resolved['path'])
            . '&file=' . rawurlencode($resolved['file'])
            . '&image=1&v=' . rawurlencode(substr($thumbHash, 0, 16));
        header('Content-Type: application/json; charset=utf-8');
        header('Cache-Control: no-store');
        echo json_encode(['ready' => true, 'url' => 'video_thumb.php?' . $params]);
        exit;
    }

    header('Content-Type: image/jpeg');
    header('Content-Length: ' . filesize($thumbFile));
    header('Cache-Control: private, max-age=2592000, immutable');
    header('ETag: "' . $thumbHash . '"');
    header('X-Content-Type-Options: nosniff');
    readfile($thumbFile);
    exit;
}

videoThumbCleanupCache($thumbDir);
$generationStarted = videoThumbStartGeneration(
    $resolved['full'],
    $thumbFile,
    $thumbPart,
    $thumbLock,
    $thumbLog,
    $thumbDir
);

if ($statusOnly) {
    header('Content-Type: application/json; charset=utf-8');
    header('Cache-Control: no-store');
    http_response_code(202);
    echo json_encode([
        'ready' => false,
        'generating' => $generationStarted || is_file($thumbLock)
    ]);
    exit;
}

videoThumbSendPlaceholder();
