<?php

namespace App\Support;

use Illuminate\Support\Facades\Storage;

class ImageHelper
{
    /**
     * Create a JPEG thumbnail for a file stored on a given disk.
     * Returns the relative path (on the same disk) or null on failure.
     */
    public static function makeThumbnailOnDisk(string $disk, string $sourcePath, string $destPath, int $maxEdge = 600, int $quality = 82): ?string
    {
        try {
            $fullSrc = Storage::disk($disk)->path($sourcePath);
            if (! file_exists($fullSrc)) {
                return null;
            }

            $data = @file_get_contents($fullSrc);
            if ($data === false) {
                return null;
            }

            // Prefer robust decode via GD from string (handles jpeg/png/webp if compiled)
            $src = @imagecreatefromstring($data);
            if (! $src) {
                return null;
            }

            $w = imagesx($src);
            $h = imagesy($src);
            if ($w === 0 || $h === 0) {
                imagedestroy($src);

                return null;
            }

            $scale = min(1.0, $maxEdge / max($w, $h));
            $tw = (int) max(1, round($w * $scale));
            $th = (int) max(1, round($h * $scale));

            $dst = imagecreatetruecolor($tw, $th);
            imagecopyresampled($dst, $src, 0, 0, 0, 0, $tw, $th, $w, $h);

            // Ensure destination directory exists
            $destDir = dirname($destPath);
            Storage::disk($disk)->makeDirectory($destDir);
            $fullDest = Storage::disk($disk)->path($destPath);

            // Encode JPEG
            @imagejpeg($dst, $fullDest, $quality);

            imagedestroy($dst);
            imagedestroy($src);

            // Confirm file written
            if (! file_exists($fullDest)) {
                return null;
            }

            return $destPath;
        } catch (\Throwable $e) {
            // Silent failure; caller can fall back to original
            return null;
        }
    }

    /**
     * Apply a watermark in-place on the given disk/path.
     * Expects $config with keys: asset (path), position, opacity (0..1), scale (0..1), padding (px).
     */
    public static function applyWatermarkOnDisk(string $disk, string $path, array $config): bool
    {
        $fullSrc = Storage::disk($disk)->path($path);
        if (! file_exists($fullSrc)) {
            return false;
        }

        $assetPath = $config['asset'] ?? null;
        if (! $assetPath) {
            return false;
        }

        $assetFull = null;
        if (Storage::disk('public')->exists($assetPath)) {
            $assetFull = Storage::disk('public')->path($assetPath);
        } elseif (file_exists($assetPath)) {
            $assetFull = $assetPath;
        }

        if (! $assetFull || ! file_exists($assetFull)) {
            return false;
        }

        try {
            $data = @file_get_contents($fullSrc);
            $src = $data !== false ? @imagecreatefromstring($data) : null;
            if (! $src) {
                return false;
            }

            $wmData = @file_get_contents($assetFull);
            $watermark = $wmData !== false ? @imagecreatefromstring($wmData) : null;
            if (! $watermark) {
                imagedestroy($src);

                return false;
            }

            imagesavealpha($src, true);
            imagesavealpha($watermark, true);

            $srcW = imagesx($src);
            $srcH = imagesy($src);
            $wmW = imagesx($watermark);
            $wmH = imagesy($watermark);

            if ($srcW <= 0 || $srcH <= 0 || $wmW <= 0 || $wmH <= 0) {
                imagedestroy($src);
                imagedestroy($watermark);

                return false;
            }

            $scale = max(0.05, min(1.0, (float) ($config['scale'] ?? 0.2)));
            $targetW = max(1, (int) round($srcW * $scale));
            $targetH = max(1, (int) round($wmH * ($targetW / $wmW)));

            $resized = imagecreatetruecolor($targetW, $targetH);
            imagealphablending($resized, false);
            imagesavealpha($resized, true);
            imagecopyresampled($resized, $watermark, 0, 0, 0, 0, $targetW, $targetH, $wmW, $wmH);
            imagedestroy($watermark);

            $padding = max(0, (int) ($config['padding'] ?? 0));
            $position = $config['position'] ?? 'bottom-right';
            $x = $padding;
            $y = $padding;

            switch ($position) {
                case 'top-right':
                    $x = max(0, $srcW - $targetW - $padding);
                    break;
                case 'top-center':
                    $x = (int) max(0, ($srcW - $targetW) / 2);
                    break;
                case 'middle-left':
                    $y = (int) max(0, ($srcH - $targetH) / 2);
                    break;
                case 'center':
                    $x = (int) max(0, ($srcW - $targetW) / 2);
                    $y = (int) max(0, ($srcH - $targetH) / 2);
                    break;
                case 'middle-right':
                    $x = max(0, $srcW - $targetW - $padding);
                    $y = (int) max(0, ($srcH - $targetH) / 2);
                    break;
                case 'bottom-left':
                    $y = max(0, $srcH - $targetH - $padding);
                    break;
                case 'bottom-center':
                    $x = (int) max(0, ($srcW - $targetW) / 2);
                    $y = max(0, $srcH - $targetH - $padding);
                    break;
                case 'bottom-right':
                    $x = max(0, $srcW - $targetW - $padding);
                    $y = max(0, $srcH - $targetH - $padding);
                    break;
                default:
                    break;
            }

            $offsetX = (int) ($config['offset_x'] ?? 0);
            $offsetY = (int) ($config['offset_y'] ?? 0);
            $x = max(0, min($srcW - $targetW, $x + $offsetX));
            $y = max(0, min($srcH - $targetH, $y + $offsetY));

            $opacity = max(0.0, min(1.0, (float) ($config['opacity'] ?? 0.25)));

            if ($opacity < 1.0) {
                self::applyOpacity($resized, $opacity);
            }

            imagealphablending($src, true);
            imagecopy($src, $resized, $x, $y, 0, 0, $targetW, $targetH);
            imagedestroy($resized);

            // Overwrite original (respect mime: always JPEG for compatibility)
            @imagejpeg($src, $fullSrc, 90);
            imagedestroy($src);

            return true;
        } catch (\Throwable $e) {
            return false;
        }
    }

    /**
     * Copy a source to destination and apply watermark there.
     */
    public static function copyWithWatermark(string $disk, string $sourcePath, string $destPath, array $config): ?string
    {
        if (! Storage::disk($disk)->exists($sourcePath)) {
            return null;
        }

        Storage::disk($disk)->makeDirectory(dirname($destPath));
        Storage::disk($disk)->copy($sourcePath, $destPath);

        $applied = self::applyWatermarkOnDisk($disk, $destPath, $config);

        return $applied ? $destPath : null;
    }

    /**
     * @param  \GdImage|resource  $image
     */
    private static function applyOpacity($image, float $opacity): void
    {
        $width = imagesx($image);
        $height = imagesy($image);

        if ($width <= 0 || $height <= 0) {
            return;
        }

        imagealphablending($image, false);
        imagesavealpha($image, true);

        for ($x = 0; $x < $width; $x++) {
            for ($y = 0; $y < $height; $y++) {
                $rgba = imagecolorat($image, $x, $y);
                $alpha = ($rgba >> 24) & 0x7F;
                $red = ($rgba >> 16) & 0xFF;
                $green = ($rgba >> 8) & 0xFF;
                $blue = $rgba & 0xFF;

                $adjustedAlpha = (int) round(127 - (127 - $alpha) * $opacity);
                $color = imagecolorallocatealpha($image, $red, $green, $blue, max(0, min(127, $adjustedAlpha)));
                imagesetpixel($image, $x, $y, $color);
            }
        }
    }
}
