<?php

namespace Miruni\Breakdance;
use Exception;
use WP_Post;

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

class Miruni_Breakdance_Service
{
    /**
     * Returns the Breakdance data tree for a given post using Breakdance functions.
     * @param int $post_id
     * @return array|false The Breakdance data tree as an array, or false if not found or invalid.
     * @phpstan-ignore-next-line - Need to define Tree type if possible or use array
     */
    public static function get_page_data(int $post_id)
    {
        // Check if the Breakdance function exists using its fully qualified name
        if (!function_exists('\Breakdance\Data\get_tree')) {
            error_log("Miruni Breakdance Service: \Breakdance\Data\get_tree function not found.");
            return false;
        }

        // Check if the Breakdance validation function exists using its fully qualified name
        if (!function_exists('\Breakdance\Data\is_valid_tree')) {
            error_log("Miruni Breakdance Service: \Breakdance\Data\is_valid_tree function not found.");
            // Optionally, proceed without validation or return false
            // return false; // Stricter approach
        }

        try {
            // Attempt to get the tree using the Breakdance function with its fully qualified name
            $tree = \Breakdance\Data\get_tree($post_id);
            error_log("Miruni Breakdance Service: Retrieved tree for post ID $post_id. - " . gettype($tree));

            // Check if the tree was retrieved successfully
            if ($tree === false) {
                return false;
            }

            // Validate the tree structure if the validation function exists

            // Call validation function with its fully qualified name
            if (!Miruni_Breakdance_Service::is_valid_tree($tree)) {
                error_log("Miruni Breakdance Service: Retrieved data for post ID $post_id is not a valid Breakdance tree.");
                return false;
            } else {
                // Basic array check if validation function is missing
                if (!is_array($tree) || !isset($tree['root'])) {
                    error_log("Miruni Breakdance Service: Retrieved data for post ID $post_id is not an array or missing root (basic check).");
                    return false;
                }
            }


            // Return the valid tree data (which should be an array)
            return $tree;

        } catch (\Exception $e) {
            error_log("Miruni Breakdance Service: Exception caught during get_page_data for post ID $post_id. Message: " . $e->getMessage());
            do_action('miruni_plugin_error', $e, [
                'context' => 'Miruni_Breakdance_Service.get_page_data',
                'operation' => "Getting Breakdance tree for post_id: $post_id",
                'user_id' => get_current_user_id()
            ]);
            return false;
        }
    }

    /**
     * Validates the Breakdance data tree structure.
     * Uses Breakdance's validation function if available, otherwise falls back to basic validation.
     *
     * @param array<mixed> $tree The Breakdance data tree to validate.
     * @return bool True if valid, false otherwise.
     */
    public static function is_valid_tree(array $tree): bool
    {
        // Check if the tree is valid using Breakdance's validation function
        if (function_exists('\Breakdance\Data\is_valid_tree')) {
            return \Breakdance\Data\is_valid_tree($tree);
        }

        // Fallback to basic validation if the function doesn't exist
        // @phpstan-ignore function.alreadyNarrowedType
        return is_array($tree) && isset($tree['root']);
    }

