<?php
// +----------------------------------------------------------------------
// | onezan-admin 快速开发的管理平台
// +----------------------------------------------------------------------
// | 官方网址：https://www.onezan.cn
// +----------------------------------------------------------------------
// | onezan团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: 543885307@qq.com
// +----------------------------------------------------------------------
namespace app\service\domain\media;

use think\facade\Log;


/**
 * Image Processor
 * @package app\service\domain\media
 */
class ImageProcessor
{
    /**
     * 默认缩略图尺寸
     * @var array
     */
    const DEFAULT_SIZES = [
        'small'  => [150, 150],
        'medium' => [400, 400],
        'large'  => [800, 800],
    ];


    /**
     * 生成图片缩略图
     * @param string $sourcePath
     * @param string $targetDir
     * @param string $baseName
     * @param array $sizes
     * @return array
     */
    public function generateThumbnails(string $sourcePath, string $targetDir, string $baseName, array $sizes = []): array
    {
        if (!$sizes) {
            $sizes = self::DEFAULT_SIZES;
        }

        $info = @getimagesize($sourcePath);
        if (!$info) {
            Log::warning('', ['path' => $sourcePath]);
            return [];
        }

        $srcW = (int)$info[0];
        $srcH = (int)$info[1];
        $srcType = $info[2] ?? 0;
        $srcImg = $this->createGdImage($sourcePath, $srcType);
        if (!$srcImg) {
            Log::warning('GD图片创建失败', ['path' => $sourcePath]);
            return [];
        }

        $result = [];
        $ext = 'webp';

        foreach ($sizes as $label => $dimensions) {
            $maxW = (int)$dimensions[0];
            $maxH = (int)$dimensions[1];

            if ($maxW <= 0 || $maxH <= 0) {
                continue;
            }


            if ($srcW <= $maxW && $srcH <= $maxH) {
                $thumbPath = $targetDir . '/' . $baseName . '_' . $label . '.' . $ext;
                $fullPath = $this->publicPath($thumbPath);
                $this->ensureDir(dirname($fullPath));

                $this->saveWebp($srcImg, $fullPath, 85);
                $result[$label] = ltrim(str_replace('\\', '/', $thumbPath), '/');
                continue;
            }


            $ratio = min($maxW / $srcW, $maxH / $srcH);
            $newW = (int)round($srcW * $ratio);
            $newH = (int)round($srcH * $ratio);

            if ($newW < 1) $newW = 1;
            if ($newH < 1) $newH = 1;

            $thumb = imagecreatetruecolor($newW, $newH);
            if (!$thumb) {
                Log::warning('创建缩略图画布失败', ['label' => $label]);
                continue;
            }


            $this->preserveAlpha($thumb);

            imagecopyresampled($thumb, $srcImg, 0, 0, 0, 0, $newW, $newH, $srcW, $srcH);

            $thumbPath = $targetDir . '/' . $baseName . '_' . $label . '.' . $ext;
            $fullPath = $this->publicPath($thumbPath);
            $this->ensureDir(dirname($fullPath));
            $this->saveWebp($thumb, $fullPath, 82);

            imagedestroy($thumb);

            $result[$label] = ltrim(str_replace('\\', '/', $thumbPath), '/');
        }

        imagedestroy($srcImg);

        return $result;
    }


