<?php
declare(strict_types=1);

ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);

/**
 * timthumb.php compatible simple resizer for PHP 8.2+
 *
 * Parámetros soportados:
 *   src = ruta local o URL remota
 *   w   = ancho
 *   h   = alto
 *   zc  = 1 recorte exacto, 0 ajuste proporcional
 *   q   = calidad 1..100
 */

function tt_fail(string $message, int $status = 400): void
{
    if (!headers_sent()) {
        http_response_code($status);
        header('Content-Type: text/plain; charset=utf-8');
        header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
    }
    echo $message;
    exit;
}

function tt_int($value, int $default = 0): int
{
    if ($value === null || $value === '') {
        return $default;
    }
    return (int)preg_replace('/[^0-9-]/', '', (string)$value);
}

function tt_clamp(int $value, int $min, int $max): int
{
    return max($min, min($max, $value));
}

function tt_norm(string $path): string
{
    return str_replace('\\', '/', $path);
}

function tt_ensure_dir(string $dir): bool
{
    if (is_dir($dir)) {
        return is_writable($dir);
    }
    if (!@mkdir($dir, 0777, true) && !is_dir($dir)) {
        return false;
    }
    return is_writable($dir);
}

function tt_mime(string $file): string
{
    $info = @getimagesize($file);
    if (!$info || empty($info['mime'])) {
        return '';
    }
    return (string)$info['mime'];
}

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

function tt_open_image(string $file, string $mime)
{
    switch ($mime) {
        case 'image/jpeg': return @imagecreatefromjpeg($file);
        case 'image/png':  return @imagecreatefrompng($file);
        case 'image/gif':  return @imagecreatefromgif($file);
        case 'image/webp': return function_exists('imagecreatefromwebp') ? @imagecreatefromwebp($file) : false;
        default:           return false;
    }
}

function tt_output_image($image, string $mime, int $quality, ?string $saveTo = null): bool
{
    if ($mime === 'image/jpeg') {
        return imagejpeg($image, $saveTo, $quality);
    }

    if ($mime === 'image/png') {
        $pngCompression = (int)round((100 - $quality) / 10);
        $pngCompression = max(0, min(9, $pngCompression));
        return imagepng($image, $saveTo, $pngCompression);
    }

    if ($mime === 'image/gif') {
        return imagegif($image, $saveTo);
    }

    if ($mime === 'image/webp' && function_exists('imagewebp')) {
        return imagewebp($image, $saveTo, $quality);
    }

    return false;
}

function tt_send_file(string $file, string $mime): void
{
    $mtime = @filemtime($file) ?: time();
    $etag = '"' . md5($file . '|' . $mtime . '|' . ((string)@filesize($file))) . '"';
    $lastModified = gmdate('D, d M Y H:i:s', $mtime) . ' GMT';

    $ifNoneMatch = trim((string)($_SERVER['HTTP_IF_NONE_MATCH'] ?? ''));
    if ($ifNoneMatch !== '' && $ifNoneMatch === $etag) {
        http_response_code(304);
        exit;
    }

    $ifModifiedSince = trim((string)($_SERVER['HTTP_IF_MODIFIED_SINCE'] ?? ''));
    if ($ifModifiedSince !== '') {
        $ifModifiedSince = preg_replace('/;.*$/', '', $ifModifiedSince);
        if ($ifModifiedSince === $lastModified) {
            http_response_code(304);
            exit;
        }
    }

    header('Content-Type: ' . $mime);
    header('Content-Length: ' . (string)filesize($file));
    header('Last-Modified: ' . $lastModified);
    header('ETag: ' . $etag);
    header('Cache-Control: public, max-age=31536000, immutable');
    header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 31536000) . ' GMT');

    readfile($file);
    exit;
}

function tt_host_allowed(string $host, array $allowedHosts): bool
{
    $host = strtolower(trim($host));
    if ($host === '') {
        return false;
    }

    foreach ($allowedHosts as $allowed) {
        $allowed = strtolower(trim($allowed));
        if ($allowed === '') {
            continue;
        }
        if ($host === $allowed) {
            return true;
        }
        if (str_ends_with($host, '.' . $allowed)) {
            return true;
        }
    }

    return false;
}

