<?php
declare(strict_types=1);

namespace Help4BuilderSuite\Modules\ImageOptimization;

use Help4BuilderSuite\Contracts\ModuleContract;
use Help4BuilderSuite\Support\AdminRequest;
use Help4BuilderSuite\Support\Settings;
use WP_Post;
use WP_REST_Request;
use WP_REST_Response;

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

final class Module implements ModuleContract
{
    public function key(): string
    {
        return 'image_optimization';
    }

    public function boot(): void
    {
        add_filter('wp_generate_attachment_metadata', [$this, 'optimizeAttachment'], 30, 2);
        add_filter('media_row_actions', [$this, 'addEnhanceRowAction'], 15, 2);
        add_action('admin_action_h4bs_enhance_image', [$this, 'handleEnhanceImageAction']);
        add_action('admin_notices', [$this, 'renderNotice']);
        add_action('rest_api_init', [$this, 'registerRestRoutes']);
        add_action('admin_menu', [$this, 'registerAdminPage']);
        add_action('admin_enqueue_scripts', [$this, 'enqueueAdminAssets']);
    }

    public function registerAdminPage(): void
    {
        add_submenu_page(
            'upload.php',
            __('Image Studio', 'help4-builder-suite'),
            __('Image Studio', 'help4-builder-suite'),
            'upload_files',
            'h4bs-image-studio',
            [$this, 'renderAdminPage']
        );
    }

    public function enqueueAdminAssets(string $hook): void
    {
        if ($hook !== 'media_page_h4bs-image-studio') {
            return;
        }

        wp_enqueue_media();
        wp_enqueue_style(
            'h4bs-image-studio',
            H4BS_URL . 'assets/css/image-studio.css',
            [],
            H4BS_VERSION
        );
        wp_enqueue_script(
            'h4bs-image-studio',
            H4BS_URL . 'assets/js/image-studio.js',
            [],
            H4BS_VERSION,
            true
        );

        $initialId = AdminRequest::queryAbsInt('attachment_id');
        $initialUrl = $initialId > 0 ? wp_get_attachment_image_url($initialId, 'full') : '';
        $initialTitle = $initialId > 0 ? (string) get_the_title($initialId) : '';
        $initialAlt = $initialId > 0 ? (string) get_post_meta($initialId, '_wp_attachment_image_alt', true) : '';
        $initialMime = $initialId > 0 ? (string) get_post_mime_type($initialId) : '';

        wp_localize_script(
            'h4bs-image-studio',
            'h4bsImageStudio',
            [
                'nonce'      => wp_create_nonce('wp_rest'),
                'restRoot'   => esc_url_raw(rest_url('h4bs/v1/images/')),
                'initial'    => [
                    'id'    => $initialId,
                    'url'   => is_string($initialUrl) ? $initialUrl : '',
                    'title' => $initialTitle,
                    'alt'   => $initialAlt,
                    'mime'  => $initialMime,
                ],
                'messages'   => [
                    'pickImage' => __('Select an image first.', 'help4-builder-suite'),
                    'saved'     => __('Image saved successfully.', 'help4-builder-suite'),
                    'saveError' => __('Could not save this image.', 'help4-builder-suite'),
                ],
                'maxBytes'   => 20 * 1024 * 1024,
                'maxPixels'  => 32 * 1024 * 1024,
                'historyEntries' => 10,
                'historyBytes' => 32 * 1024 * 1024,
            ]
        );
    }

