<?php
namespace App\Core;

class ImageProcessor
{
    public static function process(string $tmp, int $eventId, string $originalName): array|string
    {
        foreach (['thumbnails','previews'] as $d) {
            $p = ROOT."/imgs/$d/$eventId";
            if (!\is_dir($p) && !\mkdir($p, 0755, true))
                return "Não foi possível criar imgs/$d/$eventId";
        }
        $origDir = ROOT."/storage/originals/$eventId";
        if (!\is_dir($origDir) && !\mkdir($origDir, 0755, true))
            return "Não foi possível criar storage/originals/$eventId";

        if (!\function_exists('imagecreatefromjpeg'))
            return "Extensão GD não instalada — ativa no cPanel";

        $info = @\getimagesize($tmp);
        if (!$info) return "Ficheiro inválido";
        if (!\in_array($info[2],[\IMAGETYPE_JPEG,\IMAGETYPE_PNG,\IMAGETYPE_WEBP]))
            return "Formato inválido (usa JPEG, PNG ou WebP)";

        [$origW, $origH] = [$info[0], $info[1]];

        // Detectar CMYK — GD não suporta, causa crash silencioso
        if ($info[2] === \IMAGETYPE_JPEG) {
            $channels = $info['channels'] ?? 3;
            if ($channels === 4) {
                return "Foto em modo CMYK — exporta em RGB no Lightroom/Photoshop (File → Export → sRGB)";
            }
        }

        // Verificar dimensões — fotos acima de 8000px causam 503 por falta de RAM
        $maxDim = 8000;
        if ($origW > $maxDim || $origH > $maxDim) {
            return "Foto demasiado grande ({$origW}×{$origH}px — máx {$maxDim}px). Reduz a resolução de exportação.";
        }

        // Estimar RAM necessária (4 bytes × pixels × 4 cópias simultâneas)
        $ramNeeded = $origW * $origH * 4 * 4;
        $ramLimit  = self::parseMemoryLimit(\ini_get('memory_limit'));
        $ramFree   = $ramLimit - \memory_get_usage(true);
        if ($ramLimit > 0 && $ramNeeded > $ramFree) {
            $needMB  = round($ramNeeded / 1024 / 1024);
            $freeMB  = round($ramFree   / 1024 / 1024);
            return "RAM insuficiente para processar ({$origW}×{$origH}px precisa ~{$needMB}MB, disponível ~{$freeMB}MB). Reduz a resolução.";
        }

        $src = match($info[2]) {
            \IMAGETYPE_JPEG => @\imagecreatefromjpeg($tmp),
            \IMAGETYPE_PNG  => @\imagecreatefrompng($tmp),
            \IMAGETYPE_WEBP => @\imagecreatefromwebp($tmp),
            default         => null,
        };
        if (!$src) return "Não foi possível abrir a imagem";

        // Corrigir orientação EXIF
        if ($info[2] === \IMAGETYPE_JPEG && \function_exists('exif_read_data')) {
            $exif = @\exif_read_data($tmp);
            $src  = self::fixOrientation($src, (int)($exif['Orientation'] ?? 1));
        }

        $gd      = \gd_info();
        $useWebp = !empty($gd['WebP Support']) && \function_exists('imagewebp');
        $ext     = $useWebp ? 'webp' : 'jpg';
        $q       = (int)config('app.webp_quality');

        // Preservar nome original — remover só caracteres perigosos para o filesystem
        $baseName = \pathinfo($originalName, PATHINFO_FILENAME);
        $baseName = \preg_replace('/[\/\\\:\*\?"<>\|\x00-\x1F]/', '', $baseName); // remove chars perigosos
        $baseName = \trim($baseName, '. ') ?: 'foto'; // sem pontos/espaços no início/fim
        $filename = "$baseName.$ext";
        $i = 1;
        while (\file_exists(ROOT."/imgs/thumbnails/$eventId/$filename"))
            $filename = "{$baseName} ({$i}).$ext"; // ex: "foto (2).webp"
        $i++;

        $save = static function(\GdImage $img, string $path, int $quality) use ($useWebp): bool {
            $w = \imagesx($img); $h = \imagesy($img);
            $out = \imagecreatetruecolor($w, $h);
            \imagefill($out, 0, 0, \imagecolorallocate($out, 255, 255, 255));
            \imagecopy($out, $img, 0, 0, 0, 0, $w, $h);
            $ok = $useWebp ? \imagewebp($out, $path, $quality) : \imagejpeg($out, $path, $quality);
            \imagedestroy($out);
            return $ok && \file_exists($path);
        };

        $orig = self::cloneImg($src);
        $save($orig, "$origDir/$filename", $q);
        \imagedestroy($orig);

        $thumb = self::letterbox($src, (int)config('app.thumb_width'));
        self::watermark($thumb);
        if (!$save($thumb, ROOT."/imgs/thumbnails/$eventId/$filename", 80))
            return "Sem permissão em imgs/thumbnails/$eventId/";
        \imagedestroy($thumb);

        $preview = self::resize($src, (int)config('app.preview_width'));
        self::watermark($preview);
        if (!$save($preview, ROOT."/imgs/previews/$eventId/$filename", $q))
            return "Falhou ao guardar preview";
        \imagedestroy($preview);
        \imagedestroy($src);
        \gc_collect_cycles();

        return [
            'filename'      => $filename,
            'original_path' => "originals/$eventId/$filename",
            'preview_path'  => "previews/$eventId/$filename",
            'thumb_path'    => "thumbnails/$eventId/$filename",
        ];
    }

