<?php
/**
 * Trait: Niwa_AI_Chat_Content_Access
 *
 * Content reading, searching, and text-extraction helpers
 * extracted from Niwa_AI_Admin_Autonomy_Chat.
 *
 * @package suspended Niwa_AI
 */

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

trait Niwa_AI_Chat_Content_Access {

    private function execute_read_post_field( $args ) {
        if ( ! $this->admin_tools ) {
            return array( 'ok' => false, 'error' => 'Admin tools nisu ucitani.' );
        }

        $target_type  = isset( $args['target_type'] ) ? (string) $args['target_type'] : 'front_page';
        $target_value = isset( $args['target_value'] ) ? trim( (string) $args['target_value'] ) : '';
        $field        = isset( $args['field'] ) ? (string) $args['field'] : 'post_content';
        $extract_text = ! empty( $args['extract_text'] );
        $search_text  = isset( $args['search_text'] ) ? trim( (string) $args['search_text'] ) : '';

        // Resolve the post target using the same logic as replace_text_in_post.
        $resolve_args = array( 'target_type' => $target_type, 'target_value' => $target_value );

        // Use reflection-free approach: replicate resolve_post_target logic here.
        if ( 'front_page' === $target_type ) {
            $page_id = intval( get_option( 'page_on_front' ) );
            if ( $page_id <= 0 ) {
                return array( 'ok' => false, 'error' => 'Front page nije podesena kao staticka stranica.' );
            }
            $post = get_post( $page_id );
        } elseif ( 'post_id' === $target_type ) {
            $post = get_post( intval( $target_value ) );
        } elseif ( 'slug' === $target_type ) {
            $post = get_page_by_path( $target_value, OBJECT, array( 'page', 'post', 'product' ) );
        } else {
            $posts = get_posts(
                array(
                    'post_type'      => array( 'page', 'post', 'product' ),
                    'post_status'    => array( 'publish', 'draft', 'private', 'future' ),
                    'posts_per_page' => 1,
                    's'              => $target_value,
                )
            );
            $post = ! empty( $posts[0] ) ? $posts[0] : null;
        }

        if ( empty( $post ) ) {
            return array( 'ok' => false, 'error' => 'Ciljni post nije pronadjen.' );
        }

        $post_id = intval( $post->ID );

        // ------ meta_keys discovery mode ------
        if ( 'meta_keys' === $field ) {
            $all_meta  = get_post_meta( $post_id );
            $keys_info = array();
            if ( is_array( $all_meta ) ) {
                foreach ( $all_meta as $mk => $mvs ) {
                    if ( $this->is_sensitive_meta_key( $mk ) ) {
                        continue;
                    }
                    $total_size = 0;
                    foreach ( $mvs as $v ) {
                        $total_size += strlen( is_string( $v ) ? $v : maybe_serialize( $v ) );
                    }
                    $first   = isset( $mvs[0] ) ? $mvs[0] : '';
                    $first_s = is_string( $first ) ? $first : maybe_serialize( $first );
                    $is_json = is_string( $first ) && null !== json_decode( $first, true ) && JSON_ERROR_NONE === json_last_error();
                    $keys_info[] = array(
                        'key'        => $mk,
                        'count'      => count( $mvs ),
                        'total_size' => $total_size,
                        'is_json'    => $is_json,
                        'preview'    => mb_substr( (string) $first_s, 0, 200 ),
                    );
                }
            }
            usort( $keys_info, function ( $a, $b ) { return $b['total_size'] - $a['total_size']; } );
            return array(
                'ok'         => true,
                'post_id'    => $post_id,
                'post_title' => (string) $post->post_title,
                'field'      => 'meta_keys',
                'total_keys' => count( $keys_info ),
                'keys'       => $keys_info,
                'hint'       => 'Koristi "meta:KEY_NAME" kao field vrednost da procitas pun sadrzaj bilo kog kljuca.',
            );
        }

        // ------ rendered HTML mode ------
        if ( 'rendered' === $field ) {
            $original_post   = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null;
            $GLOBALS['post'] = $post;
            setup_postdata( $post );
            $rendered = apply_filters( 'the_content', $post->post_content );
            if ( $original_post ) {
                $GLOBALS['post'] = $original_post;
                setup_postdata( $original_post );
            } else {
                wp_reset_postdata();
            }
            $max       = 12000;
            $truncated = strlen( $rendered ) > $max;
            if ( $truncated ) {
                $rendered = mb_substr( $rendered, 0, $max );
            }
            return array(
                'ok'           => true,
                'post_id'      => $post_id,
                'post_title'   => (string) $post->post_title,
                'field'        => 'rendered',
                'value'        => $rendered,
                'total_length' => strlen( $rendered ),
                'truncated'    => $truncated,
            );
        }

        // ------ meta:KEY_NAME mode ------
        if ( 0 === strpos( $field, 'meta:' ) ) {
            $meta_key = substr( $field, 5 );
            if ( $this->is_sensitive_meta_key( $meta_key ) ) {
                return array( 'ok' => false, 'error' => 'Ovaj meta kljuc je obelezen kao osetljiv.' );
            }
            $raw = (string) get_post_meta( $post_id, $meta_key, true );
        }
        // ------ Standard fields (backward compat) ------
        elseif ( 'elementor_data' === $field ) {
            $raw = (string) get_post_meta( $post_id, '_elementor_data', true );
        } elseif ( 'post_title' === $field ) {
            $raw = (string) $post->post_title;
        } elseif ( 'post_excerpt' === $field ) {
            $raw = (string) $post->post_excerpt;
        } else {
            $raw = (string) $post->post_content;
        }

        if ( '' === $raw ) {
            return array(
                'ok'         => true,
                'post_id'    => $post_id,
                'post_title' => (string) $post->post_title,
                'field'      => $field,
                'value'      => '',
                'note'       => 'Polje je prazno.',
            );
        }

        // For elementor_data with extract_text, parse and extract text nodes.
        if ( 'elementor_data' === $field && $extract_text ) {
            $decoded = json_decode( $raw, true );
            if ( ! is_array( $decoded ) ) {
                return array(
                    'ok'         => true,
                    'post_id'    => $post_id,
                    'post_title' => (string) $post->post_title,
                    'field'      => $field,
                    'note'       => 'Elementor data postoji ali nije validan JSON.',
                    'raw_length' => strlen( $raw ),
                    'raw_preview' => mb_substr( $raw, 0, 2000 ),
                );
            }

            $text_nodes = array();
            $this->extract_elementor_text_nodes( $decoded, $text_nodes );

            // Filter by search_text if provided.
            if ( '' !== $search_text ) {
                $search_lower = mb_strtolower( $search_text );
                $text_nodes   = array_values(
                    array_filter(
                        $text_nodes,
                        function ( $node ) use ( $search_lower ) {
                            return false !== mb_strpos( mb_strtolower( $node['text'] ), $search_lower );
                        }
                    )
                );
            }

            return array(
                'ok'          => true,
                'post_id'     => $post_id,
                'post_title'  => (string) $post->post_title,
                'field'       => $field,
                'mode'        => 'extract_text',
                'total_nodes' => count( $text_nodes ),
                'text_nodes'  => $text_nodes,
                'hint'        => 'Use the exact "text" values from these nodes as find_text in replace_text_in_post with field=elementor_data. The text includes HTML tags like <span> which must be matched exactly. The replacement operates on decoded text content within each widget, not on raw JSON — so these values are safe to use directly.',
            );
        }

        // For meta:KEY fields with extract_text, try generic JSON text extraction.
        if ( 0 === strpos( $field, 'meta:' ) && $extract_text ) {
            $decoded = json_decode( $raw, true );
            if ( is_array( $decoded ) ) {
                $text_nodes = array();
                $this->extract_generic_json_text_nodes( $decoded, $text_nodes );

                // Filter by search_text if provided.
                if ( '' !== $search_text ) {
                    $search_lower = mb_strtolower( $search_text );
                    $text_nodes   = array_values(
                        array_filter(
                            $text_nodes,
                            function ( $node ) use ( $search_lower ) {
                                return false !== mb_strpos( mb_strtolower( $node['text'] ), $search_lower );
                            }
                        )
                    );
                }

                return array(
                    'ok'          => true,
                    'post_id'     => $post_id,
                    'post_title'  => (string) $post->post_title,
                    'field'       => $field,
                    'mode'        => 'extract_text',
                    'total_nodes' => count( $text_nodes ),
                    'text_nodes'  => $text_nodes,
                    'hint'        => 'Use the exact "text" values from these nodes as find_text in replace_text_in_post with field=' . $field . '. The generic JSON walker will find and replace the text across all string values in the JSON structure.',
                );
            }
            // Not JSON — fall through to raw value return.
        }

        // For non-extract mode or non-meta/elementor fields, return raw value.
        $value = $raw;

        // Filter by search_text if provided.
        if ( '' !== $search_text ) {
            $search_lower = mb_strtolower( $search_text );
            if ( false === mb_strpos( mb_strtolower( $value ), $search_lower ) ) {
                return array(
                    'ok'         => true,
                    'post_id'    => $post_id,
                    'post_title' => (string) $post->post_title,
                    'field'      => $field,
                    'note'       => 'Trazeni tekst "' . $search_text . '" nije pronadjen u ovom polju.',
                    'field_length' => strlen( $value ),
                );
            }
        }

        // Truncate if too large.
        $max_return = 12000;
        $truncated  = false;
        if ( strlen( $value ) > $max_return ) {
            $value     = mb_substr( $value, 0, $max_return );
            $truncated = true;
        }

        return array(
            'ok'           => true,
            'post_id'      => $post_id,
            'post_title'   => (string) $post->post_title,
            'field'        => $field,
            'value'        => $value,
            'total_length' => strlen( $raw ),
            'truncated'    => $truncated,
        );
    }

