<?php

namespace Miruni\RestApi;
use WP_Error;
use WP_REST_Response;
use WP_REST_Request;
use Exception;
use WP_Post;
use Miruni\Gutenberg\Miruni_Gutenberg_Manager;

if (!defined('ABSPATH'))
    exit; // Exit if accessed directly

/**
 * WordPress Plugin: Gutenberg Blocks to HTML Updater
 * Simple version using native WordPress functions
 */
class Miruni_Update_Gutenberg_Blocks_API extends Miruni_BaseAPI
{
    public static string $namespace = 'miruni/v1';
    public static string $route = '/update-gutenberg-blocks';

    /**
     * Register REST API routes
     */
    public static function setup(): void
    {
        add_action('rest_api_init', function () {
            register_rest_route(self::$namespace, self::$route, array(
                'methods' => 'POST',
                'callback' => [__CLASS__, 'update_post_with_blocks'],
                'permission_callback' => [__CLASS__, 'snippet_secret_required_permission_callback'],
                'args' => array(
                    'page_id' => array(
                        'required' => false,
                        'type' => 'integer',
                        'description' => 'The ID of the post to update (can be provided at top level or inside blocks object)',
                        'validate_callback' => function ($param) {
                            return $param === null || (is_numeric($param) && $param > 0);
                        }
                    ),
                    'blocks' => array(
                        'required' => true,
                        'description' => 'Array of Gutenberg blocks in JSON format, or object containing post_id and blocks array'
                    )
                )
            ));
        });

        self::setup_surgical_update();
        self::setup_preview_single_block_update();
    }

    public static function get_rest_url(int $page_id): string
    {
        $params = ['page_id' => $page_id];
        return self::_get_rest_url($params);
    }


    /**
     * Main callback to update post with Gutenberg blocks
     * @param WP_REST_Request<array<mixed>> $request
     * @return WP_Error|WP_REST_Response
     */
    public static function update_post_with_blocks(WP_REST_Request $request): WP_Error|WP_REST_Response
    {
        // Get parameters from request body for POST requests
        $blocks_data = $request->get_param('blocks');

        // Handle nested structure where blocks contain post_id and blocks array
        if (isset($blocks_data['post_id']) && isset($blocks_data['blocks'])) {
            $post_id = (int) $blocks_data['post_id'];
            $blocks = $blocks_data['blocks'];
        } else {
            // Fallback to flat structure
            $post_id = (int) $request->get_param('page_id');
            $blocks = $blocks_data;
        }

        // Validate we have a post_id
        if (empty($post_id)) {
            return new WP_Error(
                'missing_post_id',
                __('Post ID is required either as page_id parameter or inside blocks object.'),
                array('status' => 400)
            );
        }

        // Validate we have blocks array
        if (!is_array($blocks) || empty($blocks)) {
            return new WP_Error(
                'invalid_blocks',
                __('Blocks must be a non-empty array.'),
                array('status' => 400)
            );
        }

        // Verify post exists
        $post = get_post($post_id);
        if (!$post) {
            return new WP_Error(
                'post_not_found',
                __('Post not found.'),
                array('status' => 404)
            );
        }

        try {
            // Transform blocks from custom format to WordPress format
            $wp_blocks = self::transform_blocks_to_wp_format($blocks);

            // Debug: Log first transformed block
            if (!empty($wp_blocks)) {
                error_log("First transformed block: " . json_encode($wp_blocks[0]));
            }

            // Convert blocks array back to block content using native WordPress function
            $block_content = serialize_blocks($wp_blocks);

            // Clear post cache
            clean_post_cache($post_id);

            // Return success response
            return new WP_REST_Response(array(
                'success' => true,
                'updated_content' => Miruni_Update_Gutenberg_Blocks_API::format_html($block_content)
            ), 200);

        } catch (Exception $e) {
            return new WP_Error(
                'processing_error',
                'Error processing blocks: ' . $e->getMessage(),
                array('status' => 500)
            );
        }
    }