    private static function parseMemoryLimit(string $limit): int
    {
        if ($limit === '-1') return 0;
        $val  = (int)$limit;
        $unit = \strtolower(\substr($limit, -1));
        return match($unit) {
            'g' => $val * 1024 * 1024 * 1024,
            'm' => $val * 1024 * 1024,
            'k' => $val * 1024,
            default => $val,
        };
    }

    private static function fixOrientation(\GdImage $img, int $o): \GdImage
    {
        $angle = match($o){ 3=>180, 6=>-90, 8=>90, default=>0 };
        if ($angle===0) return $img;
        $r = \imagerotate($img, $angle, 0);
        \imagedestroy($img);
        return $r ?: $img;
    }

    private static function letterbox(\GdImage $src, int $size): \GdImage
    {
        $w=\imagesx($src); $h=\imagesy($src);
        $out=\imagecreatetruecolor($size,$size);
        \imagefill($out,0,0,\imagecolorallocate($out,0,0,0));
        if($w>$h){$nw=$size;$nh=(int)($h*$size/$w);}
        else{$nh=$size;$nw=(int)($w*$size/$h);}
        \imagecopyresampled($out,$src,(int)(($size-$nw)/2),(int)(($size-$nh)/2),0,0,$nw,$nh,$w,$h);
        return $out;
    }

    private static function resize(\GdImage $src, int $maxW): \GdImage
    {
        $w=\imagesx($src); $h=\imagesy($src);
        if($w<=$maxW) return self::cloneImg($src);
        $nw=$maxW; $nh=(int)($h*$maxW/$w);
        $out=\imagecreatetruecolor($nw,$nh);
        \imagefill($out,0,0,\imagecolorallocate($out,255,255,255));
        \imagecopyresampled($out,$src,0,0,0,0,$nw,$nh,$w,$h);
        return $out;
    }

    private static function cloneImg(\GdImage $src): \GdImage
    {
        $w=\imagesx($src); $h=\imagesy($src);
        $out=\imagecreatetruecolor($w,$h);
        \imagefill($out,0,0,\imagecolorallocate($out,255,255,255));
        \imagecopy($out,$src,0,0,0,0,$w,$h);
        return $out;
    }

    private static function watermark(\GdImage $img): void
    {
        $w=\imagesx($img); $h=\imagesy($img);
        $text=config('app.watermark_text');
        $alpha=(int)(127*(1-config('app.watermark_opacity')/100));
        $font=5; $tw=\strlen($text)*\imagefontwidth($font); $charH=\imagefontheight($font);
        $scale=6; $tileW=$tw+10; $tileH=$charH+10;
        $tile=\imagecreatetruecolor($tileW,$tileH);
        \imagealphablending($tile,false); \imagesavealpha($tile,true);
        \imagefill($tile,0,0,\imagecolorallocatealpha($tile,0,0,0,127));
        \imagealphablending($tile,true);
        \imagestring($tile,$font,6,5,$text,\imagecolorallocatealpha($tile,0,0,0,\min(127,$alpha+25)));
        \imagestring($tile,$font,5,4,$text,\imagecolorallocatealpha($tile,255,255,255,$alpha));
        $sW=$tileW*$scale; $sH=$tileH*$scale;
        $scaled=\imagecreatetruecolor($sW,$sH);
        \imagealphablending($scaled,false); \imagesavealpha($scaled,true);
        \imagefill($scaled,0,0,\imagecolorallocatealpha($scaled,0,0,0,127));
        \imagecopyresized($scaled,$tile,0,0,0,0,$sW,$sH,$tileW,$tileH);
        \imagedestroy($tile);
        $rotated=\imagerotate($scaled,25,\imagecolorallocatealpha($scaled,0,0,0,127),1);
        \imagedestroy($scaled);
        if(!$rotated) return;
        $rW=\imagesx($rotated); $rH=\imagesy($rotated);
        \imagealphablending($img,true);
        $stepX=(int)($rW*1.1); $stepY=(int)($rH*1.2);
        for($row=0,$y=-$rH;$y<$h+$rH;$y+=$stepY,$row++){
            $ox=($row%2===0)?0:(int)($stepX/2);
            for($x=-$rW+$ox;$x<$w+$rW;$x+=$stepX)
                \imagecopy($img,$rotated,$x,$y,0,0,$rW,$rH);
        }
        \imagedestroy($rotated);
    }
}
