<?php
declare(strict_types=1);

/**
 * /functional/albums/photo.php
 *
 * Безпечна віддача фото з альбомів:
 * - не дає відкривати фото альбомів напряму через /upload/... без перевірки прав
 * - ГОСТІ (неавторизовані) не можуть відкривати жодне фото альбомів по прямому URL
 * - для авторизованих перевіряє приватність альбому: public / friends / private
 * - додатково враховує приватність профілю власника
 * - для чужих / недоступних фото повертає 404
 *
 * Підтримує 2 режими:
 * 1) ?photo_id=123
 * 2) ?path=/upload/username/albums/1/file.webp
 *
 * Розраховано на rewrite з /upload/.htaccess:
 *   /upload/<user>/albums/<album_id>/<file>
 *   => /functional/albums/photo.php?path=/upload/<user>/albums/<album_id>/<file>
 */

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

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

if (!function_exists('albphoto_norm_path')) {
    function albphoto_norm_path(string $path): string
    {
        $path = str_replace('\\', '/', $path);
        $path = preg_replace('~/+~', '/', $path) ?? $path;
        return rtrim($path, '/');
    }
}

if (!function_exists('albphoto_starts_with')) {
    function albphoto_starts_with(string $haystack, string $needle): bool
    {
        if ($needle === '') return true;
        return strncmp($haystack, $needle, strlen($needle)) === 0;
    }
}

if (!function_exists('albphoto_safe_basename')) {
    function albphoto_safe_basename(string $name): string
    {
        $name = trim($name);
        if ($name === '') return 'photo';
        $name = str_replace(["\r", "\n", "\0", '"', "'", '\\', '/'], '_', $name);
        $name = preg_replace('/[^\pL\pN\.\-\_\(\)\s]+/u', '_', $name) ?? $name;
        $name = trim($name);
        return $name !== '' ? $name : 'photo';
    }
}

if (!function_exists('albphoto_404')) {
    function albphoto_404(): void
    {
        if (!headers_sent()) {
            http_response_code(404);
            header('Content-Type: text/plain; charset=UTF-8');
            header('X-Robots-Tag: noindex, nofollow, noarchive, nosnippet', true);
            header('Cache-Control: private, no-store, no-cache, must-revalidate, max-age=0', true);
            header('Pragma: no-cache', true);
            header('Expires: 0', true);
        }
        exit('Not found');
    }
}

if (!function_exists('albphoto_me_id')) {
    function albphoto_me_id(?array $me): int
    {
        return (int)(is_array($me) ? ($me['id'] ?? 0) : 0);
    }
}

if (!function_exists('albphoto_is_admin')) {
    function albphoto_is_admin(?array $me): bool
    {
        if (!is_array($me) || !$me) return false;

        if (function_exists('is_admin')) {
            try {
                return (bool)is_admin($me);
            } catch (Throwable $e) {
                // fallback below
            }
        }

        return strtolower((string)($me['role'] ?? '')) === 'admin';
    }
}