    /**
     * Transform blocks from custom format to WordPress format
     * @param array<mixed> $blocks
     * @return array<mixed>
     */
    private static function transform_blocks_to_wp_format(array $blocks): array
    {
        $wp_blocks = array();

        foreach ($blocks as $block) {
            $wp_blocks[] = self::transform_single_block($block);
        }

        return $wp_blocks;
    }

    /**
     * Transform a single block to WordPress format
     * @param mixed $block
     * @return mixed
     */
    private static function transform_single_block($block)
    {
        if (!is_array($block)) {
            return $block;
        }

        $wp_block = array();

        // Handle blockName
        if (!empty($block['blockName'])) {
            $wp_block['blockName'] = $block['blockName'];
        } else {
            $wp_block['blockName'] = '';
        }

        // Handle attrs
        if (isset($block['attrs'])) {
            $wp_block['attrs'] = $block['attrs'];
        } else {
            $wp_block['attrs'] = array();
        }

        // Handle innerHTML - prefer 'inner_html' over existing 'innerHTML'
        if (isset($block['innerHTML'])) {
            $wp_block['innerHTML'] = $block['innerHTML'];
        } else {
            $wp_block['innerHTML'] = '';
        }

        // Handle innerContent
        if (isset($block['innerContent'])) {
            $wp_block['innerContent'] = $block['innerContent'];
        } else {
            $wp_block['innerContent'] = array();
        }

        // Handle inner blocks 
        if (isset($block['innerBlocks']) && is_array($block['innerBlocks'])) {
            $wp_block['innerBlocks'] = self::transform_blocks_to_wp_format($block['innerBlocks']);
        } else {
            $wp_block['innerBlocks'] = array();
        }

        return $wp_block;
    }

