<?php
declare(strict_types=1);

namespace FlysystemOffload\Media;

use FlysystemOffload\Helpers\PathHelper;
use League\Flysystem\FilesystemOperator;
use WP_Error;

if (! defined('ABSPATH')) {
    exit;
}

if (! class_exists(\WP_Image_Editor::class)) {
    require_once ABSPATH . WPINC . '/class-wp-image-editor.php';
}

if (! class_exists(\WP_Image_Editor_Imagick::class) && file_exists(ABSPATH . WPINC . '/class-wp-image-editor-imagick.php')) {
    require_once ABSPATH . WPINC . '/class-wp-image-editor-imagick.php';
}

if (! class_exists(\WP_Image_Editor_Imagick::class)) {
    // Si la clase base no está disponible (p. ej. Imagick deshabilitado),
    // salimos silenciosamente para permitir que WordPress siga usando GD.
    return;
}

class ImageEditorImagick extends \WP_Image_Editor_Imagick
{
    protected static ?FilesystemOperator $filesystem = null;
    protected ?string $remotePath = null;
    protected ?string $localPath = null;

    public static function bootWithFilesystem(?FilesystemOperator $filesystem): void
    {
        self::$filesystem = $filesystem;
    }

    public function load()
    {
        if ($this->isFlyPath($this->file) && self::$filesystem) {
            $this->remotePath = PathHelper::stripProtocol($this->file);
            $temp = $this->downloadToTemp($this->remotePath);

            if (is_wp_error($temp)) {
                return $temp;
            }

            $this->localPath = $temp;
            $this->file      = $temp;
        }

        return parent::load();
    }

    public function save($filename = null, $mime_type = null)
    {
        $result = parent::save($filename, $mime_type);

        if (is_wp_error($result) || ! $this->remotePath || ! self::$filesystem || empty($result['path'])) {
            return $result;
        }

        $remote = $this->determineRemotePath($result['path']);
        $sync   = $this->pushToRemote($result['path'], $remote);

        if (is_wp_error($sync)) {
            return $sync;
        }

        $result['path'] = 'fly://' . $remote;
        if (isset($result['file'])) {
            $result['file'] = basename($remote);
        }

        return $result;
    }

    public function multi_resize($sizes)
    {
        $results = parent::multi_resize($sizes);

        if (! $this->remotePath || ! self::$filesystem) {
            return $results;
        }

        foreach ($results as &$result) {
            if (empty($result['path'])) {
                continue;
            }

            $remote = $this->determineRemotePath($result['path']);
            $sync   = $this->pushToRemote($result['path'], $remote);

            if (is_wp_error($sync)) {
                $result['error'] = $sync->get_error_message();
                continue;
            }

            $result['path'] = 'fly://' . $remote;
            if (isset($result['file'])) {
                $result['file'] = basename($remote);
            }
        }
        unset($result);

        return $results;
    }

    public function stream($mime_type = null)
    {
        if ($this->remotePath && $this->localPath) {
            $this->file = $this->localPath;
        }

        return parent::stream($mime_type);
    }

    public function __destruct()
    {
        if ($this->localPath && file_exists($this->localPath)) {
            @unlink($this->localPath);
        }

        parent::__destruct();
    }

    protected function pushToRemote(string $localFile, string $remotePath)
    {
        $stream = @fopen($localFile, 'rb');

        if (! $stream) {
            return new WP_Error(
                'flysystem_offload_upload_fail',
                __('No se pudo leer el archivo procesado para subirlo al almacenamiento remoto.', 'flysystem-offload')
            );
        }

        try {
            self::$filesystem->writeStream($remotePath, $stream);
        } catch (\Throwable $e) {
            if (is_resource($stream)) {
                fclose($stream);
            }

            return new WP_Error(
                'flysystem_offload_upload_fail',
                sprintf(
                    __('Fallo al subir %s al almacenamiento remoto: %s', 'flysystem-offload'),
                    $remotePath,
                    $e->getMessage()
                )
            );
        }

        if (is_resource($stream)) {
            fclose($stream);
        }

        @unlink($localFile);

        return true;
    }

    protected function downloadToTemp(string $remotePath)
    {
        if (! function_exists('wp_tempnam')) {
            require_once ABSPATH . 'wp-admin/includes/file.php';
        }

        $temp = wp_tempnam(basename($remotePath));

        if (! $temp) {
            return new WP_Error(
                'flysystem_offload_temp_fail',
                __('No se pudo crear un archivo temporal para procesar la imagen.', 'flysystem-offload')
            );
        }

        try {
            $source = self::$filesystem->readStream($remotePath);
            if (! is_resource($source)) {
                throw new \RuntimeException('No se pudo abrir el stream remoto.');
            }

            $target = fopen($temp, 'wb');
            if (! $target) {
                throw new \RuntimeException('No se pudo abrir el archivo temporal.');
            }

            stream_copy_to_stream($source, $target);

            fclose($source);
            fclose($target);
        } catch (\Throwable $e) {
            if (isset($source) && is_resource($source)) {
                fclose($source);
            }
            if (isset($target) && is_resource($target)) {
                fclose($target);
            }
            @unlink($temp);

            return new WP_Error(
                'flysystem_offload_download_fail',
                sprintf(
                    __('Fallo al descargar "%s" del almacenamiento remoto: %s', 'flysystem-offload'),
                    $remotePath,
                    $e->getMessage()
                )
            );
        }

        return $temp;
    }

    protected function determineRemotePath(string $localSavedPath): string
    {
        $remoteDir = $this->remotePath ? trim(dirname($this->remotePath), './') : '';
        $basename  = basename($localSavedPath);

        return ltrim(($remoteDir ? $remoteDir . '/' : '') . $basename, '/');
    }

    protected function isFlyPath(string $path): bool
    {
        return strncmp($path, 'fly://', 6) === 0;
    }
}