    public function renderAdminPage(): void
    {
        if (! current_user_can('upload_files')) {
            return;
        }
        ?>
        <div class="wrap h4bs-admin-wrap">
            <h1><?php esc_html_e('Help4 Image Studio', 'help4-builder-suite'); ?></h1>
            <p>
                <?php esc_html_e('Select an image first. Work on a copy whenever possible so the approved original remains available for recovery.', 'help4-builder-suite'); ?>
            </p>
            <div class="h4bs-image-studio" data-h4bs-image-studio data-ready="false" data-dirty="false">
                <section class="h4bs-image-studio-start" data-h4bs-start>
                    <h2><?php esc_html_e('Choose the original image', 'help4-builder-suite'); ?></h2>
                    <p><?php esc_html_e('The original stays unchanged. Image Studio saves your work as a new Media Library image.', 'help4-builder-suite'); ?></p>
                    <button type="button" class="button button-primary" data-h4bs-pick><?php esc_html_e('Choose image', 'help4-builder-suite'); ?></button>
                </section>
                <div class="h4bs-image-studio-workspace" data-h4bs-workspace hidden>
                <div class="h4bs-image-studio-summary">
                    <div><strong><?php esc_html_e('Original', 'help4-builder-suite'); ?></strong><span data-h4bs-original-title><?php esc_html_e('No image selected', 'help4-builder-suite'); ?></span></div>
                    <div><strong><?php esc_html_e('Working copy', 'help4-builder-suite'); ?></strong><span data-h4bs-copy-state><?php esc_html_e('No edits yet', 'help4-builder-suite'); ?></span></div>
                    <button type="button" class="button" data-h4bs-replace><?php esc_html_e('Choose a different image', 'help4-builder-suite'); ?></button>
                </div>
                <div class="h4bs-image-studio-toolbar" role="toolbar" aria-label="<?php esc_attr_e('Image editing actions', 'help4-builder-suite'); ?>">
                    <button type="button" class="button" data-h4bs-tool="draw" disabled><?php esc_html_e('Draw Tool', 'help4-builder-suite'); ?></button>
                    <button type="button" class="button" data-h4bs-tool="crop" disabled><?php esc_html_e('Crop Tool', 'help4-builder-suite'); ?></button>
                    <button type="button" class="button" data-h4bs-crop-apply disabled><?php esc_html_e('Apply Crop', 'help4-builder-suite'); ?></button>
                    <button type="button" class="button" data-h4bs-overlay-apply disabled><?php esc_html_e('Apply Overlay', 'help4-builder-suite'); ?></button>
                    <button type="button" class="button" data-h4bs-undo disabled><?php esc_html_e('Undo', 'help4-builder-suite'); ?></button>
                    <button type="button" class="button" data-h4bs-redo disabled><?php esc_html_e('Redo', 'help4-builder-suite'); ?></button>
                    <button type="button" class="button" data-h4bs-reset disabled><?php esc_html_e('Reset to original', 'help4-builder-suite'); ?></button>
                </div>
                <details class="h4bs-image-studio-controls-disclosure">
                    <summary><?php esc_html_e('Drawing and overlay settings', 'help4-builder-suite'); ?></summary>
                <div class="h4bs-image-studio-controls">
                    <label>
                        <?php esc_html_e('Draw Color', 'help4-builder-suite'); ?>
                        <input type="color" value="#ff4d4d" data-h4bs-draw-color>
                    </label>
                    <label>
                        <?php esc_html_e('Brush Size', 'help4-builder-suite'); ?>
                        <input type="range" min="1" max="60" step="1" value="8" data-h4bs-brush-size>
                    </label>
                    <label>
                        <?php esc_html_e('Overlay Color', 'help4-builder-suite'); ?>
                        <input type="color" value="#000000" data-h4bs-overlay-color>
                    </label>
                    <label>
                        <?php esc_html_e('Overlay Opacity', 'help4-builder-suite'); ?>
                        <input type="range" min="0" max="100" step="1" value="25" data-h4bs-overlay-opacity>
                    </label>
                </div>
                </details>
                <p class="h4bs-image-studio-status" data-h4bs-status role="status" aria-live="polite"><?php esc_html_e('Choose an image to begin.', 'help4-builder-suite'); ?></p>
                <div class="h4bs-image-studio-canvas-wrap">
                    <canvas data-h4bs-canvas></canvas>
                </div>
                <section class="h4bs-image-studio-save-panel">
                    <h2><?php esc_html_e('Save a new Media Library image', 'help4-builder-suite'); ?></h2>
                    <fieldset>
                        <legend><?php esc_html_e('How should screen readers treat this image?', 'help4-builder-suite'); ?></legend>
                        <label><input type="radio" name="h4bs_image_alt_mode" value="meaningful" checked data-h4bs-alt-mode> <?php esc_html_e('The image adds information', 'help4-builder-suite'); ?></label>
                        <label><input type="radio" name="h4bs_image_alt_mode" value="decorative" data-h4bs-alt-mode> <?php esc_html_e('The image is decorative', 'help4-builder-suite'); ?></label>
                    </fieldset>
                    <label data-h4bs-alt-wrap><?php esc_html_e('Image description', 'help4-builder-suite'); ?><input type="text" maxlength="250" data-h4bs-alt><span class="description"><?php esc_html_e('Describe what matters in the image, without repeating nearby text.', 'help4-builder-suite'); ?></span></label>
                    <div class="h4bs-image-studio-save-actions">
                        <button type="button" class="button button-primary" data-h4bs-save disabled><?php esc_html_e('Save as new image', 'help4-builder-suite'); ?></button>
                        <a href="" data-h4bs-saved-link hidden><?php esc_html_e('View new image in Media Library', 'help4-builder-suite'); ?></a>
                    </div>
                </section>
                </div>
            </div>
        </div>
        <?php
    }