function tt_host_public(string $host): bool
{
    if (filter_var($host, FILTER_VALIDATE_IP)) {
        return false;
    }

    $resolvedAny = false;

    $ipv4 = @gethostbynamel($host);
    if (is_array($ipv4) && count($ipv4) > 0) {
        $resolvedAny = true;
        foreach ($ipv4 as $ip) {
            if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
                return false;
            }
        }
    }

    if (function_exists('dns_get_record')) {
        $ipv6Records = @dns_get_record($host, DNS_AAAA);
        if (is_array($ipv6Records) && count($ipv6Records) > 0) {
            $resolvedAny = true;
            foreach ($ipv6Records as $record) {
                $ip6 = (string)($record['ipv6'] ?? '');
                if ($ip6 !== '' && filter_var($ip6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
                    return false;
                }
            }
        }
    }

    return $resolvedAny;
}

function tt_download_remote(string $url, string $cacheDir, array $allowedHosts, int $ttl, int $maxBytes): string
{
    if (!function_exists('curl_init')) {
        tt_fail('cURL no esta disponible', 500);
    }

    $parts = @parse_url($url);
    if (!is_array($parts)) {
        tt_fail('src remoto invalido', 400);
    }

    $scheme = strtolower((string)($parts['scheme'] ?? ''));
    $host = strtolower((string)($parts['host'] ?? ''));

    if (!in_array($scheme, array('http', 'https'), true)) {
        tt_fail('solo se permite http/https', 400);
    }

    if (!tt_host_allowed($host, $allowedHosts)) {
        tt_fail('host remoto no permitido', 403);
    }

    if (!tt_host_public($host)) {
        tt_fail('host remoto bloqueado', 403);
    }

    if (!tt_ensure_dir($cacheDir)) {
        tt_fail('no se pudo crear cache_src', 500);
    }

    $hash = sha1($url);
    $metaFile = $cacheDir . '/' . $hash . '.json';

    if (is_file($metaFile)) {
        $meta = json_decode((string)@file_get_contents($metaFile), true);
        if (is_array($meta)) {
            $file = (string)($meta['file'] ?? '');
            $ts = (int)($meta['ts'] ?? 0);
            if ($file !== '' && is_file($file) && (time() - $ts) <= $ttl) {
                return $file;
            }
        }
    }

    $tmp = tempnam(sys_get_temp_dir(), 'tt_');
    if ($tmp === false) {
        tt_fail('no se pudo crear temporal', 500);
    }

    $fp = @fopen($tmp, 'wb');
    if ($fp === false) {
        @unlink($tmp);
        tt_fail('no se pudo abrir temporal', 500);
    }

    $ch = curl_init($url);
    if ($ch === false) {
        fclose($fp);
        @unlink($tmp);
        tt_fail('fallo curl_init', 500);
    }

    curl_setopt($ch, CURLOPT_FILE, $fp);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_MAXREDIRS, 3);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 8);
    curl_setopt($ch, CURLOPT_TIMEOUT, 20);
    curl_setopt($ch, CURLOPT_USERAGENT, 'timthumb-php82/2.0');
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
    curl_setopt($ch, CURLOPT_HEADER, false);
    curl_setopt($ch, CURLOPT_NOPROGRESS, false);

    if (defined('CURLOPT_PROTOCOLS')) {
        curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
    }
    if (defined('CURLOPT_REDIR_PROTOCOLS')) {
        curl_setopt($ch, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
    }

    curl_setopt(
        $ch,
        CURLOPT_XFERINFOFUNCTION,
        static function ($resource, $downloadSize, $downloaded, $uploadSize, $uploaded) use ($maxBytes): int {
            return ((int)$downloaded > $maxBytes) ? 1 : 0;
        }
    );

    $ok = curl_exec($ch);
    $errno = curl_errno($ch);
    $httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    fclose($fp);

    if ($ok !== true || $errno !== 0) {
        @unlink($tmp);
        tt_fail('fallo descarga remota', 502);
    }

    if ($httpCode < 200 || $httpCode >= 300) {
        @unlink($tmp);
        tt_fail('respuesta HTTP remota invalida: ' . $httpCode, 502);
    }

    clearstatcache(true, $tmp);
    $size = @filesize($tmp) ?: 0;
    if ($size <= 0 || $size > $maxBytes) {
        @unlink($tmp);
        tt_fail('archivo remoto invalido', 415);
    }

    $mime = tt_mime($tmp);
    $allowedMimes = array('image/jpeg', 'image/png', 'image/gif', 'image/webp');
    if (!in_array($mime, $allowedMimes, true)) {
        @unlink($tmp);
        tt_fail('mime remoto no permitido', 415);
    }

    $finalFile = $cacheDir . '/' . $hash . '.' . tt_extension_from_mime($mime);
    if (!@rename($tmp, $finalFile)) {
        @unlink($tmp);
        tt_fail('no se pudo mover cache remoto', 500);
    }

    @chmod($finalFile, 0666);

    $meta = array(
        'url'  => $url,
        'file' => $finalFile,
        'mime' => $mime,
        'ts'   => time(),
    );
    @file_put_contents($metaFile, json_encode($meta, JSON_UNESCAPED_SLASHES));

    return $finalFile;
}

