<?php
declare(strict_types=1);

require __DIR__ . '/../../includes/bootstrap.php';
require_admin_login();

header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store, no-cache, must-revalidate');

const MEDIA_MAX_BYTES = 8 * 1024 * 1024; // 8MB
const MEDIA_MAX_SIDE  = 4000;            // max width/height

function respond(array $payload, int $status = 200): void
{
    http_response_code($status);
    echo json_encode($payload, JSON_UNESCAPED_SLASHES);
    exit;
}

function require_csrf(): string
{
    $token = (string)($_POST['csrf'] ?? $_GET['csrf'] ?? $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '');
    if (!csrf_check($token)) {
        respond(['ok' => false, 'error' => 'Invalid CSRF token.'], 400);
    }
    return $token;
}

function ensure_gd(): void
{
    if (!extension_loaded('gd')) {
        respond(['ok' => false, 'error' => 'GD extension is required on the server.'], 500);
    }
}

function load_project(int $id): array
{
    $st = pdo()->prepare("SELECT id, slug, title FROM projects WHERE id = ? LIMIT 1");
    $st->execute([$id]);
    $row = $st->fetch(PDO::FETCH_ASSOC);
    if (!$row) {
        respond(['ok' => false, 'error' => 'Project not found.'], 404);
    }
    return $row;
}

function ensure_media_dir(string $dir): void
{
    if (!is_dir($dir)) {
        if (!@mkdir($dir, 0755, true) && !is_dir($dir)) {
            respond(['ok' => false, 'error' => 'Cannot create media directory.'], 500);
        }
    }
    if (!is_writable($dir)) {
        respond(['ok' => false, 'error' => 'Media directory is not writable.'], 500);
    }
}

function list_media(string $dir, string $folder): array
{
    $files = [];
    if (!is_dir($dir)) return $files;

    foreach (glob($dir . DIRECTORY_SEPARATOR . '*.{jpg,jpeg,png,webp}', GLOB_BRACE) ?: [] as $path) {
        if (!is_file($path)) continue;
        $name = basename($path);
        $stat = @stat($path) ?: [];
        $size = (int)($stat['size'] ?? 0);
        $mtime = (int)($stat['mtime'] ?? time());
        $dim = @getimagesize($path) ?: [];
        $files[] = [
            'name'    => $name,
            'url'     => project_media_url($folder, $name),
            'size'    => $size,
            'width'   => (int)($dim[0] ?? 0),
            'height'  => (int)($dim[1] ?? 0),
            'modified'=> $mtime,
        ];
    }

    usort($files, fn($a, $b) => ($b['modified'] ?? 0) <=> ($a['modified'] ?? 0));
    return $files;
}

function normalize_orientation($img, array $info, string $binary)
{
    if (!function_exists('exif_read_data')) return $img;
    $mime = strtolower((string)($info['mime'] ?? ''));
    if ($mime !== 'image/jpeg') return $img;

    $orientation = 0;
    try {
        $exif = @exif_read_data('data://image/jpeg;base64,' . base64_encode($binary), 'IFD0');
        if (!empty($exif['Orientation'])) $orientation = (int)$exif['Orientation'];
    } catch (Throwable $e) {
        return $img;
    }
    if ($orientation < 2 || $orientation > 8) return $img;

    $canFlip = function_exists('imageflip');
    switch ($orientation) {
        case 2: if ($canFlip) imageflip($img, IMG_FLIP_HORIZONTAL); break;
        case 3: $img = imagerotate($img, 180, 0); break;
        case 4: if ($canFlip) imageflip($img, IMG_FLIP_VERTICAL); break;
        case 5: $img = imagerotate($img, 90, 0); if ($canFlip) imageflip($img, IMG_FLIP_HORIZONTAL); break;
        case 6: $img = imagerotate($img, -90, 0); break;
        case 7: $img = imagerotate($img, -90, 0); if ($canFlip) imageflip($img, IMG_FLIP_HORIZONTAL); break;
        case 8: $img = imagerotate($img, 90, 0); break;
    }
    return $img;
}