    /**
     * Extracts the Breakdance tree data array from various input formats,
     * validates it (basic check), encodes it to JSON, and returns it
     * in the format expected by Breakdance\Data\set_meta.
     *
     * Handles direct arrays, JSON strings, or arrays containing a 'tree_json_string'.
     *
     * @param string|array $data The input data (PHP array or JSON string).
     * @return array<string, string>|false The formatted data array ['tree_json_string' => $json], or false on failure.
     * @phpstan-ignore missingType.iterableValue
     */
    public static function tree_to_breakdance_data(string|array $data): array|false
    {
        $tree_data_array = null;

        // --- Logic to extract $tree_data_array remains the same ---
        if (is_string($data)) {
            $decoded_data = json_decode($data, true);
            if (json_last_error() !== JSON_ERROR_NONE) {
                error_log("Miruni Breakdance Service [tree_to_breakdance_data]: Failed to JSON decode input string. Error: " . json_last_error_msg());
                return false;
            }
            if (isset($decoded_data['tree_json_string']) && is_string($decoded_data['tree_json_string'])) {
                $inner_decoded = json_decode($decoded_data['tree_json_string'], true);
                if (json_last_error() !== JSON_ERROR_NONE) {
                    error_log("Miruni Breakdance Service [tree_to_breakdance_data]: Failed to JSON decode inner tree_json_string. Error: " . json_last_error_msg());
                    return false;
                }
                $tree_data_array = $inner_decoded;
            } else {
                $tree_data_array = $decoded_data;
            }
            // @phpstan-ignore function.alreadyNarrowedType
        } elseif (is_array($data)) {
            if (isset($data['tree_json_string']) && is_string($data['tree_json_string'])) {
                $inner_decoded = json_decode($data['tree_json_string'], true);
                if (json_last_error() !== JSON_ERROR_NONE) {
                    error_log("Miruni Breakdance Service [tree_to_breakdance_data]: Failed to JSON decode inner tree_json_string from array. Error: " . json_last_error_msg());
                    return false;
                }
                $tree_data_array = $inner_decoded;
            } elseif (isset($data['tree_json_string']) && is_array($data['tree_json_string'])) {
                $tree_data_array = $data['tree_json_string'];
            } else {
                $tree_data_array = $data;
            }
        } else {
            error_log("Miruni Breakdance Service [tree_to_breakdance_data]: Invalid type for input data. Expected string or array, got " . gettype($data));
            return false;
        }
        // --- End of extraction logic ---


        // Final check to ensure we have an array before encoding
        if (!is_array($tree_data_array)) {
            error_log("Miruni Breakdance Service [tree_to_breakdance_data]: Could not derive a valid tree data array before encoding.");
            return false;
        }

        // Basic validation: Check if it looks like a Breakdance tree (has a root)
        // More robust validation happens later using is_valid_tree if available
        if (!isset($tree_data_array['root'])) {
            error_log("Miruni Breakdance Service [tree_to_breakdance_data]: Derived tree data array is missing 'root' key.");
            // Allow potentially incomplete trees for now, validation happens later
            // return false; // Uncomment for stricter validation here
        }

        // Encode the extracted tree data array into a JSON string
        $tree_json_string = json_encode($tree_data_array, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        if (json_last_error() !== JSON_ERROR_NONE || $tree_json_string === false) {
            error_log("Miruni Breakdance Service [tree_to_breakdance_data]: Failed to JSON encode extracted tree data array. Error: " . json_last_error_msg());
            return false;
        }

        // Return the data in the format Breakdance expects for set_meta
        return ['tree_json_string' => $tree_json_string];
    }

    /**
     * Updates the Breakdance data for a given post.
     * Uses Breakdance\Data\set_meta if available, otherwise falls back to update_post_meta.
     *
     * Accepts the Breakdance data tree as either a PHP array or a JSON string representation of that tree.
     *
     * @param int $post_id
     * @param string|array $modified_data PHP array or JSON string representing the Breakdance data tree.
     * @return bool True on success, false on failure
     * @phpstan-ignore missingType.iterableValue
     */
    public static function update_page_data(int $post_id, string|array $modified_data): bool
    {
        error_log("Miruni Breakdance Service: Updating Breakdance data for post ID $post_id.");
        error_log("Type of input modified_data: " . gettype($modified_data));

        try {
            // 1. Get the formatted Breakdance data array ['tree_json_string' => $json]
            $formatted_data = self::tree_to_breakdance_data($modified_data);

            if ($formatted_data === false || !isset($formatted_data['tree_json_string'])) {
                error_log("Miruni Breakdance Service: Failed to get formatted tree data for post ID $post_id in update_page_data.");
                return false; // Error already logged in tree_to_breakdance_data
            }

            // 2. Decode the inner JSON string to get the tree array for validation
            $tree_json_string_for_validation = $formatted_data['tree_json_string'];
            $tree_data_array_for_validation = json_decode($tree_json_string_for_validation, true);

            if (json_last_error() !== JSON_ERROR_NONE) {
                error_log("Miruni Breakdance Service: Failed to JSON decode tree_json_string for validation for post ID $post_id. Error: " . json_last_error_msg());
                return false;
            }

            if (!is_array($tree_data_array_for_validation)) {
                error_log("Miruni Breakdance Service: Decoded tree data for validation is not an array for post ID $post_id.");
                return false;
            }

            // 3. Validate the tree structure using Breakdance's function if available
            if (!self::is_valid_tree($tree_data_array_for_validation)) {
                error_log("Miruni Breakdance Service: Derived tree data array is not valid according to is_valid_tree for post ID $post_id.");
                return false;
            }

            // 4. Attempt to save using Breakdance\Data\set_meta with the pre-formatted data
            $update_result = false;
            if (function_exists('\Breakdance\Data\set_meta')) {
                error_log("Miruni Breakdance Service: Using \Breakdance\Data\set_meta for post ID $post_id.");
                // $formatted_data is already in the correct format ['tree_json_string' => $json]
                $update_result = \Breakdance\Data\set_meta($post_id, '_breakdance_data', $formatted_data);
                if ($update_result === false) {
                    error_log("Miruni Breakdance Service: \Breakdance\Data\set_meta failed for post ID $post_id.");
                }
            } else {
                // 5. Fallback to update_post_meta if set_meta doesn't exist
                error_log("Miruni Breakdance Service: \Breakdance\Data\set_meta not found. Falling back to update_post_meta for post ID $post_id.");
                // update_post_meta expects a single string value (usually JSON).
                // We need to encode the *entire* $formatted_data structure into JSON.
                $json_to_save = json_encode($formatted_data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
                if (json_last_error() !== JSON_ERROR_NONE) {
                    error_log("Miruni Breakdance Service: Failed to JSON encode final formatted data for update_post_meta fallback for post ID $post_id. Error: " . json_last_error_msg());
                    return false;
                }

                // Use wp_slash for security before saving with update_post_meta
                $update_meta_result = update_post_meta($post_id, '_breakdance_data', wp_slash($json_to_save));
                $update_result = ($update_meta_result !== false); // update_post_meta returns meta_id or true on success, false on failure.
                if ($update_result === false) {
                    error_log("Miruni Breakdance Service: update_post_meta fallback failed for post ID $post_id.");
                }
            }

            if ($update_result === false) {
                error_log("Miruni Breakdance Service: Failed to update Breakdance data for post ID $post_id after attempting save.");
                return false;
            }

            error_log("Miruni Breakdance Service: Successfully updated Breakdance data for post ID $post_id.");
            return true;

        } catch (\Exception $e) {
            error_log("Miruni Breakdance Service: Exception caught during update_page_data for post ID $post_id. Message: " . $e->getMessage());
            do_action('miruni_plugin_error', $e, [
                'context' => 'Miruni_Breakdance_Service.update_page_data',
                'operation' => "Updating Breakdance post meta for post_id: $post_id",
                'user_id' => get_current_user_id()
            ]);
            return false;
        }
    }

    /**
     * Check if the post is a Breakdance page
     *
     * @param mixed $post Post object
     * @return bool True if it's a Breakdance page
     */
    public static function is_breakdance_page($post): bool
    {
        if (!$post) {
            return false;
        }

        $post_id = $post instanceof WP_Post ? $post->ID : ($post['ID'] ?? null);

        if (!$post_id) {
            return false;
        }

        // Check if Breakdance is active and if this is a Breakdance-edited page
        if (function_exists('breakdance_loaded')) {
            return !empty(get_post_meta($post_id, '_breakdance_data', true));
        }

        return !empty(get_post_meta($post_id, '_breakdance_data', true));
    }

    public static function render_breakdance_page(int $post_id): void
    {
        if (function_exists('\Breakdance\Render\generateCacheForPost')) {
            error_log("Miruni Breakdance Service: Calling Breakdance render function for post ID $post_id.");
            try {
                \Breakdance\Render\generateCacheForPost($post_id);
            } catch (Exception $e) {
                do_action('miruni_plugin_error', $e, [
                    'context' => 'Miruni_Breakdance_Service.render_breakdance_page',
                    'operation' => "Generating cache for Breakdance post ID: $post_id",
                    'user_id' => get_current_user_id()
                ]);
            }
        }
    }

    public static function get_global_block_data(int $global_block_id): string|false
    {
        $post = get_post($global_block_id);
        if (!$post || !($post instanceof WP_Post) || $post->post_type !== 'breakdance_block') {
            error_log("Miruni Breakdance Service: No post found for global block ID $global_block_id.");
            return false;
        }

        $block_data = Miruni_Breakdance_Service::get_page_data($global_block_id);
        if ($block_data === false) {
            error_log("Miruni Breakdance Service: Failed to get global block data for ID $global_block_id.");
            return false;
        }
        return json_encode($block_data);
    }
}