    /**
     * Format HTML string with proper indentation
     * @param string $input_html
     * @return string
     */
    private static function format_html(string $input_html): string
    {
        // First, add line breaks around WordPress block comments for better readability
        $html = preg_replace('/<!--\s*(\/?)wp:([^>]+)-->/s', "\n<!-- $1wp:$2 -->\n", $input_html);

        if ($html === null) {
            // If preg_replace fails, return original HTML
            return $input_html;
        }

        // Clean up multiple consecutive newlines
        $html = preg_replace('/\n\s*\n/', "\n", $html);

        if (empty($html)) {
            // If the HTML is empty after processing, return original input
            return $input_html;
        }

        // Method 1: Using DOMDocument (most reliable)
        if (class_exists('DOMDocument')) {
            $dom = new \DOMDocument('1.0', 'UTF-8');
            $dom->formatOutput = true;
            $dom->preserveWhiteSpace = false;

            // Suppress errors for malformed HTML
            libxml_use_internal_errors(true);

            // Load HTML with UTF-8 encoding
            $dom->loadHTML('<?xml encoding="UTF-8">' . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

            // Clear any libxml errors
            libxml_clear_errors();

            $formatted = $dom->saveHTML();
            if ($formatted === false) {
                // If saveHTML fails, return original HTML
                return $html;
            }

            // Remove the XML declaration that was added
            $formatted = str_replace('<?xml encoding="UTF-8">', '', $formatted);

            // Post-process to ensure WordPress block comments are properly spaced
            $formatted_comment_spaces = preg_replace('/<!--\s*(\/?)wp:([^>]+)-->/s', "\n<!-- $1wp:$2 -->\n", $formatted);
            if ($formatted_comment_spaces === null) {
                // If preg_replace fails, return original formatted HTML
                return $formatted;
            }
            return trim($formatted_comment_spaces);
        }

        return $html; // Fallback to original HTML if DOMDocument is not available

        // Method 2: Fallback - Simple regex-based formatting
        //return self::simple_format_html($html);
    }

    /**
     * Simple HTML formatting using regex (fallback method)
     * @param string $_html
     * @return string
     */
    // private static function simple_format_html(string $_html): string
    // {
    //     // Add line breaks after closing tags
    //     $html = preg_replace('/>\s*</', ">\n<", $_html);
    //     if ($html === null) {
    //         // If preg_replace fails, return original HTML
    //         return $_html;
    //     }


    //     // Add proper indentation
    //     $lines = explode("\n", $html);
    //     $formatted_lines = array();
    //     $indent_level = 0;

    //     foreach ($lines as $line) {
    //         $line = trim($line);
    //         if (empty($line))
    //             continue;

    //         // Decrease indent for closing tags
    //         if (preg_match('/^<\/[^>]+>/', $line)) {
    //             $indent_level = max(0, $indent_level - 1);
    //         }

    //         // Add indentation
    //         $formatted_lines[] = str_repeat('  ', $indent_level) . $line;

    //         // Increase indent for opening tags (but not self-closing)
    //         if (preg_match('/^<[^\/][^>]*[^\/]>/', $line)) {
    //             $indent_level++;
    //         }
    //     }

    //     return implode("\n", $formatted_lines);
    // }

    /**
     * Helper method to update post programmatically
     * @param int $post_id
     * @param array<mixed> $blocks
     * @param bool $store_as_html
     * @return bool
     */
    public static function update_post_programmatically(int $post_id, array $blocks, bool $store_as_html = true): bool
    {
        // Verify post exists
        if (!get_post($post_id)) {
            return false;
        }

        // Transform blocks from custom format to WordPress format
        $wp_blocks = self::transform_blocks_to_wp_format($blocks);

        // Convert blocks to content
        $block_content = serialize_blocks($wp_blocks);

        if ($store_as_html) {
            $content = do_blocks($block_content);
        } else {
            $content = $block_content;
        }

        // Update post
        $result = wp_update_post(array(
            'ID' => $post_id,
            'post_content' => Miruni_Update_Gutenberg_Blocks_API::format_html($content)
        ));

        // @phpstan-ignore function.impossibleType
        return !is_wp_error($result);
    }

    /**
     * Helper method to update a single block surgically
     * @param int $post_id
     * @param string $block_name
     * @param array<string, mixed> $match_attrs
     * @param array<string, mixed> $updates
     * @return bool|array{
     *  updated_post: string,
     *  updated_block: string
     * }
     */
    public static function update_single_block_surgically(int $post_id, string $block_name, array $match_attrs, array $updates): array|bool
    {
        try {
            return Miruni_Gutenberg_Manager::update_block_surgically($post_id, $block_name, $match_attrs, $updates);
        } catch (Exception $e) {
            error_log("Surgical block update failed: " . $e->getMessage());
            return false;
        }
    }

    /**
     * New endpoint for surgical block updates
     * @return void
     */
    public static function setup_surgical_update(): void
    {
        add_action('rest_api_init', function () {
            register_rest_route(self::$namespace, '/update-single-block', array(
                'methods' => 'POST',
                'callback' => [__CLASS__, 'update_single_block'],
                'permission_callback' => [__CLASS__, 'snippet_secret_required_permission_callback'],
                'args' => array(
                    'post_id' => array(
                        'required' => true,
                        'type' => 'integer',
                        'description' => 'The ID of the post containing the block'
                    ),
                    'block_name' => array(
                        'required' => true,
                        'type' => 'string',
                        'description' => 'The block name to find'
                    ),
                    'match_attrs' => array(
                        'required' => false,
                        'description' => 'Attributes that must match to identify the block'
                    ),
                    'updates' => array(
                        'required' => true,
                        'description' => 'Updates to apply to the block'
                    )
                )
            ));
        });
    }

    /**
     * REST API callback for surgical block updates
     * @param WP_REST_Request<array<string, mixed>> $request
     * @return WP_Error|WP_REST_Response
     */
    public static function update_single_block(WP_REST_Request $request): WP_Error|WP_REST_Response
    {
        $post_id = (int) $request->get_param('post_id');
        $block_name = (string) $request->get_param('block_name');
        $match_attrs = $request->get_param('match_attrs');
        $updates = $request->get_param('updates');

        // Ensure match_attrs is an array
        if (!is_array($match_attrs)) {
            $match_attrs = [];
        }

        // Ensure updates is an array
        if (!is_array($updates)) {
            return new WP_Error(
                'invalid_updates',
                __('Updates parameter must be an array.'),
                array('status' => 400)
            );
        }

        if (empty($post_id) || empty($block_name) || empty($updates)) {
            return new WP_Error(
                'missing_parameters',
                __('post_id, block_name, and updates are required.'),
                array('status' => 400)
            );
        }

        try {
            $success = Miruni_Gutenberg_Manager::update_block_surgically($post_id, $block_name, $match_attrs, $updates);

            if (!$success) {
                return new WP_Error(
                    'update_failed',
                    __('Failed to update block.'),
                    array('status' => 500)
                );
            }

            return new WP_REST_Response(array(
                'success' => true,
                'message' => 'Block updated successfully'
            ), 200);

        } catch (Exception $e) {
            return new WP_Error(
                'processing_error',
                'Error updating block: ' . $e->getMessage(),
                array('status' => 500)
            );
        }
    }

    /**
     * Preview a single block update
     * @param WP_REST_Request<array<string, mixed>> $request
     * @return WP_Error|WP_REST_Response
     */
    public static function preview_single_block_update(WP_REST_Request $request): WP_Error|WP_REST_Response
    {
        $post_id = (int) $request->get_param('post_id');
        $block_name = (string) $request->get_param('block_name');
        $match_attrs = $request->get_param('match_attrs');
        $updates = $request->get_param('updates');

        // Ensure match_attrs is an array
        if (!is_array($match_attrs)) {
            $match_attrs = [];
        }

        // Ensure updates is an array
        if (!is_array($updates)) {
            return new WP_Error(
                'invalid_updates',
                __('Updates parameter must be an array.'),
                array('status' => 400)
            );
        }

        if (empty($post_id) || empty($block_name) || empty($updates)) {
            return new WP_Error(
                'missing_parameters',
                __('post_id, block_name, and updates are required.'),
                array('status' => 400)
            );
        }

        try {
            $updated_content = Miruni_Gutenberg_Manager::preview_block_updates($post_id, $block_name, $match_attrs, $updates);
            if ($updated_content === false) {
                return new WP_Error(
                    'preview_failed',
                    __('Failed to preview block updates.'),
                    array('status' => 500)
                );
            }
            return new WP_REST_Response(array(
                'success' => true,
                'updated_post' => self::format_html($updated_content['updated_post']),
                'updated_block' => $updated_content['updated_block']
            ), 200);

        } catch (Exception $e) {
            return new WP_Error(
                'processing_error',
                'Error processing block preview: ' . $e->getMessage(),
                array('status' => 500)
            );
        }
    }

    public static function setup_preview_single_block_update(): void
    {
        add_action('rest_api_init', function () {
            register_rest_route(self::$namespace, '/preview-single-block-update', array(
                'methods' => 'POST',
                'callback' => [__CLASS__, 'preview_single_block_update'],
                'permission_callback' => [__CLASS__, 'snippet_secret_required_permission_callback'],
                'args' => array(
                    'post_id' => array(
                        'required' => true,
                        'type' => 'integer',
                        'description' => 'The ID of the post containing the block'
                    ),
                    'block_name' => array(
                        'required' => true,
                        'type' => 'string',
                        'description' => 'The block name to find'
                    ),
                    'match_attrs' => array(
                        'required' => false,
                        'description' => 'Attributes that must match to identify the block'
                    ),
                    'updates' => array(
                        'required' => true,
                        'description' => 'Updates to apply to the block'
                    )
                )
            ));
        });
    }


    /**
     * Get the REST API URL for surgical block updates
     * @return string
     */
    public static function get_surgical_update_rest_url(int $post_id): string
    {
        return self::_get_rest_url(['post_id' => $post_id], '/update-single-block');
    }
    /**
     * Get the REST API URL for previewing a single block update
     * @return string
     */
    public static function get_preview_single_block_update_rest_url(int $post_id): string
    {
        return self::_get_rest_url(['post_id' => $post_id], '/preview-single-block-update');
    }
}