<?php
declare(strict_types=1);

// Supports local image optimization plus cached proxying for approved remote images.

$acceptHeader = $_SERVER['HTTP_ACCEPT'] ?? '';
$quality = clampInt($_GET['q'] ?? 82, 40, 92);
$targetWidth = clampInt($_GET['w'] ?? 0, 0, 2400);
$remoteUrl = isset($_GET['url']) ? trim((string) $_GET['url']) : '';

if ($remoteUrl !== '') {
    handleRemoteImage($remoteUrl, $targetWidth, $quality, $acceptHeader);
    exit;
}

$requestUri = $_SERVER['REQUEST_URI'] ?? '';
$requestPath = parse_url($requestUri, PHP_URL_PATH);
$filePath = __DIR__ . ($requestPath ?: '');

if (!is_file($filePath)) {
    header('HTTP/1.1 404 Not Found');
    echo 'Image not found.';
    exit;
}

serveOptimizedPath($filePath, $targetWidth, $quality, $acceptHeader);

function clampInt($value, int $min, int $max): int
{
    if (!is_numeric($value)) {
        return $min;
    }

    $intValue = (int) $value;
    if ($intValue < $min) {
        return $min;
    }
    if ($intValue > $max) {
        return $max;
    }

    return $intValue;
}

function handleRemoteImage(string $remoteUrl, int $targetWidth, int $quality, string $acceptHeader): void
{
    if (!isAllowedRemoteUrl($remoteUrl)) {
        header('HTTP/1.1 400 Bad Request');
        echo 'Unsupported remote image source.';
        exit;
    }

    $sourcePath = getRemoteSourcePath($remoteUrl);
    if ($sourcePath === null) {
        redirectToOriginal($remoteUrl);
    }

    serveOptimizedPath($sourcePath, $targetWidth, $quality, $acceptHeader);
}

function isAllowedRemoteUrl(string $url): bool
{
    $parts = parse_url($url);
    if (!$parts || ($parts['scheme'] ?? '') !== 'https') {
        return false;
    }

    $host = strtolower($parts['host'] ?? '');
    $allowedHosts = [
        'firebasestorage.googleapis.com',
        'images.unsplash.com',
        'lh3.googleusercontent.com',
    ];

    return in_array($host, $allowedHosts, true);
}

function getRemoteSourcePath(string $remoteUrl): ?string
{
    $remoteCacheDir = getCacheRoot() . DIRECTORY_SEPARATOR . 'remote';
    ensureDirectory($remoteCacheDir);

    $cacheKey = sha1($remoteUrl);
    $existing = glob($remoteCacheDir . DIRECTORY_SEPARATOR . $cacheKey . '.*');
    if (!empty($existing) && is_file($existing[0])) {
        return $existing[0];
    }

    $binary = fetchRemoteBinary($remoteUrl);
    if ($binary === null) {
        return null;
    }

    $imageInfo = @getimagesizefromstring($binary);
    if (!$imageInfo || empty($imageInfo['mime'])) {
        return null;
    }

    $extension = mimeToExtension($imageInfo['mime']);
    if ($extension === null) {
        return null;
    }

    $targetPath = $remoteCacheDir . DIRECTORY_SEPARATOR . $cacheKey . '.' . $extension;
    $tempPath = $targetPath . '.tmp';

    if (@file_put_contents($tempPath, $binary, LOCK_EX) === false) {
        @unlink($tempPath);
        return createTemporaryImageFile($cacheKey, $binary, $extension);
    }

    @rename($tempPath, $targetPath);
    @chmod($targetPath, 0644);

    if (is_file($targetPath)) {
        return $targetPath;
    }

    return createTemporaryImageFile($cacheKey, $binary, $extension);
}

function fetchRemoteBinary(string $remoteUrl): ?string
{
    if (function_exists('curl_init')) {
        $handle = curl_init($remoteUrl);
        curl_setopt_array($handle, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_CONNECTTIMEOUT => 10,
            CURLOPT_TIMEOUT => 20,
            CURLOPT_USERAGENT => 'SalumToursImageOptimizer/1.0',
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2,
        ]);

        $binary = curl_exec($handle);
        $statusCode = (int) curl_getinfo($handle, CURLINFO_RESPONSE_CODE);
        curl_close($handle);

        if ($statusCode >= 200 && $statusCode < 300 && is_string($binary) && $binary !== '') {
            return $binary;
        }
    }

    $context = stream_context_create([
        'http' => [
            'method' => 'GET',
            'timeout' => 20,
            'header' => "User-Agent: SalumToursImageOptimizer/1.0\r\n",
        ],
    ]);

    $binary = @file_get_contents($remoteUrl, false, $context);
    return is_string($binary) && $binary !== '' ? $binary : null;
}