    /**
     * Search for text across ALL storage locations of a post:
     * post_title, post_content, post_excerpt, and every post meta value.
     *
     * Returns a list of locations where the text was found so the agent
     * knows exactly which field to target in replace_text_in_post.
     */
    private function execute_search_text_in_post( $args ) {
        $target_type  = isset( $args['target_type'] ) ? (string) $args['target_type'] : 'front_page';
        $target_value = isset( $args['target_value'] ) ? trim( (string) $args['target_value'] ) : '';
        $search_text  = isset( $args['search_text'] ) ? trim( (string) $args['search_text'] ) : '';

        if ( '' === $search_text ) {
            return array( 'ok' => false, 'error' => 'search_text je obavezan.' );
        }

        // Resolve post target — same logic as execute_read_post_field().
        if ( 'front_page' === $target_type ) {
            $page_id = intval( get_option( 'page_on_front' ) );
            if ( $page_id <= 0 ) {
                return array( 'ok' => false, 'error' => 'Front page nije podesena kao staticka stranica.' );
            }
            $post = get_post( $page_id );
        } elseif ( 'post_id' === $target_type ) {
            $post = get_post( intval( $target_value ) );
        } elseif ( 'slug' === $target_type ) {
            $post = get_page_by_path( $target_value, OBJECT, array( 'page', 'post', 'product' ) );
        } else {
            $posts = get_posts(
                array(
                    'post_type'      => array( 'page', 'post', 'product' ),
                    'post_status'    => array( 'publish', 'draft', 'private', 'future' ),
                    'posts_per_page' => 1,
                    's'              => $target_value,
                )
            );
            $post = ! empty( $posts[0] ) ? $posts[0] : null;
        }

        if ( empty( $post ) ) {
            return array( 'ok' => false, 'error' => 'Ciljni post nije pronadjen.' );
        }

        $post_id = intval( $post->ID );

        $search_lower = mb_strtolower( $search_text );
        $locations    = array();

        // Check core post fields.
        $core_fields = array(
            'post_title'   => (string) $post->post_title,
            'post_content' => (string) $post->post_content,
            'post_excerpt' => (string) $post->post_excerpt,
        );
        foreach ( $core_fields as $field_name => $field_value ) {
            if ( '' !== $field_value && false !== mb_strpos( mb_strtolower( $field_value ), $search_lower ) ) {
                $locations[] = array(
                    'location'          => $field_name,
                    'field_for_replace' => $field_name,
                    'value_type'        => 'plain',
                    'value_length'      => strlen( $field_value ),
                    'context'           => $this->snippet_around_match( $field_value, $search_text ),
                );
            }
        }

        // Check ALL post meta.
        $all_meta = get_post_meta( $post_id );
        if ( is_array( $all_meta ) ) {
            foreach ( $all_meta as $meta_key => $meta_values ) {
                if ( $this->is_sensitive_meta_key( $meta_key ) ) {
                    continue;
                }

                foreach ( $meta_values as $raw_val ) {
                    $searchable = is_string( $raw_val ) ? $raw_val : maybe_serialize( $raw_val );
                    if ( ! is_string( $searchable ) || '' === $searchable ) {
                        continue;
                    }
                    if ( false === mb_strpos( mb_strtolower( $searchable ), $search_lower ) ) {
                        continue;
                    }

                    // Determine value type.
                    $val_type = 'plain';
                    if ( null !== json_decode( $searchable, true ) && JSON_ERROR_NONE === json_last_error() ) {
                        $val_type = 'json';
                    } elseif ( is_serialized( $searchable ) ) {
                        $val_type = 'serialized_php';
                    }

                    $locations[] = array(
                        'location'          => 'meta:' . $meta_key,
                        'field_for_replace' => 'meta:' . $meta_key,
                        'meta_key'          => $meta_key,
                        'value_type'        => $val_type,
                        'value_length'      => strlen( $searchable ),
                        'context'           => $this->snippet_around_match( $searchable, $search_text ),
                    );
                    break; // One match per meta key is enough.
                }
            }
        }

        return array(
            'ok'          => true,
            'post_id'     => $post_id,
            'post_title'  => (string) $post->post_title,
            'search_text' => $search_text,
            'found_in'    => count( $locations ),
            'locations'   => $locations,
            'hint'        => count( $locations ) > 0
                ? 'Koristi "field_for_replace" vrednost kao "field" parametar u replace_text_in_post ili read_post_field.'
                : 'Tekst nije pronadjen ni u jednom polju ni meta vrednosti za ovaj post.',
        );
    }