    public function registerRestRoutes(): void
    {
        register_rest_route(
            'h4bs/v1',
            '/images/(?P<id>\d+)/enhance',
            [
                'methods'             => 'POST',
                'callback'            => [$this, 'enhanceImageRoute'],
                'permission_callback' => static function (WP_REST_Request $request): bool {
                    $id = (int) $request->get_param('id');
                    return $id > 0 && current_user_can('edit_post', $id);
                },
            ]
        );

        register_rest_route(
            'h4bs/v1',
            '/images/(?P<id>\d+)/editor-save',
            [
                'methods'             => 'POST',
                'callback'            => [$this, 'saveEditedImageRoute'],
                'permission_callback' => static function (WP_REST_Request $request): bool {
                    $id = (int) $request->get_param('id');
                    return $id > 0 && current_user_can('edit_post', $id);
                },
            ]
        );
    }

    public function enhanceImageRoute(WP_REST_Request $request): WP_REST_Response
    {
        $attachmentId = (int) $request->get_param('id');
        if ($attachmentId <= 0) {
            return new WP_REST_Response(['message' => 'Invalid image id'], 422);
        }

        $ok = $this->enhanceAttachment($attachmentId, true);
        if (! $ok) {
            return new WP_REST_Response(['message' => 'Enhancement failed or Imagick unavailable'], 500);
        }

        return new WP_REST_Response(['status' => 'ok', 'attachment_id' => $attachmentId]);
    }

    public function saveEditedImageRoute(WP_REST_Request $request): WP_REST_Response
    {
        $attachmentId = (int) $request->get_param('id');
        $data = (string) $request->get_param('image_data');
        $alt = sanitize_text_field((string) $request->get_param('alt_text'));
        $decorative = rest_sanitize_boolean($request->get_param('decorative'));
        $requestId = sanitize_text_field((string) $request->get_header('X-H4BS-Request-ID'));
        if ($attachmentId <= 0 || $data === '' || ! preg_match('/^[a-f0-9-]{36}$/', $requestId)) {
            return new WP_REST_Response(['message' => 'Missing required fields'], 422);
        }
        if (! $decorative && $alt === '') {
            return new WP_REST_Response(['message' => __('Add an image description or mark the image decorative.', 'help4-builder-suite')], 422);
        }
        $payloadHash = hash('sha256', $attachmentId . '|' . hash('sha256', $data) . '|' . $alt . '|' . ($decorative ? '1' : '0'));
        $userId = get_current_user_id();
        $ledger = get_user_meta($userId, '_h4bs_image_save_replay', true);
        $ledger = is_array($ledger) ? $ledger : [];
        if (isset($ledger[$requestId]) && is_array($ledger[$requestId])) {
            if (! hash_equals((string) ($ledger[$requestId]['hash'] ?? ''), $payloadHash)) {
                return new WP_REST_Response(['message' => __('This save request was already used for different changes. Try saving again.', 'help4-builder-suite')], 409);
            }
            return new WP_REST_Response((array) ($ledger[$requestId]['response'] ?? []), 200);
        }

        $saved = $this->saveEditedImageCopy($attachmentId, $data, $decorative ? '' : $alt);
        if (! is_array($saved)) {
            return new WP_REST_Response(['message' => 'Could not save edited image'], 500);
        }
        $response = ['status' => 'ok'] + $saved;
        $ledger[$requestId] = ['hash' => $payloadHash, 'response' => $response, 'time' => time()];
        if (count($ledger) > 10) {
            uasort($ledger, static fn(array $a, array $b): int => ((int) ($a['time'] ?? 0)) <=> ((int) ($b['time'] ?? 0)));
            $ledger = array_slice($ledger, -10, null, true);
        }
        update_user_meta($userId, '_h4bs_image_save_replay', $ledger);
        return new WP_REST_Response($response, 200);
    }

    /**
     * @param array<string, mixed> $actions
     * @return array<string, mixed>
     */
    public function addEnhanceRowAction(array $actions, WP_Post $post): array
    {
        if ($post->post_type !== 'attachment') {
            return $actions;
        }
        if (! current_user_can('edit_post', $post->ID)) {
            return $actions;
        }

        $studioUrl = admin_url('upload.php?page=h4bs-image-studio&attachment_id=' . (int) $post->ID);
        $actions['h4bs_open_studio'] = '<a href="' . esc_url($studioUrl) . '">' . esc_html__('Open in Image Studio', 'help4-builder-suite') . '</a>';

        if (! extension_loaded('imagick')) {
            return $actions;
        }

        $url = wp_nonce_url(
            admin_url('admin.php?action=h4bs_enhance_image&attachment_id=' . (int) $post->ID),
            'h4bs_enhance_image_' . (int) $post->ID
        );
        $actions['h4bs_enhance_image'] = '<a href="' . esc_url($url) . '">' . esc_html__('Enhance (Help4)', 'help4-builder-suite') . '</a>';

        return $actions;
    }