if (!function_exists('albphoto_are_friends')) {
    function albphoto_are_friends(PDO $pdo, int $a, int $b): bool
    {
        if ($a <= 0 || $b <= 0) return false;
        if ($a === $b) return true;

        try {
            $st = $pdo->prepare("
                SELECT 1
                FROM friend_requests
                WHERE (
                        (from_user_id = ? AND to_user_id = ?)
                     OR (from_user_id = ? AND to_user_id = ?)
                )
                  AND status = 'accepted'
                LIMIT 1
            ");
            $st->execute([$a, $b, $b, $a]);
            return (bool)$st->fetchColumn();
        } catch (Throwable $e) {
            return false;
        }
    }
}

if (!function_exists('albphoto_prepare_path')) {
    function albphoto_prepare_path(?string $raw): string
    {
        $path = trim((string)$raw);
        if ($path === '') return '';

        $path = rawurldecode($path);
        $path = str_replace("\0", '', $path);
        $path = str_replace('\\', '/', $path);
        $path = preg_replace('~/+~', '/', $path) ?? $path;
        $path = '/' . ltrim($path, '/');

        if (!albphoto_starts_with($path, '/upload/')) {
            $path = '/upload/' . ltrim($path, '/');
            $path = preg_replace('~/+~', '/', $path) ?? $path;
        }

        return $path;
    }
}

if (!function_exists('albphoto_load_row')) {
    function albphoto_load_row(PDO $pdo, int $photoId, string $path): ?array
    {
        try {
            if ($photoId > 0) {
                $st = $pdo->prepare("
                    SELECT
                        ap.id,
                        ap.album_id,
                        ap.user_id,
                        ap.file_path,
                        ap.original_name,
                        ap.mime,
                        ap.size_bytes,
                        ap.width,
                        ap.height,
                        ap.caption,
                        pa.user_id AS album_owner_id,
                        pa.privacy,
                        IFNULL(u.is_private, 0) AS owner_is_private
                    FROM album_photos ap
                    JOIN photo_albums pa ON pa.id = ap.album_id
                    JOIN users u ON u.id = pa.user_id
                    WHERE ap.id = ?
                    LIMIT 1
                ");
                $st->execute([$photoId]);
                $row = $st->fetch(PDO::FETCH_ASSOC) ?: null;
                if ($row) return $row;
            }

            if ($path !== '') {
                $st = $pdo->prepare("
                    SELECT
                        ap.id,
                        ap.album_id,
                        ap.user_id,
                        ap.file_path,
                        ap.original_name,
                        ap.mime,
                        ap.size_bytes,
                        ap.width,
                        ap.height,
                        ap.caption,
                        pa.user_id AS album_owner_id,
                        pa.privacy,
                        IFNULL(u.is_private, 0) AS owner_is_private
                    FROM album_photos ap
                    JOIN photo_albums pa ON pa.id = ap.album_id
                    JOIN users u ON u.id = pa.user_id
                    WHERE ap.file_path = ?
                    LIMIT 1
                ");
                $st->execute([$path]);
                $row = $st->fetch(PDO::FETCH_ASSOC) ?: null;
                if ($row) return $row;
            }
        } catch (Throwable $e) {
            error_log('albums/photo.php load row error: ' . $e->getMessage());
        }

        return null;
    }
}

if (!function_exists('albphoto_can_view')) {
    function albphoto_can_view(PDO $pdo, array $row, ?array $me): bool
    {
        $meId = albphoto_me_id($me);

        // КЛЮЧОВЕ ПОСИЛЕННЯ:
        // гість не може відкрити жодне фото альбомів по прямому URL
        if ($meId <= 0) {
            return false;
        }

        $ownerId  = (int)($row['album_owner_id'] ?? 0);
        $isOwner  = ($ownerId > 0 && $meId === $ownerId);
        $isAdmin  = albphoto_is_admin($me);
        $privacy  = (string)($row['privacy'] ?? 'public');
        $privacy  = in_array($privacy, ['public', 'friends', 'private'], true) ? $privacy : 'public';
        $isPrivateProfile = ((int)($row['owner_is_private'] ?? 0) === 1);

        $areFriends = false;
        if (!$isOwner && !$isAdmin && $ownerId > 0) {
            $areFriends = albphoto_are_friends($pdo, $meId, $ownerId);
        }

        // якщо профіль закритий — навіть public-альбоми не видно стороннім недрузям
        if ($isPrivateProfile && !$isOwner && !$isAdmin && !$areFriends) {
            return false;
        }

        if ($privacy === 'public') {
            return true;
        }

        if ($privacy === 'friends') {
            return ($isOwner || $isAdmin || $areFriends);
        }

        // private
        return ($isOwner || $isAdmin);
    }
}

if (!function_exists('albphoto_abs_file')) {
    function albphoto_abs_file(string $relPath): ?string
    {
        $relPath = trim($relPath);
        if ($relPath === '') return null;

        $projectRoot = realpath(__DIR__ . '/../../');
        if (!$projectRoot) {
            $projectRoot = __DIR__ . '/../../';
        }

        $projectRoot = albphoto_norm_path((string)$projectRoot);

        $uploadRoot = realpath($projectRoot . '/upload');
        if (!$uploadRoot) {
            $uploadRoot = $projectRoot . '/upload';
        }
        $uploadRoot = albphoto_norm_path((string)$uploadRoot);

        $full = $projectRoot . '/' . ltrim($relPath, '/');
        $real = realpath($full);

        if (!$real || !is_file($real)) {
            return null;
        }

        $realNorm = albphoto_norm_path((string)$real);

        if (
            $realNorm !== $uploadRoot
            && !albphoto_starts_with($realNorm, $uploadRoot . '/')
        ) {
            return null;
        }

        return $real;
    }
}

$photoId = (int)($_GET['photo_id'] ?? 0);
$path    = albphoto_prepare_path((string)($_GET['path'] ?? ''));

if ($photoId <= 0 && $path === '') {
    albphoto_404();
}

$row = albphoto_load_row($pdo, $photoId, $path);
if (!$row) {
    albphoto_404();
}

if (!albphoto_can_view($pdo, $row, $me)) {
    albphoto_404();
}

$filePath = trim((string)($row['file_path'] ?? ''));
$fileAbs  = albphoto_abs_file($filePath);
if (!$fileAbs) {
    albphoto_404();
}

$mime = trim((string)($row['mime'] ?? ''));
if ($mime === '' || stripos($mime, 'image/') !== 0) {
    $fi = function_exists('finfo_open') ? @finfo_open(FILEINFO_MIME_TYPE) : false;
    if ($fi) {
        $detected = (string)@finfo_file($fi, $fileAbs);
        @finfo_close($fi);
        if ($detected !== '') {
            $mime = $detected;
        }
    }
    if ($mime === '') {
        $mime = 'application/octet-stream';
    }
}

$size = (int)($row['size_bytes'] ?? 0);
if ($size <= 0) {
    $fs = @filesize($fileAbs);
    if ($fs !== false) {
        $size = (int)$fs;
    }
}

$downloadName = trim((string)($row['original_name'] ?? ''));
if ($downloadName === '') {
    $downloadName = basename($fileAbs);
}
$downloadName = albphoto_safe_basename($downloadName);

if (!headers_sent()) {
    http_response_code(200);
    header('Content-Type: ' . $mime);
    if ($size > 0) {
        header('Content-Length: ' . (string)$size);
    }
    header('Content-Disposition: inline; filename="' . $downloadName . '"');
    header('X-Content-Type-Options: nosniff', true);
    header('X-Robots-Tag: noindex, nofollow, noarchive, nosnippet', true);
    header('Cache-Control: private, no-store, no-cache, must-revalidate, max-age=0', true);
    header('Pragma: no-cache', true);
    header('Expires: 0', true);
}

$fh = @fopen($fileAbs, 'rb');
if ($fh === false) {
    albphoto_404();
}

while (!feof($fh)) {
    $chunk = fread($fh, 8192);
    if ($chunk === false) break;
    echo $chunk;
}

fclose($fh);
exit;