    /**
     * 压缩图片（如果需要）
     * @param string $sourcePath
     * @param int $thresholdKB
     * @param int $quality
     * @return array
     */
    public function compressIfNeeded(string $sourcePath, int $thresholdKB = 500, int $quality = 80): array
    {
        $fileSize = is_file($sourcePath) ? (int)filesize($sourcePath) : 0;
        $thresholdBytes = $thresholdKB * 1024;

        if ($thresholdBytes <= 0 || $fileSize <= $thresholdBytes) {
            return [
                'path' => $sourcePath,
                'size' => $fileSize,
                'compressed' => false,
            ];
        }


        $info = @getimagesize($sourcePath);
        if (!$info) {
            return ['path' => $sourcePath, 'size' => $fileSize, 'compressed' => false];
        }

        $srcType = $info[2] ?? 0;
        $srcImg = $this->createGdImage($sourcePath, $srcType);
        if (!$srcImg) {
            return ['path' => $sourcePath, 'size' => $fileSize, 'compressed' => false];
        }


        $ext = strtolower(pathinfo($sourcePath, PATHINFO_EXTENSION));
        if ($ext === 'webp') {
            imagedestroy($srcImg);
            return ['path' => $sourcePath, 'size' => $fileSize, 'compressed' => false];
        }


        $newPath = preg_replace('/\.[a-zA-Z]+$/', '.webp', $sourcePath);
        if ($newPath === $sourcePath) {
            $newPath = $sourcePath . '.webp';
        }

        $this->saveWebp($srcImg, $newPath, $quality);
        imagedestroy($srcImg);


        if ($newPath !== $sourcePath) {
            @unlink($sourcePath);
        }

        $newSize = is_file($newPath) ? (int)filesize($newPath) : 0;

        return [
            'path' => $newPath,
            'size' => $newSize,
            'compressed' => true,
        ];
    }


    /**
     * 获取缩略图尺寸
     * @return array
     */
    public function getThumbnailSizes(): array
    {
        try {
            $raw = \app\service\ConfigService::getString('image_thumb_sizes', '');
            if ($raw === '') {
                return self::DEFAULT_SIZES;
            }
            $decoded = json_decode($raw, true);
            if (!is_array($decoded) || !$decoded) {
                return self::DEFAULT_SIZES;
            }
            $result = [];
            foreach ($decoded as $label => $dim) {
                if (!is_array($dim) || count($dim) < 2) continue;
                $w = (int)$dim[0];
                $h = (int)$dim[1];
                if ($w <= 0 || $h <= 0) continue;
                $result[(string)$label] = [$w, $h];
            }
            return $result ?: self::DEFAULT_SIZES;
        } catch (\Throwable $e) {
            return self::DEFAULT_SIZES;
        }
    }

    /**
     * 获取图片压缩阈值（KB）
     * @return int
     */
    public function getCompressThresholdKB(): int
    {
        return \app\service\ConfigService::getImageCompressThresholdKB();
    }

    // ========== private helpers ==========

    /**
     * 创建GD图片资源
     * @param string $path
     * @param int $type
     * @return resource|bool
     */
    private function createGdImage(string $path, int $type)
    {
        $result = false;
        switch ($type) {
            case IMAGETYPE_JPEG:
                $result = @imagecreatefromjpeg($path);
                break;
            case IMAGETYPE_PNG:
                $result = @imagecreatefrompng($path);
                break;
            case IMAGETYPE_WEBP:
                if (function_exists('imagecreatefromwebp')) {
                    $result = @imagecreatefromwebp($path);
                }
                break;
            case IMAGETYPE_GIF:
                $result = @imagecreatefromgif($path);
                break;
        }
        if (!$result) {
            Log::warning('GD图片创建失败', ['path' => $path, 'type' => $type]);
        }
        return $result;
    }

    /**
     * 保持GD图片透明通道
     * @param resource $img
     * @return void
     */
    private function preserveAlpha(\GdImage $img): void
    {
        imagealphablending($img, false);
        imagesavealpha($img, true);
    }

    /**
     * 保存WebP图片
     * @param resource $img
     * @param string $path
     * @param int $quality
     * @return void
     */
    private function saveWebp($img, string $path, int $quality): void
    {
        $this->ensureDir(dirname($path));
        imagewebp($img, $path, $quality);
    }

    /**
     * 确保目录存在
     * @param string $dir
     * @return void
     */
    private function ensureDir(string $dir): void
    {
        if (!is_dir($dir)) {
            @mkdir($dir, 0755, true);
        }
    }

    /**
     * 获取公共路径
     * @param string $relativePath
     * @return string
     */
    private function publicPath(string $relativePath): string
    {
        return app()->getRootPath() . 'public' . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . ltrim($relativePath, '/\\');
    }
}