    /**
     * Recursively extract text content from Elementor JSON elements.
     */
    private function extract_elementor_text_nodes( $elements, &$results, $depth = 0 ) {
        if ( ! is_array( $elements ) || $depth > 20 ) {
            return;
        }

        foreach ( $elements as $element ) {
            if ( ! is_array( $element ) ) {
                continue;
            }

            $widget_type = isset( $element['widgetType'] ) ? (string) $element['widgetType'] : '';
            $el_type     = isset( $element['elType'] ) ? (string) $element['elType'] : '';
            $type_label  = '' !== $widget_type ? $widget_type : $el_type;
            $settings    = isset( $element['settings'] ) && is_array( $element['settings'] ) ? $element['settings'] : array();

            // ----- V3 classic: known text fields stored as direct strings. -----
            $text_fields = array(
                'title'            => 'heading/title',
                'editor'           => 'text editor',
                'text'             => 'text',
                'description_text' => 'description',
                'description'      => 'description',
                'button_text'      => 'button',
                'inner_text'       => 'inner text',
                'prefix'           => 'prefix',
                'suffix'           => 'suffix',
                'before_text'      => 'before text',
                'after_text'       => 'after text',
                'highlight_text'   => 'highlight',
                'rotating_text'    => 'rotating text',
                'alert_title'      => 'alert title',
                'alert_description' => 'alert description',
                'tab_title'        => 'tab title',
                'testimonial_content' => 'testimonial',
                'testimonial_name'    => 'testimonial name',
                'testimonial_job'     => 'testimonial job',
                'caption'          => 'caption',
                'link_text'        => 'link text',
                'badge_text'       => 'badge',
                'ribbon_title'     => 'ribbon',
                'sub_title'        => 'subtitle',
                'header_text'      => 'header',
                'footer_text'      => 'footer',
            );

            // Track which keys we already extracted (to avoid duplicates from V4 pass).
            $extracted_keys = array();

            foreach ( $text_fields as $setting_key => $label ) {
                if ( isset( $settings[ $setting_key ] ) && is_string( $settings[ $setting_key ] ) && '' !== trim( $settings[ $setting_key ] ) ) {
                    $raw_text = $settings[ $setting_key ];
                    $stripped = wp_strip_all_tags( $raw_text );
                    if ( '' !== trim( $stripped ) ) {
                        $results[] = array(
                            'widget_type' => $type_label,
                            'setting'     => $setting_key,
                            'label'       => $label,
                            'text'        => $raw_text,
                            'text_plain'  => trim( $stripped ),
                        );
                        $extracted_keys[ $setting_key ] = true;
                    }
                }
            }

            // ----- V4 typed values: {$$type: "string", value: "..."} -----
            // Elementor V4 wraps text content in typed objects.  The $$type
            // annotation guarantees the value is user-visible text, so we can
            // safely scan ALL settings keys — no hard-coded list needed.
            foreach ( $settings as $setting_key => $val ) {
                if ( isset( $extracted_keys[ $setting_key ] ) ) {
                    continue;
                }
                if ( is_array( $val )
                     && isset( $val['$$type'] )
                     && 'string' === $val['$$type']
                     && isset( $val['value'] )
                     && is_string( $val['value'] )
                     && '' !== trim( $val['value'] )
                ) {
                    $raw_text = $val['value'];
                    $stripped = wp_strip_all_tags( $raw_text );
                    if ( '' !== trim( $stripped ) ) {
                        $label = isset( $text_fields[ $setting_key ] ) ? $text_fields[ $setting_key ] : $setting_key;
                        $results[] = array(
                            'widget_type' => $type_label,
                            'setting'     => $setting_key,
                            'label'       => $label . ' (v4)',
                            'text'        => $raw_text,
                            'text_plain'  => trim( $stripped ),
                        );
                    }
                }
            }

            // ----- V3 Repeaters: icon_list. -----
            if ( isset( $settings['icon_list'] ) && is_array( $settings['icon_list'] ) ) {
                foreach ( $settings['icon_list'] as $idx => $item ) {
                    if ( isset( $item['text'] ) && is_string( $item['text'] ) && '' !== trim( $item['text'] ) ) {
                        $results[] = array(
                            'widget_type' => $widget_type ?: 'icon-list',
                            'setting'     => "icon_list[$idx].text",
                            'label'       => 'list item',
                            'text'        => $item['text'],
                            'text_plain'  => trim( wp_strip_all_tags( $item['text'] ) ),
                        );
                    }
                }
            }

            // ----- V3 Repeaters: tabs. -----
            if ( isset( $settings['tabs'] ) && is_array( $settings['tabs'] ) ) {
                foreach ( $settings['tabs'] as $idx => $tab ) {
                    foreach ( array( 'tab_title', 'tab_content' ) as $tk ) {
                        if ( isset( $tab[ $tk ] ) && is_string( $tab[ $tk ] ) && '' !== trim( $tab[ $tk ] ) ) {
                            $results[] = array(
                                'widget_type' => $widget_type ?: 'tabs',
                                'setting'     => "tabs[$idx].$tk",
                                'label'       => $tk,
                                'text'        => $tab[ $tk ],
                                'text_plain'  => trim( wp_strip_all_tags( $tab[ $tk ] ) ),
                            );
                        }
                    }
                }
            }

            // ----- V4 Repeaters: any sequential array containing typed string values. -----
            foreach ( $settings as $setting_key => $repeater ) {
                if ( ! is_array( $repeater ) || ! isset( $repeater[0] ) || ! is_array( $repeater[0] ) ) {
                    continue;
                }
                foreach ( $repeater as $idx => $rep_item ) {
                    if ( ! is_array( $rep_item ) ) {
                        continue;
                    }
                    foreach ( $rep_item as $rk => $rv ) {
                        if ( is_array( $rv )
                             && isset( $rv['$$type'] )
                             && 'string' === $rv['$$type']
                             && isset( $rv['value'] )
                             && is_string( $rv['value'] )
                             && '' !== trim( $rv['value'] )
                        ) {
                            $raw_text = $rv['value'];
                            $stripped = wp_strip_all_tags( $raw_text );
                            if ( '' !== trim( $stripped ) ) {
                                $results[] = array(
                                    'widget_type' => $type_label,
                                    'setting'     => "{$setting_key}[$idx].$rk",
                                    'label'       => $rk . ' (v4 repeater)',
                                    'text'        => $raw_text,
                                    'text_plain'  => trim( $stripped ),
                                );
                            }
                        }
                    }
                }
            }

            // Recurse into child elements.
            if ( isset( $element['elements'] ) && is_array( $element['elements'] ) ) {
                $this->extract_elementor_text_nodes( $element['elements'], $results, $depth + 1 );
            }
        }
    }