    public function handleEnhanceImageAction(): void
    {
        $attachmentId = AdminRequest::queryAbsInt('attachment_id');
        if ($attachmentId <= 0) {
            wp_die('Missing attachment id.');
        }

        check_admin_referer('h4bs_enhance_image_' . $attachmentId);

        if (! current_user_can('edit_post', $attachmentId)) {
            wp_die('Not allowed.');
        }

        $ok = $this->enhanceAttachment($attachmentId, true);
        $message = $ok
            ? __('Image enhanced with Help4 Imagick pipeline.', 'help4-builder-suite')
            : __('Image enhancement skipped (Imagick unavailable or file missing).', 'help4-builder-suite');
        set_transient('h4bs_image_opt_notice', $message, 30);

        $referer = wp_get_referer();
        if (! is_string($referer) || $referer === '') {
            $referer = admin_url('upload.php');
        }
        wp_safe_redirect($referer);
        exit;
    }

    public function renderNotice(): void
    {
        if (! current_user_can('upload_files')) {
            return;
        }

        $notice = get_transient('h4bs_image_opt_notice');
        if (! is_string($notice) || $notice === '') {
            return;
        }

        delete_transient('h4bs_image_opt_notice');
        printf('<div class="notice notice-info is-dismissible"><p>%s</p></div>', esc_html($notice));
    }

    /**
     * @param array<string, mixed> $metadata
     * @return array<string, mixed>
     */
    public function optimizeAttachment(array $metadata, int $attachmentId): array
    {
        if (! Settings::bool('image_opt_enable', true)) {
            return $metadata;
        }

        $enhance = Settings::bool('image_opt_enhance_uploaded', false);
        $this->enhanceAttachment($attachmentId, $enhance);

        return $metadata;
    }

    /** @return array<string, mixed>|false */
    private function saveEditedImageCopy(int $attachmentId, string $dataUri, string $altText)
    {
        $sourcePath = get_attached_file($attachmentId);
        if (! is_string($sourcePath) || $sourcePath === '' || ! is_file($sourcePath)) {
            return false;
        }

        $matches = [];
        if (! preg_match('/^data:image\/(png|jpeg|webp);base64,(.+)$/', $dataUri, $matches)) {
            return false;
        }

        $blob = base64_decode((string) $matches[2], true);
        if (! is_string($blob) || $blob === '') {
            return false;
        }

        if (strlen($blob) > 20 * 1024 * 1024) {
            return false;
        }
        $dimensions = @getimagesizefromstring($blob);
        $width = is_array($dimensions) ? (int) ($dimensions[0] ?? 0) : 0;
        $height = is_array($dimensions) ? (int) ($dimensions[1] ?? 0) : 0;
        if (! is_array($dimensions) || $width <= 0 || $height <= 0
            || $width > 12000 || $height > 12000 || ($width * $height) > 32 * 1024 * 1024) {
            return false;
        }
        $detectedMime = (string) ($dimensions['mime'] ?? '');
        $allowedMime = ['image/png' => 'png', 'image/jpeg' => 'jpg', 'image/webp' => 'webp'];
        if (! isset($allowedMime[$detectedMime])) {
            return false;
        }
        $sourceTitle = sanitize_file_name((string) pathinfo($sourcePath, PATHINFO_FILENAME));
        $filename = ($sourceTitle !== '' ? $sourceTitle : 'image') . '-edited.' . $allowedMime[$detectedMime];
        if (! function_exists('wp_generate_attachment_metadata')) {
            require_once ABSPATH . 'wp-admin/includes/image.php';
        }
        $upload = wp_upload_bits($filename, null, $blob);
        if (! empty($upload['error']) || empty($upload['file']) || empty($upload['url'])) {
            return false;
        }
        $newPath = (string) $upload['file'];
        $mime = wp_check_filetype($newPath);
        /* translators: %s: title of the original Media Library image. */
        $copyTitle = sprintf(__('%s edited copy', 'help4-builder-suite'), get_the_title($attachmentId));
        $newId = wp_insert_attachment([
            'post_mime_type' => (string) ($mime['type'] ?? 'image/jpeg'),
            'post_title' => $copyTitle,
            'post_status' => 'inherit',
            'post_parent' => (int) wp_get_post_parent_id($attachmentId),
            'post_author' => get_current_user_id(),
        ], $newPath);
        if (is_wp_error($newId) || (int) $newId <= 0) {
            wp_delete_file($newPath);
            return false;
        }
        $newId = (int) $newId;
        $metadata = wp_generate_attachment_metadata($newId, $newPath);
        if (! is_array($metadata) || $metadata === []) {
            wp_delete_attachment($newId, true);
            return false;
        }
        $metadataUpdated = wp_update_attachment_metadata($newId, $metadata);
        if (! $metadataUpdated && wp_get_attachment_metadata($newId, true) !== $metadata) {
            wp_delete_attachment($newId, true);
            return false;
        }
        update_post_meta($newId, '_wp_attachment_image_alt', $altText);
        return [
            'attachment_id' => $newId,
            'url' => (string) wp_get_attachment_url($newId),
            'edit_url' => (string) get_edit_post_link($newId, 'raw'),
        ];
    }

