<?php
declare(strict_types=1);

/**
 * /functional/video/stream.php
 * Стрім відео з підтримкою Range (перемотка):
 * - доступ тільки власнику
 * - читаємо файл з FS по rel_path
 *
 * ВАЖЛИВО:
 * - не виводимо текст/HTML у відповіді (щоб не ламати плеєр)
 * - коректний 206/416 для Range
 * - private/no-store (приватний контент)
 */

require_once __DIR__ . '/../../includes/start.php';

$pdo = db();
$me  = current_user();

require_once __DIR__ . '/_lib.php';

require_login();
mv_private_seo_headers();

/* прибираємо будь-який буфер, щоб Range не ламався */
while (ob_get_level() > 0) { @ob_end_clean(); }

/* для великих файлів */
@set_time_limit(0);
@ini_set('zlib.output_compression', '0');

if (!headers_sent()) {
  header('X-Content-Type-Options: nosniff', true);
  header('Accept-Ranges: bytes', true);
  header('Content-Encoding: identity', true);

  /* приватність / кеш */
  header('Cache-Control: private, no-store, no-cache, must-revalidate, max-age=0', true);
  header('Pragma: no-cache', true);
  header('Expires: 0', true);
}

$id = (int)($_GET['id'] ?? 0);
if ($id <= 0) {
  http_response_code(404);
  exit;
}

/* беремо тільки потрібне */
$st = $pdo->prepare("SELECT id, user_id, rel_path, mime FROM myvideos WHERE id=? AND user_id=? LIMIT 1");
$st->execute([$id, (int)($me['id'] ?? 0)]);
$v = $st->fetch(PDO::FETCH_ASSOC);

if (!$v) {
  http_response_code(404);
  exit;
}

$rel = (string)($v['rel_path'] ?? '');
$fs  = mv_rel_to_fs($rel);

if ($rel === '' || !is_file($fs) || !mv_fs_is_safe($fs)) {
  http_response_code(404);
  exit;
}

$size = (int)@filesize($fs);
if ($size <= 0) {
  http_response_code(404);
  exit;
}

/* mime sanitize */
$mime = (string)($v['mime'] ?? 'video/mp4');
$mime = trim(explode(';', $mime, 2)[0]);
$allowed = mv_allowed_video_mimes();
if (!in_array($mime, $allowed, true)) {
  // fallback по розширенню
  $ext = strtolower(pathinfo($rel, PATHINFO_EXTENSION));
  $mime = match ($ext) {
    'webm' => 'video/webm',
    'ogg'  => 'video/ogg',
    'mov'  => 'video/quicktime',
    'm4v'  => 'video/x-m4v',
    'mkv'  => 'video/x-matroska',
    default => 'video/mp4',
  };
}

$fp = @fopen($fs, 'rb');
if (!$fp) {
  http_response_code(500);
  exit;
}

$start = 0;
$end   = $size - 1;
$send206 = false;

$rangeHeader = (string)($_SERVER['HTTP_RANGE'] ?? '');
$rangeHeader = trim($rangeHeader);

/* не підтримуємо multi-range: bytes=0-1,2-3 */
if ($rangeHeader !== '' && str_contains($rangeHeader, ',')) {
  http_response_code(416);
  header("Content-Range: bytes */$size");
  @fclose($fp);
  exit;
}

if ($rangeHeader !== '' && preg_match('/^bytes=(\d*)-(\d*)$/i', $rangeHeader, $m)) {
  $rStart = ($m[1] !== '') ? (int)$m[1] : null;
  $rEnd   = ($m[2] !== '') ? (int)$m[2] : null;

  if ($rStart === null && $rEnd !== null) {
    // bytes=-500 (останні 500 байт)
    if ($rEnd <= 0) {
      http_response_code(416);
      header("Content-Range: bytes */$size");
      @fclose($fp);
      exit;
    }
    $rStart = max(0, $size - $rEnd);
    $rEnd   = $size - 1;
  } elseif ($rStart !== null && $rEnd === null) {
    // bytes=500-
    $rEnd = $size - 1;
  }

  if ($rStart === null || $rStart < 0 || $rStart >= $size) {
    http_response_code(416);
    header("Content-Range: bytes */$size");
    @fclose($fp);
    exit;
  }

  if ($rEnd === null) $rEnd = $size - 1;
  if ($rEnd < $rStart) {
    http_response_code(416);
    header("Content-Range: bytes */$size");
    @fclose($fp);
    exit;
  }

  $start = $rStart;
  $end   = min($size - 1, $rEnd);
  $send206 = true;
}

$length = ($end - $start) + 1;

header('Content-Type: ' . $mime);

if ($send206) {
  http_response_code(206);
  header("Content-Range: bytes $start-$end/$size");
  header('Content-Length: ' . (string)$length);
  @fseek($fp, $start);
} else {
  header('Content-Length: ' . (string)$size);
}

/* HEAD: тільки заголовки */
if (strtoupper((string)($_SERVER['REQUEST_METHOD'] ?? 'GET')) === 'HEAD') {
  @fclose($fp);
  exit;
}

/* stream */
$chunk = 1024 * 1024; // 1MB
$left  = $length;

while ($left > 0 && !feof($fp)) {
  if (connection_aborted()) break;

  $read = ($left > $chunk) ? $chunk : $left;
  $buf = fread($fp, $read);
  if ($buf === false || $buf === '') break;

  echo $buf;
  $left -= strlen($buf);

  @flush();
}

@fclose($fp);
exit;