    /* ================================================================
     * GENERIC HELPERS — builder-agnostic content tools
     * ============================================================= */

    /**
     * Check whether a post-meta key should be hidden from the agent
     * (session tokens, passwords, nonces, etc.).
     */
    private function is_sensitive_meta_key( $meta_key ) {
        static $prefixes = array(
            '_wp_session_', '_transient_', '_site_transient_',
            '_edit_lock', '_edit_last',
        );
        foreach ( $prefixes as $pfx ) {
            if ( 0 === strpos( $meta_key, $pfx ) ) {
                return true;
            }
        }
        static $needles = array( 'secret', 'token', 'password', 'nonce', 'api_key', 'auth_' );
        $lower = strtolower( $meta_key );
        foreach ( $needles as $n ) {
            if ( false !== strpos( $lower, $n ) ) {
                return true;
            }
        }
        return false;
    }

    /**
     * Return a short snippet around the first occurrence of $needle in $haystack.
     * Approximately $radius characters on each side, with "..." if truncated.
     */
    private function snippet_around_match( $haystack, $needle, $radius = 60 ) {
        $pos = mb_stripos( $haystack, $needle );
        if ( false === $pos ) {
            return '';
        }
        $start = max( 0, $pos - $radius );
        $end   = min( mb_strlen( $haystack ), $pos + mb_strlen( $needle ) + $radius );
        $snip  = mb_substr( $haystack, $start, $end - $start );
        if ( $start > 0 ) {
            $snip = '...' . $snip;
        }
        if ( $end < mb_strlen( $haystack ) ) {
            $snip .= '...';
        }
        // Collapse whitespace for readability.
        $snip = preg_replace( '/\s+/', ' ', $snip );
        return $snip;
    }