function serveOptimizedPath(string $sourcePath, int $targetWidth, int $quality, string $acceptHeader): void
{
    $imageInfo = @getimagesize($sourcePath);
    if (!$imageInfo || empty($imageInfo['mime'])) {
        serveFile($sourcePath, detectMimeType($sourcePath));
        return;
    }

    $sourceMime = $imageInfo['mime'];
    $sourceFormat = mimeToFormat($sourceMime);
    if ($sourceFormat === null || in_array($sourceFormat, ['gif', 'svg'], true)) {
        serveFile($sourcePath, $sourceMime);
        return;
    }

    $wantsWebp = supportsWebp($acceptHeader) && function_exists('imagewebp');
    $targetFormat = $wantsWebp ? 'webp' : $sourceFormat;

    if ($targetWidth === 0 && $targetFormat === $sourceFormat) {
        serveFile($sourcePath, $sourceMime);
        return;
    }

    $variantPath = buildVariantPath($sourcePath, $targetWidth, $quality, $targetFormat);
    $variantMime = formatToMime($targetFormat);
    $sourceMTime = safeFilemtime($sourcePath);

    if (is_file($variantPath) && safeFilemtime($variantPath) >= $sourceMTime) {
        serveFile($variantPath, $variantMime);
        return;
    }

    $sourceImage = createImageResource($sourcePath, $sourceFormat);
    if (!$sourceImage) {
        serveFile($sourcePath, $sourceMime);
        return;
    }

    $sourceWidth = imagesx($sourceImage);
    $sourceHeight = imagesy($sourceImage);
    $resizeWidth = $targetWidth > 0 && $targetWidth < $sourceWidth ? $targetWidth : $sourceWidth;
    $resizeHeight = (int) round(($resizeWidth / $sourceWidth) * $sourceHeight);

    $outputImage = $sourceImage;
    if ($resizeWidth !== $sourceWidth) {
        $outputImage = createDestinationCanvas($resizeWidth, $resizeHeight, $sourceFormat);
        imagecopyresampled(
            $outputImage,
            $sourceImage,
            0,
            0,
            0,
            0,
            $resizeWidth,
            $resizeHeight,
            $sourceWidth,
            $sourceHeight
        );
    }

    ensureDirectory(dirname($variantPath));
    if (!saveImageResource($outputImage, $variantPath, $targetFormat, $quality)) {
        serveGeneratedImage($outputImage, $targetFormat, $quality);
        return;
    }

    @chmod($variantPath, 0644);

    if ($outputImage !== $sourceImage) {
        imagedestroy($outputImage);
    }
    imagedestroy($sourceImage);

    serveFile($variantPath, $variantMime);
}

function buildVariantPath(string $sourcePath, int $targetWidth, int $quality, string $targetFormat): string
{
    $variantsDir = getCacheRoot() . DIRECTORY_SEPARATOR . 'variants';
    $cacheKey = sha1($sourcePath . '|' . safeFilemtime($sourcePath) . '|' . $targetWidth . '|' . $quality . '|' . $targetFormat);

    return $variantsDir . DIRECTORY_SEPARATOR . $cacheKey . '.' . $targetFormat;
}

function createImageResource(string $sourcePath, string $format)
{
    switch ($format) {
        case 'jpg':
            $jpeg = function_exists('imagecreatefromjpeg') ? @imagecreatefromjpeg($sourcePath) : null;
            return $jpeg ?: null;
        case 'png':
            $image = function_exists('imagecreatefrompng') ? @imagecreatefrompng($sourcePath) : null;
            if ($image !== null && $image !== false) {
                imagepalettetotruecolor($image);
                imagealphablending($image, true);
                imagesavealpha($image, true);
            }
            return $image ?: null;
        case 'webp':
            $webp = function_exists('imagecreatefromwebp') ? @imagecreatefromwebp($sourcePath) : null;
            return $webp ?: null;
        default:
            return null;
    }
}

function createDestinationCanvas(int $width, int $height, string $sourceFormat)
{
    $canvas = imagecreatetruecolor($width, $height);

    if (in_array($sourceFormat, ['png', 'webp'], true)) {
        imagealphablending($canvas, false);
        imagesavealpha($canvas, true);
        $transparent = imagecolorallocatealpha($canvas, 0, 0, 0, 127);
        imagefilledrectangle($canvas, 0, 0, $width, $height, $transparent);
    }

    return $canvas;
}

function saveImageResource($image, string $targetPath, string $format, int $quality): bool
{
    switch ($format) {
        case 'jpg':
            return function_exists('imagejpeg') && @imagejpeg($image, $targetPath, $quality);
        case 'png':
            $compression = max(0, min(9, (int) round((100 - $quality) / 10)));
            return function_exists('imagepng') && @imagepng($image, $targetPath, $compression);
        case 'webp':
            return function_exists('imagewebp') && @imagewebp($image, $targetPath, $quality);
        default:
            return false;
    }
}

function supportsWebp(string $acceptHeader): bool
{
    return stripos($acceptHeader, 'image/webp') !== false;
}

