<?php
declare(strict_types=1);

/**
 * /functional/albums/upload.php
 * Завантаження фотографій у фотоальбом.
 *
 * POST:
 * - csrf
 * - album_id
 * - back (optional)
 * - set_cover_photo_id (optional, existing photo id)
 * - make_first_uploaded_cover = 1 (optional)
 * - photos[] / photos (multiple file input)
 * - caption[] (optional captions by file order)
 *
 * Результат:
 * - редірект назад на сторінку альбому
 * - am=uploaded / am=nofiles / am=limit / am=csrf / am=denied / am=db
 */

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

if (!headers_sent()) {
    header('X-Robots-Tag: noindex, nofollow, noarchive, nosnippet', true);
    header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0', true);
    header('Pragma: no-cache', true);
}

if (!function_exists('alb_upload_redirect')) {
    function alb_upload_redirect(string $url, int $code = 302): void
    {
        header('Location: ' . $url, true, $code);
        exit;
    }
}

if (!function_exists('alb_upload_require_login')) {
    function alb_upload_require_login(): void
    {
        if (function_exists('require_login')) {
            require_login();
            return;
        }

        $me = alb_me();
        if (!$me || (int)($me['id'] ?? 0) <= 0) {
            alb_upload_redirect('/login.php');
        }
    }
}

if (!function_exists('alb_upload_safe_back_url')) {
    function alb_upload_safe_back_url(string $back, string $fallback): string
    {
        $back = trim($back);
        if ($back === '') {
            return $fallback;
        }

        if (str_contains($back, "\r") || str_contains($back, "\n")) {
            return $fallback;
        }

        if (!str_starts_with($back, '/')) {
            return $fallback;
        }

        return $back;
    }
}

if (!function_exists('alb_upload_append_query')) {
    function alb_upload_append_query(string $url, array $params): string
    {
        $url = trim($url);
        if ($url === '') {
            $url = '/profile/index.php?tab=albums';
        }

        $fragment = '';
        if (str_contains($url, '#')) {
            [$url, $fragment] = explode('#', $url, 2);
            $fragment = '#' . $fragment;
        }

        $path = $url;
        $query = '';

        if (str_contains($url, '?')) {
            [$path, $query] = explode('?', $url, 2);
        }

        $current = [];
        if ($query !== '') {
            parse_str($query, $current);
            if (!is_array($current)) {
                $current = [];
            }
        }

        foreach ($params as $k => $v) {
            $current[(string)$k] = $v;
        }

        $qs = http_build_query($current);
        return $path . ($qs !== '' ? ('?' . $qs) : '') . $fragment;
    }
}

if (!function_exists('alb_upload_files_from_request')) {
    /**
     * Нормалізує $_FILES['photos'] у плоский список файлів.
     *
     * Підтримує:
     * - <input type="file" name="photos[]" multiple>
     * - <input type="file" name="photos">
     */
    function alb_upload_files_from_request(string $field = 'photos'): array
    {
        if (!isset($_FILES[$field]) || !is_array($_FILES[$field])) {
            return [];
        }

        $src = $_FILES[$field];

        if (!isset($src['name'])) {
            return [];
        }

        $out = [];

        if (is_array($src['name'])) {
            $count = count($src['name']);
            for ($i = 0; $i < $count; $i++) {
                $name = (string)($src['name'][$i] ?? '');
                $tmp  = (string)($src['tmp_name'][$i] ?? '');
                $err  = (int)($src['error'][$i] ?? UPLOAD_ERR_NO_FILE);
                $size = (int)($src['size'][$i] ?? 0);
                $type = (string)($src['type'][$i] ?? '');

                if ($name === '' && $tmp === '' && $err === UPLOAD_ERR_NO_FILE) {
                    continue;
                }

                $out[] = [
                    'name'     => $name,
                    'type'     => $type,
                    'tmp_name' => $tmp,
                    'error'    => $err,
                    'size'     => $size,
                    '_index'   => $i,
                ];
            }

            return $out;
        }

        $name = (string)($src['name'] ?? '');
        $tmp  = (string)($src['tmp_name'] ?? '');
        $err  = (int)($src['error'] ?? UPLOAD_ERR_NO_FILE);
        $size = (int)($src['size'] ?? 0);
        $type = (string)($src['type'] ?? '');

        if ($name === '' && $tmp === '' && $err === UPLOAD_ERR_NO_FILE) {
            return [];
        }

        return [[
            'name'     => $name,
            'type'     => $type,
            'tmp_name' => $tmp,
            'error'    => $err,
            'size'     => $size,
            '_index'   => 0,
        ]];
    }
}