    /**
     * Generic walker that extracts all non-trivial string values from any
     * JSON-decoded structure.  Used for extract_text mode on arbitrary
     * builder JSON (non-Elementor).
     *
     * Filters out URLs, hex colours, CSS values, tiny strings, and other
     * non-text content so the agent sees only human-readable text.
     */
    private function extract_generic_json_text_nodes( $data, &$results, $path = '', $depth = 0 ) {
        if ( $depth > 50 || ! is_array( $data ) ) {
            return;
        }

        foreach ( $data as $key => $value ) {
            $current_path = '' !== $path ? $path . '.' . $key : (string) $key;

            if ( is_string( $value ) ) {
                $stripped = wp_strip_all_tags( $value );
                // Only meaningful text: >2 chars, not URLs, not hex colours, not CSS units.
                if ( '' !== trim( $stripped )
                     && mb_strlen( trim( $stripped ) ) > 2
                     && ! preg_match( '/^(https?:\/\/|#[0-9a-fA-F]{3,8}$|[0-9a-f]{8}-|rgba?\(|\d+px$|\d+%$|\d+em$|\d+rem$)/i', trim( $value ) )
                ) {
                    $results[] = array(
                        'path'       => $current_path,
                        'key'        => (string) $key,
                        'text'       => $value,
                        'text_plain' => trim( $stripped ),
                    );
                }
            } elseif ( is_array( $value ) ) {
                $this->extract_generic_json_text_nodes( $value, $results, $current_path, $depth + 1 );
            }
        }
    }
}