    private function enhanceAttachment(int $attachmentId, bool $enhance): bool
    {
        if (! Settings::bool('image_opt_enable', true)) {
            return false;
        }
        if (! extension_loaded('imagick')) {
            return false;
        }

        $metadata = wp_get_attachment_metadata($attachmentId, true);
        if (! is_array($metadata)) {
            return false;
        }

        $targets = $this->collectTargets($metadata);
        if (! $targets) {
            return false;
        }

        $quality = max(40, min(95, (int) Settings::get('image_opt_quality', '82')));
        $generateWebp = Settings::bool('image_opt_generate_webp', true);

        $processed = false;
        foreach ($targets as $path) {
            if (! is_string($path) || ! is_file($path)) {
                continue;
            }
            $this->optimizeFile($path, $quality, $generateWebp, $enhance);
            $processed = true;
        }

        return $processed;
    }

    /**
     * @param array<string, mixed> $metadata
     * @return array<int, string>
     */
    private function collectTargets(array $metadata): array
    {
        $upload = wp_get_upload_dir();
        $base = (string) ($upload['basedir'] ?? '');
        if ($base === '') {
            return [];
        }

        $targets = [];
        if (! empty($metadata['file'])) {
            $targets[] = $base . DIRECTORY_SEPARATOR . ltrim((string) $metadata['file'], '/\\');
        }

        if (! empty($metadata['sizes']) && is_array($metadata['sizes'])) {
            $dir = dirname((string) ($metadata['file'] ?? ''));
            foreach ($metadata['sizes'] as $size) {
                if (! is_array($size) || empty($size['file'])) {
                    continue;
                }
                $targets[] = $base . DIRECTORY_SEPARATOR . ltrim($dir . '/' . (string) $size['file'], '/\\');
            }
        }

        return array_values(array_unique($targets));
    }

    private function optimizeFile(string $path, int $quality, bool $generateWebp, bool $enhance): void
    {
        $ext = strtolower((string) pathinfo($path, PATHINFO_EXTENSION));
        if (! in_array($ext, ['jpg', 'jpeg', 'png', 'webp'], true)) {
            return;
        }

        try {
            $image = new \Imagick($path);
            if (method_exists($image, 'autoOrient')) {
                $image->autoOrient();
            } elseif (method_exists($image, 'autoOrientImage')) {
                $image->autoOrientImage();
            }

            if ($enhance) {
                if (method_exists($image, 'normalizeImage')) {
                    $image->normalizeImage();
                }
                if (method_exists($image, 'enhanceImage')) {
                    $image->enhanceImage();
                }
                $image->unsharpMaskImage(0, 0.75, 1.0, 0.05);
            }

            $image->stripImage();
            $image->setImageCompressionQuality($quality);
            if (in_array($ext, ['jpg', 'jpeg'], true)) {
                $image->setInterlaceScheme(\Imagick::INTERLACE_PLANE);
            }
            $image->writeImage($path);

            if ($generateWebp && $ext !== 'webp') {
                $webpPath = preg_replace('/\.[^.]+$/', '.webp', $path);
                if (is_string($webpPath) && $webpPath !== '') {
                    $webp = clone $image;
                    $webp->setImageFormat('webp');
                    $webp->setImageCompressionQuality($quality);
                    $webp->writeImage($webpPath);
                    $webp->destroy();
                }
            }

            $image->destroy();
        } catch (\Throwable $e) {
            // Skip broken files and continue the optimization pass.
        }
    }
}