function save_image(string $binary, string $dir, string $folder): array
{
    ensure_gd();

    if (strlen($binary) > MEDIA_MAX_BYTES) {
        respond(['ok' => false, 'error' => 'Image exceeds 8MB limit.'], 400);
    }

    $info = @getimagesizefromstring($binary);
    if (!$info || empty($info['mime'])) {
        respond(['ok' => false, 'error' => 'Unsupported or invalid image.'], 400);
    }

    $mime = strtolower((string)$info['mime']);
    if (!in_array($mime, ['image/jpeg', 'image/png', 'image/webp'], true)) {
        respond(['ok' => false, 'error' => 'Only JPEG, PNG or WebP images are allowed.'], 400);
    }

    $img = @imagecreatefromstring($binary);
    if (!$img) {
        respond(['ok' => false, 'error' => 'Unable to read the image data.'], 400);
    }

    $img = normalize_orientation($img, $info, $binary);
    $width  = imagesx($img);
    $height = imagesy($img);

    $maxSide = MEDIA_MAX_SIDE;
    if ($width > $maxSide || $height > $maxSide) {
        $scale = $maxSide / max($width, $height);
        $newW = (int)max(1, floor($width * $scale));
        $newH = (int)max(1, floor($height * $scale));
        $dst = imagecreatetruecolor($newW, $newH);
        imagealphablending($dst, true);
        imagesavealpha($dst, true);
        imagecopyresampled($dst, $img, 0, 0, 0, 0, $newW, $newH, $width, $height);
        imagedestroy($img);
        $img = $dst;
        $width = $newW;
        $height = $newH;
    }

    $ext = 'jpg';
    if ($mime === 'image/png') $ext = 'png';
    if ($mime === 'image/webp' && function_exists('imagewebp')) $ext = 'webp';

    $filename = 'img-' . date('Ymd-His') . '-' . bin2hex(random_bytes(3)) . '.' . $ext;
    $path = $dir . DIRECTORY_SEPARATOR . $filename;

    switch ($ext) {
        case 'png':
            imagepng($img, $path, 6);
            break;
        case 'webp':
            imagewebp($img, $path, 85);
            break;
        default:
            imagejpeg($img, $path, 85);
            break;
    }
    imagedestroy($img);

    $stat = @stat($path) ?: [];
    $dim2 = @getimagesize($path) ?: [];

    return [
        'name'     => $filename,
        'url'      => project_media_url($folder, $filename),
        'size'     => (int)($stat['size'] ?? 0),
        'width'    => (int)($dim2[0] ?? $width),
        'height'   => (int)($dim2[1] ?? $height),
        'modified' => (int)($stat['mtime'] ?? time()),
    ];
}

function read_uploaded_file(array $files, int $idx): string
{
    $tmp  = (string)($files['tmp_name'][$idx] ?? '');
    $size = (int)($files['size'][$idx] ?? 0);
    $err  = (int)($files['error'][$idx] ?? UPLOAD_ERR_NO_FILE);

    if ($err !== UPLOAD_ERR_OK) {
        throw new RuntimeException('Upload failed for one file.');
    }
    if (!is_uploaded_file($tmp)) {
        throw new RuntimeException('Suspicious upload rejected.');
    }
    if ($size > MEDIA_MAX_BYTES) {
        throw new RuntimeException('File is larger than 8MB.');
    }
    $data = file_get_contents($tmp);
    if ($data === false) {
        throw new RuntimeException('Unable to read upload.');
    }
    if (strlen($data) > MEDIA_MAX_BYTES) {
        throw new RuntimeException('File is larger than 8MB.');
    }
    return $data;
}