if (!function_exists('alb_upload_existing_photo_ids')) {
    function alb_upload_existing_photo_ids(PDO $pdo, int $albumId): array
    {
        if ($albumId <= 0) {
            return [];
        }

        try {
            $st = $pdo->prepare("SELECT id FROM album_photos WHERE album_id = ?");
            $st->execute([$albumId]);
            $rows = $st->fetchAll(PDO::FETCH_COLUMN) ?: [];
            return array_values(array_map('intval', $rows));
        } catch (Throwable $e) {
            return [];
        }
    }
}

if (!function_exists('alb_upload_next_sort_order')) {
    function alb_upload_next_sort_order(PDO $pdo, int $albumId): int
    {
        try {
            $st = $pdo->prepare("SELECT COALESCE(MAX(sort_order), 0) FROM album_photos WHERE album_id = ?");
            $st->execute([$albumId]);
            return max(1, ((int)$st->fetchColumn()) + 1);
        } catch (Throwable $e) {
            return 1;
        }
    }
}

if (!function_exists('alb_upload_photo_abs_from_path')) {
    function alb_upload_photo_abs_from_path(string $path): string
    {
        $path = trim($path);
        $path = strtok($path, '?') ?: $path;
        $relative = ltrim($path, '/');

        if (preg_match('~^upload/~i', $relative)) {
            $relative = preg_replace('~^upload/~i', '', $relative) ?? $relative;
            $relative = ltrim($relative, '/');
        }

        return rtrim(alb_upload_root_abs(), '/') . '/' . $relative;
    }
}

alb_upload_require_login();

$pdo = alb_db();
$me  = alb_me();
$meId = alb_current_user_id($me);

if ($meId <= 0) {
    alb_upload_redirect('/login.php');
}

if (strtoupper((string)($_SERVER['REQUEST_METHOD'] ?? 'GET')) !== 'POST') {
    alb_upload_redirect('/profile/index.php?tab=albums');
}

if (!alb_ready($pdo)) {
    alb_upload_redirect('/profile/index.php?tab=albums&am=schema');
}

$albumId = (int)($_POST['album_id'] ?? 0);
if ($albumId <= 0) {
    alb_upload_redirect('/profile/index.php?tab=albums');
}

$album = alb_album_get($pdo, $albumId);
if (!$album) {
    alb_upload_redirect('/profile/index.php?tab=albums');
}

$owner = alb_user_get($pdo, (int)($album['user_id'] ?? 0));
$fallbackBack = $owner ? alb_album_url($album, $owner) : '/profile/index.php?tab=albums';

$rawBack = (string)($_POST['back'] ?? '');
$back = alb_upload_safe_back_url($rawBack, $fallbackBack);

if (!alb_csrf_check((string)($_POST['csrf'] ?? ''))) {
    alb_upload_redirect(alb_upload_append_query($back, ['am' => 'csrf']));
}

if (!alb_can_manage_album($album, $me)) {
    alb_upload_redirect(alb_upload_append_query($back, ['am' => 'denied']));
}

$username = trim((string)($owner['username'] ?? ''));
if ($username === '') {
    $username = 'user_' . (int)($album['user_id'] ?? 0);
}

$albumDir = alb_album_dir_abs($username, $albumId);
if (!is_dir($albumDir) && !mkdir($albumDir, 0775, true) && !is_dir($albumDir)) {
    alb_upload_redirect(alb_upload_append_query($back, ['am' => 'db']));
}
@chmod($albumDir, 0775);

$files = alb_upload_files_from_request('photos');
if (!$files) {
    alb_upload_redirect(alb_upload_append_query($back, ['am' => 'nofiles']));
}

/* м’яке обмеження за раз */
$maxFilesPerRequest = 20;
if (count($files) > $maxFilesPerRequest) {
    alb_upload_redirect(alb_upload_append_query($back, [
        'am'      => 'limit',
        'max'     => $maxFilesPerRequest,
        'attempt' => count($files),
    ]));
}

$captions = $_POST['caption'] ?? [];
if (!is_array($captions)) {
    $captions = [];
}