$maxDimension = 6000;
$maxDownloadBytes = 12 * 1024 * 1024;
$remoteCacheTtl = 6 * 3600;
$cacheDir = __DIR__ . '/cache';
$remoteCacheDir = __DIR__ . '/cache_src';

if (!function_exists('imagecreatetruecolor')) {
    tt_fail('GD no esta habilitado', 500);
}

$src = trim((string)($_GET['src'] ?? ''));
$src = str_replace("\0", '', $src);
$src = preg_replace('/[\r\n]+/', '', $src);
if ($src === '') {
    tt_fail('falta src', 400);
}

$w = tt_clamp(tt_int($_GET['w'] ?? 0, 0), 0, $maxDimension);
$h = tt_clamp(tt_int($_GET['h'] ?? 0, 0), 0, $maxDimension);
$q = tt_clamp(tt_int($_GET['q'] ?? 80, 80), 1, 100);
$zc = tt_int($_GET['zc'] ?? 0, 0) === 1;

$docRoot = rtrim((string)($_SERVER['DOCUMENT_ROOT'] ?? ''), '/');
if ($docRoot === '') {
    tt_fail('DOCUMENT_ROOT vacio', 500);
}

$docRootReal = realpath($docRoot);
if ($docRootReal === false) {
    tt_fail('DOCUMENT_ROOT invalido', 500);
}

$docRootReal = tt_norm($docRootReal);

$cacheOk = tt_ensure_dir($cacheDir);
if (!$cacheOk) {
    tt_fail('cache no escribible', 500);
}

$httpHost = strtolower((string)($_SERVER['HTTP_HOST'] ?? ''));
$httpHost = preg_replace('/:\d+$/', '', $httpHost);

$allowedRemoteHosts = array(
    $httpHost,
    'www.' . $httpHost,
    'mundosindical.com.ar',
    'www.mundosindical.com.ar',
);
$allowedRemoteHosts = array_values(array_unique(array_filter($allowedRemoteHosts)));

$sourceFile = false;
$parsed = @parse_url($src);
$isRemote = is_array($parsed) && isset($parsed['scheme']) && isset($parsed['host']);

if ($isRemote) {
    $remoteHost = strtolower((string)($parsed['host'] ?? ''));
    $remoteHost = preg_replace('/:\d+$/', '', $remoteHost);

    if (!tt_host_allowed($remoteHost, $allowedRemoteHosts)) {
        tt_fail('host remoto no permitido', 403);
    }

    $pathOnly = (string)parse_url($src, PHP_URL_PATH);
    $pathOnly = tt_norm($pathOnly);
    $relative = ltrim($pathOnly, '/');
    $tryLocal = realpath($docRootReal . '/' . $relative);

    if ($tryLocal !== false && is_file($tryLocal)) {
        $tryLocalNorm = tt_norm($tryLocal);
        if (strncmp($tryLocalNorm, $docRootReal, strlen($docRootReal)) === 0) {
            $sourceFile = $tryLocal;
        }
    }

    if ($sourceFile === false) {
        $sourceFile = tt_download_remote($src, $remoteCacheDir, $allowedRemoteHosts, $remoteCacheTtl, $maxDownloadBytes);
    }
} else {
    $localPath = (string)parse_url($src, PHP_URL_PATH);
    $localPath = tt_norm($localPath);
    $relative = ltrim($localPath, '/');
    $resolved = realpath($docRootReal . '/' . $relative);

    if ($resolved === false || !is_file($resolved)) {
        tt_fail('imagen no encontrada', 404);
    }

    $resolvedNorm = tt_norm($resolved);
    if (strncmp($resolvedNorm, $docRootReal, strlen($docRootReal)) !== 0) {
        tt_fail('ruta invalida', 403);
    }

    $sourceFile = $resolved;
}