function fetch_remote_image(string $url): string
{
    $url = trim($url);
    if ($url === '' || !filter_var($url, FILTER_VALIDATE_URL)) {
        throw new RuntimeException('Please provide a valid image URL.');
    }
    if (!preg_match('#^https?://#i', $url)) {
        throw new RuntimeException('Only http/https URLs are allowed.');
    }

    $data = '';
    if (function_exists('curl_init')) {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => false,
            CURLOPT_MAXREDIRS      => 0,
            CURLOPT_USERAGENT      => 'PortfolioMediaFetcher/1.0',
            CURLOPT_TIMEOUT        => 10,
            CURLOPT_CONNECTTIMEOUT => 5,
            CURLOPT_BUFFERSIZE     => 102400,
        ]);
        $data = (string)curl_exec($ch);
        $code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        if (curl_errno($ch) || $code >= 400 || $code === 0) {
            curl_close($ch);
            throw new RuntimeException('Failed to download image (HTTP ' . $code . ').');
        }
        curl_close($ch);
    } else {
        $ctx = stream_context_create([
            'http' => [
                'method'  => 'GET',
                'timeout' => 10,
                'header'  => "User-Agent: PortfolioMediaFetcher/1.0\r\n",
            ],
        ]);
        $fh = @fopen($url, 'rb', false, $ctx);
        if (!$fh) throw new RuntimeException('Could not open the URL.');
        while (!feof($fh) && strlen($data) <= MEDIA_MAX_BYTES) {
            $chunk = fread($fh, 8192);
            if ($chunk === false) break;
            $data .= $chunk;
            if (strlen($data) > MEDIA_MAX_BYTES) {
                fclose($fh);
                throw new RuntimeException('Remote image is larger than 8MB.');
            }
        }
        fclose($fh);
    }

    if ($data === '' || strlen($data) === 0) {
        throw new RuntimeException('Image download returned empty data.');
    }
    if (strlen($data) > MEDIA_MAX_BYTES) {
        throw new RuntimeException('Remote image is larger than 8MB.');
    }

    return $data;
}

// ----- Main routing -----
$csrfToken = require_csrf();
$projectId = (int)($_GET['project_id'] ?? $_POST['project_id'] ?? 0);
if ($projectId <= 0) respond(['ok' => false, 'error' => 'Missing project id.'], 400);

$project = load_project($projectId);
$folder  = project_media_folder($project);
$dir     = project_media_dir($project);
ensure_media_dir($dir);

$action = strtolower((string)($_REQUEST['action'] ?? ($_SERVER['REQUEST_METHOD'] === 'GET' ? 'list' : '')));

try {
    if ($action === 'list') {
        respond(['ok' => true, 'files' => list_media($dir, $folder)]);
    }

    if ($action === 'upload') {
        $files = $_FILES['files'] ?? null;
        if (!$files || !isset($files['tmp_name']) || !is_array($files['tmp_name'])) {
            respond(['ok' => false, 'error' => 'No files received.'], 400);
        }

        $saved = [];
        $total = count($files['tmp_name']);
        for ($i = 0; $i < $total; $i++) {
            $data = read_uploaded_file($files, $i);
            $saved[] = save_image($data, $dir, $folder);
            if (count($saved) >= 10) break; // prevent huge multi-upload bursts
        }

        respond(['ok' => true, 'saved' => count($saved), 'files' => $saved]);
    }

    if ($action === 'fetch_url') {
        $url = (string)($_POST['image_url'] ?? '');
        $data = fetch_remote_image($url);
        $file = save_image($data, $dir, $folder);
        respond(['ok' => true, 'saved' => 1, 'files' => [$file]]);
    }

    if ($action === 'delete') {
        $name = (string)($_POST['name'] ?? '');
        if ($name === '' || !preg_match('/^[a-zA-Z0-9._-]+$/', $name)) {
            respond(['ok' => false, 'error' => 'Bad filename.'], 400);
        }
        $path = realpath($dir . DIRECTORY_SEPARATOR . $name);
        $dirReal = realpath($dir);
        if (!$path || !$dirReal || strpos($path, $dirReal) !== 0 || !is_file($path)) {
            respond(['ok' => false, 'error' => 'File not found.'], 404);
        }
        @unlink($path);
        respond(['ok' => true]);
    }

    respond(['ok' => false, 'error' => 'Unknown action.'], 400);
} catch (Throwable $e) {
    respond(['ok' => false, 'error' => $e->getMessage()], 400);
}