function mimeToExtension(string $mime): ?string
{
    switch ($mime) {
        case 'image/jpeg':
            return 'jpg';
        case 'image/png':
            return 'png';
        case 'image/webp':
            return 'webp';
        case 'image/gif':
            return 'gif';
        default:
            return null;
    }
}

function mimeToFormat(string $mime): ?string
{
    switch ($mime) {
        case 'image/jpeg':
            return 'jpg';
        case 'image/png':
            return 'png';
        case 'image/webp':
            return 'webp';
        case 'image/gif':
            return 'gif';
        case 'image/svg+xml':
            return 'svg';
        default:
            return null;
    }
}

function formatToMime(string $format): string
{
    switch ($format) {
        case 'jpg':
            return 'image/jpeg';
        case 'png':
            return 'image/png';
        case 'webp':
            return 'image/webp';
        default:
            return 'application/octet-stream';
    }
}

function detectMimeType(string $filePath): string
{
    if (function_exists('mime_content_type')) {
        $mime = @mime_content_type($filePath);
        if (is_string($mime) && $mime !== '') {
            return $mime;
        }
    }

    return 'application/octet-stream';
}

function getCacheRoot(): string
{
    static $cacheRoot = null;

    if ($cacheRoot !== null) {
        return $cacheRoot;
    }

    $preferredRoot = __DIR__ . DIRECTORY_SEPARATOR . '.image-cache';
    if (ensureWritableDirectory($preferredRoot)) {
        $cacheRoot = $preferredRoot;
        return $cacheRoot;
    }

    $fallbackRoot = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'salum-tours-image-cache';
    if (ensureWritableDirectory($fallbackRoot)) {
        $cacheRoot = $fallbackRoot;
        return $cacheRoot;
    }

    $cacheRoot = $preferredRoot;
    return $cacheRoot;
}

function ensureDirectory(string $directory): void
{
    if (!is_dir($directory)) {
        @mkdir($directory, 0755, true);
    }
}

function ensureWritableDirectory(string $directory): bool
{
    ensureDirectory($directory);
    return is_dir($directory) && is_writable($directory);
}

function safeFilemtime(string $filePath): int
{
    $mtime = @filemtime($filePath);
    return $mtime !== false ? $mtime : time();
}

function createTemporaryImageFile(string $cacheKey, string $binary, string $extension): ?string
{
    $temporaryDirectory = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'salum-tours-image-optimizer';
    if (!ensureWritableDirectory($temporaryDirectory)) {
        return null;
    }

    $targetPath = $temporaryDirectory . DIRECTORY_SEPARATOR . $cacheKey . '.' . $extension;
    if (is_file($targetPath) && filesize($targetPath) > 0) {
        return $targetPath;
    }

    $tempPath = $targetPath . '.tmp';
    if (@file_put_contents($tempPath, $binary, LOCK_EX) === false) {
        @unlink($tempPath);
        return null;
    }

    @rename($tempPath, $targetPath);
    @chmod($targetPath, 0644);

    return is_file($targetPath) ? $targetPath : null;
}

function redirectToOriginal(string $url): void
{
    header('Cache-Control: public, max-age=3600');
    header('Location: ' . $url, true, 302);
    exit;
}

function serveGeneratedImage($image, string $format, int $quality): void
{
    header('Content-Type: ' . formatToMime($format));
    header('Cache-Control: public, max-age=86400');
    header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 86400) . ' GMT');
    header('Vary: Accept');
    header('X-Content-Type-Options: nosniff');

    switch ($format) {
        case 'jpg':
            imagejpeg($image, null, $quality);
            break;
        case 'png':
            $compression = max(0, min(9, (int) round((100 - $quality) / 10)));
            imagepng($image, null, $compression);
            break;
        case 'webp':
            imagewebp($image, null, $quality);
            break;
        default:
            break;
    }

    imagedestroy($image);
    exit;
}

function serveFile(string $filePath, string $mimeType): void
{
    $fileSize = filesize($filePath);
    $fileTime = safeFilemtime($filePath);
    $etag = sprintf('"%x-%x"', $fileSize, $fileTime);

    if (
        (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime((string) $_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $fileTime) ||
        (isset($_SERVER['HTTP_IF_NONE_MATCH']) && trim((string) $_SERVER['HTTP_IF_NONE_MATCH'], '"') === trim($etag, '"'))
    ) {
        header('HTTP/1.1 304 Not Modified');
        exit;
    }

    header('Content-Type: ' . $mimeType);
    header('Content-Length: ' . $fileSize);
    header('Cache-Control: public, max-age=31536000, immutable');
    header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 31536000) . ' GMT');
    header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $fileTime) . ' GMT');
    header('ETag: ' . $etag);
    header('Vary: Accept');
    header('X-Content-Type-Options: nosniff');

    readfile($filePath);
    exit;
}