if ($sourceFile === false || !is_file($sourceFile)) {
    tt_fail('imagen no encontrada', 404);
}

$mime = tt_mime($sourceFile);
$validMimes = array('image/jpeg', 'image/png', 'image/gif', 'image/webp');
if (!in_array($mime, $validMimes, true)) {
    tt_fail('mime invalido', 415);
}

$info = @getimagesize($sourceFile);
if (!$info) {
    tt_fail('imagen invalida', 415);
}

$srcW = (int)$info[0];
$srcH = (int)$info[1];
if ($srcW < 1 || $srcH < 1) {
    tt_fail('dimensiones invalidas', 415);
}

$dstW = $srcW;
$dstH = $srcH;

if ($w > 0 && $h === 0) {
    $dstW = $w;
    $dstH = max(1, (int)round($srcH * ($dstW / $srcW)));
} elseif ($h > 0 && $w === 0) {
    $dstH = $h;
    $dstW = max(1, (int)round($srcW * ($dstH / $srcH)));
} elseif ($w > 0 && $h > 0) {
    if ($zc) {
        $dstW = $w;
        $dstH = $h;
    } else {
        $scale = min($w / $srcW, $h / $srcH);
        $dstW = max(1, (int)round($srcW * $scale));
        $dstH = max(1, (int)round($srcH * $scale));
    }
}

$dstW = tt_clamp($dstW, 1, $maxDimension);
$dstH = tt_clamp($dstH, 1, $maxDimension);

$srcMtime = @filemtime($sourceFile) ?: 0;
$cacheKey = tt_norm($sourceFile) . '|' . $srcMtime . '|w=' . $dstW . '|h=' . $dstH . '|zc=' . ($zc ? '1' : '0') . '|q=' . $q;
$cacheFile = $cacheDir . '/' . md5($cacheKey) . '.' . tt_extension_from_mime($mime);

if (is_file($cacheFile)) {
    tt_send_file($cacheFile, $mime);
}

$sourceImage = tt_open_image($sourceFile, $mime);
if ($sourceImage === false) {
    tt_fail('no se pudo abrir la imagen', 500);
}

$canvas = imagecreatetruecolor($dstW, $dstH);
if ($canvas === false) {
    imagedestroy($sourceImage);
    tt_fail('no se pudo crear canvas', 500);
}

if (in_array($mime, array('image/png', 'image/gif', 'image/webp'), true)) {
    imagealphablending($canvas, false);
    imagesavealpha($canvas, true);
    $transparent = imagecolorallocatealpha($canvas, 0, 0, 0, 127);
    imagefilledrectangle($canvas, 0, 0, $dstW, $dstH, $transparent);
}

if ($zc && $w > 0 && $h > 0) {
    $dstRatio = $dstW / $dstH;
    $srcRatio = $srcW / $srcH;

    if ($srcRatio > $dstRatio) {
        $cropH = $srcH;
        $cropW = (int)round($srcH * $dstRatio);
        $srcX = (int)round(($srcW - $cropW) / 2);
        $srcY = 0;
    } else {
        $cropW = $srcW;
        $cropH = (int)round($srcW / $dstRatio);
        $srcX = 0;
        $srcY = (int)round(($srcH - $cropH) / 2);
    }

    imagecopyresampled($canvas, $sourceImage, 0, 0, $srcX, $srcY, $dstW, $dstH, $cropW, $cropH);
} else {
    imagecopyresampled($canvas, $sourceImage, 0, 0, 0, 0, $dstW, $dstH, $srcW, $srcH);
}

if (!tt_output_image($canvas, $mime, $q, $cacheFile)) {
    imagedestroy($canvas);
    imagedestroy($sourceImage);
    tt_fail('no se pudo guardar cache', 500);
}

@chmod($cacheFile, 0666);
imagedestroy($canvas);
imagedestroy($sourceImage);

tt_send_file($cacheFile, $mime);