$existingPhotoIds = alb_upload_existing_photo_ids($pdo, $albumId);
$existingCoverId  = (int)($album['cover_photo_id'] ?? 0);
$setCoverPhotoId  = (int)($_POST['set_cover_photo_id'] ?? 0);
$makeFirstUploadedCover = ((int)($_POST['make_first_uploaded_cover'] ?? 0) === 1);

$nextSort = alb_upload_next_sort_order($pdo, $albumId);

$uploadedRows = [];
$uploadedFileAbs = [];
$firstInsertedPhotoId = 0;
$uploadedCount = 0;
$failedCount = 0;

$ownTx = !$pdo->inTransaction();

try {
    if ($ownTx) {
        $pdo->beginTransaction();
    }

    foreach ($files as $file) {
        $fileErr = (int)($file['error'] ?? UPLOAD_ERR_NO_FILE);
        if ($fileErr === UPLOAD_ERR_NO_FILE) {
            continue;
        }

        $captionIndex = (int)($file['_index'] ?? 0);
        $caption = alb_clean_caption((string)($captions[$captionIndex] ?? ''));

        try {
            [$urlPath, $mime, $size, $orig, $width, $height] = alb_move_uploaded_image(
                $file,
                $albumDir,
                10 * 1024 * 1024
            );

            $st = $pdo->prepare("
                INSERT INTO album_photos
                    (album_id, user_id, file_path, original_name, mime, size_bytes, width, height, caption, sort_order, created_at, updated_at)
                VALUES
                    (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
            ");
            $st->execute([
                $albumId,
                $meId,
                $urlPath,
                (string)$orig,
                (string)$mime,
                (int)$size,
                $width !== null ? (int)$width : null,
                $height !== null ? (int)$height : null,
                $caption !== '' ? $caption : null,
                $nextSort,
            ]);

            $photoId = (int)$pdo->lastInsertId();
            if ($photoId <= 0) {
                throw new RuntimeException('Failed to insert album photo.');
            }

            $nextSort++;
            $uploadedCount++;

            if ($firstInsertedPhotoId <= 0) {
                $firstInsertedPhotoId = $photoId;
            }

            $uploadedRows[] = [
                'id'        => $photoId,
                'file_path' => $urlPath,
            ];
            $uploadedFileAbs[] = alb_upload_photo_abs_from_path($urlPath);
        } catch (Throwable $e) {
            $failedCount++;
        }
    }

    /* cover_photo_id:
       1) якщо прийшов існуючий photo_id цього альбому
       2) якщо cover порожній і щось залили — ставимо перше нове фото
       3) якщо явно requested make_first_uploaded_cover=1 — ставимо перше нове фото
    */
    $newCoverId = 0;

    if ($setCoverPhotoId > 0 && in_array($setCoverPhotoId, $existingPhotoIds, true)) {
        $newCoverId = $setCoverPhotoId;
    } elseif ($firstInsertedPhotoId > 0 && ($existingCoverId <= 0 || $makeFirstUploadedCover)) {
        $newCoverId = $firstInsertedPhotoId;
    }

    if ($newCoverId > 0) {
        $st = $pdo->prepare("UPDATE photo_albums SET cover_photo_id = ?, updated_at = NOW() WHERE id = ? LIMIT 1");
        $st->execute([$newCoverId, $albumId]);
    } elseif ($uploadedCount > 0) {
        $st = $pdo->prepare("UPDATE photo_albums SET updated_at = NOW() WHERE id = ? LIMIT 1");
        $st->execute([$albumId]);
    }

    if ($uploadedCount <= 0) {
        throw new RuntimeException('No files uploaded.');
    }

    if ($ownTx) {
        $pdo->commit();
    }
} catch (Throwable $e) {
    if ($ownTx && $pdo->inTransaction()) {
        $pdo->rollBack();
    }

    foreach ($uploadedFileAbs as $abs) {
        alb_safe_unlink_under_upload($abs);
    }

    $am = ($uploadedCount <= 0) ? 'nofiles' : 'db';
    alb_upload_redirect(alb_upload_append_query($back, [
        'am'      => $am,
        'uploaded'=> $uploadedCount,
        'failed'  => $failedCount,
    ]));
}

/* якщо щось не вставилось у БД, але файли вже встигли покластись — БД закомітилась лише по успішних;
   тут нічого не чистимо, бо ці записи вже валідні */

alb_upload_redirect(alb_upload_append_query($back, [
    'am'       => 'uploaded',
    'uploaded' => $uploadedCount,
    'failed'   => $failedCount,
]));