<?php

/**
 * Generates responsive WebP + JPEG derivatives for article featured images.
 * Also creates 1200x630 OG crop and 400x400 square thumb. Uses GD (always available).
 * AVIF is generated when imageavif() is available (PHP 8.1+ with libavif).
 */
class ArticleImagePipeline
{
    public const WIDTHS = [320, 640, 960, 1280, 1920];
    public const BASE_DIR = '/public/images/articles';

    /**
     * Process an uploaded image file into responsive set.
     * @return array{success: bool, basename?: string, rel_dir?: string, error?: string, og?: string}
     */
    public static function process(string $srcPath, string $slugHint): array
    {
        if (!file_exists($srcPath)) {
            return ['success' => false, 'error' => 'Source missing'];
        }
        $info = @getimagesize($srcPath);
        if ($info === false) {
            return ['success' => false, 'error' => 'Not an image'];
        }
        [$w, $h, $type] = $info;

        $source = self::loadImage($srcPath, $type);
        if (!$source) {
            return ['success' => false, 'error' => 'Unsupported image'];
        }

        // Strip EXIF for JPEG is implicit on re-encode.
        $year = date('Y');
        $month = date('m');
        $relDir = '/images/articles/' . $year . '/' . $month;
        $absDir = APP_ROOT . '/public' . $relDir;
        if (!is_dir($absDir)) {
            @mkdir($absDir, 0755, true);
        }

        $base = self::safeSlug($slugHint) . '-' . substr(bin2hex(random_bytes(3)), 0, 6);

        // Generate responsive widths (only smaller than original)
        foreach (self::WIDTHS as $target) {
            if ($target > $w) continue;
            $resized = self::resize($source, $w, $h, $target);
            if (!$resized) continue;
            $pathJpg  = $absDir . '/' . $base . '-' . $target . '.jpg';
            $pathWebp = $absDir . '/' . $base . '-' . $target . '.webp';
            @imagejpeg($resized, $pathJpg, 82);
            if (function_exists('imagewebp')) {
                @imagewebp($resized, $pathWebp, 78);
            }
            if (function_exists('imageavif')) {
                @imageavif($resized, $absDir . '/' . $base . '-' . $target . '.avif', 55);
            }
            imagedestroy($resized);
        }

        // Always generate a "full" at original size capped to 1920 as fallback.
        $largest = min($w, 1920);
        $full = self::resize($source, $w, $h, $largest);
        if ($full) {
            @imagejpeg($full, $absDir . '/' . $base . '.jpg', 85);
            if (function_exists('imagewebp')) {
                @imagewebp($full, $absDir . '/' . $base . '.webp', 80);
            }
            imagedestroy($full);
        }

        // OG 1200x630 (cover crop)
        $og = self::cover($source, $w, $h, 1200, 630);
        if ($og) {
            @imagejpeg($og, $absDir . '/' . $base . '-og.jpg', 82);
            imagedestroy($og);
        }

        // Square 400x400 thumb
        $sq = self::cover($source, $w, $h, 400, 400);
        if ($sq) {
            @imagejpeg($sq, $absDir . '/' . $base . '-sq.jpg', 80);
            imagedestroy($sq);
        }

        imagedestroy($source);

        return [
            'success' => true,
            'basename' => $base,
            'rel_dir' => $relDir,
            'og' => $relDir . '/' . $base . '-og.jpg',
        ];
    }

    public static function srcset(string $relDir, string $basename, string $ext = 'jpg'): string
    {
        $parts = [];
        foreach (self::WIDTHS as $w) {
            $file = APP_ROOT . '/public' . $relDir . '/' . $basename . '-' . $w . '.' . $ext;
            if (file_exists($file)) {
                $parts[] = APP_URL . $relDir . '/' . $basename . '-' . $w . '.' . $ext . ' ' . $w . 'w';
            }
        }
        return implode(', ', $parts);
    }

    public static function defaultSrc(string $relDir, string $basename): string
    {
        $file = APP_ROOT . '/public' . $relDir . '/' . $basename . '.jpg';
        if (file_exists($file)) {
            return APP_URL . $relDir . '/' . $basename . '.jpg';
        }
        // fall back to largest responsive
        for ($i = count(self::WIDTHS) - 1; $i >= 0; $i--) {
            $w = self::WIDTHS[$i];
            $file = APP_ROOT . '/public' . $relDir . '/' . $basename . '-' . $w . '.jpg';
            if (file_exists($file)) {
                return APP_URL . $relDir . '/' . $basename . '-' . $w . '.jpg';
            }
        }
        return '';
    }

    public static function ogUrl(string $relDir, string $basename): string
    {
        $file = APP_ROOT . '/public' . $relDir . '/' . $basename . '-og.jpg';
        if (file_exists($file)) {
            return APP_URL . $relDir . '/' . $basename . '-og.jpg';
        }
        return self::defaultSrc($relDir, $basename);
    }

    public static function deleteAll(string $relDir, string $basename): void
    {
        $dir = APP_ROOT . '/public' . $relDir;
        if (!is_dir($dir)) return;
        foreach (glob($dir . '/' . $basename . '*') as $f) {
            @unlink($f);
        }
    }

    private static function loadImage(string $path, int $type): \GdImage|false
    {
        return match ($type) {
            IMAGETYPE_JPEG => @imagecreatefromjpeg($path),
            IMAGETYPE_PNG  => @imagecreatefrompng($path),
            IMAGETYPE_WEBP => function_exists('imagecreatefromwebp') ? @imagecreatefromwebp($path) : false,
            IMAGETYPE_GIF  => @imagecreatefromgif($path),
            default => false,
        };
    }

    private static function resize(\GdImage $src, int $sw, int $sh, int $targetW): \GdImage|false
    {
        $ratio = $sh / $sw;
        $targetH = (int)round($targetW * $ratio);
        $dst = imagecreatetruecolor($targetW, $targetH);
        if (!$dst) return false;
        imagecopyresampled($dst, $src, 0, 0, 0, 0, $targetW, $targetH, $sw, $sh);
        return $dst;
    }

    private static function cover(\GdImage $src, int $sw, int $sh, int $tw, int $th): \GdImage|false
    {
        $dst = imagecreatetruecolor($tw, $th);
        if (!$dst) return false;
        // cover: scale so src fills dst, then center-crop
        $srcRatio = $sw / $sh;
        $dstRatio = $tw / $th;
        if ($srcRatio > $dstRatio) {
            $cropH = $sh;
            $cropW = (int)round($sh * $dstRatio);
            $cropX = (int)(($sw - $cropW) / 2);
            $cropY = 0;
        } else {
            $cropW = $sw;
            $cropH = (int)round($sw / $dstRatio);
            $cropX = 0;
            $cropY = (int)(($sh - $cropH) / 2);
        }
        imagecopyresampled($dst, $src, 0, 0, $cropX, $cropY, $tw, $th, $cropW, $cropH);
        return $dst;
    }

    private static function safeSlug(string $s): string
    {
        $s = preg_replace('/[^a-zA-Z0-9_-]+/', '-', $s);
        $s = trim($s ?? '', '-');
        return $s !== '' ? strtolower(substr($s, 0, 40)) : 'img';
    }
}
