<?php
/**
 * Spellmatic_ContentExtractor
 *
 * Detects which page builder (if any) is active on a post and extracts clean
 * plain text from the correct location so Harper can lint it.
 *
 * Supported builders (Phase 3):
 *   standard   — plain post_content + post_title (Gutenberg, Classic Editor, Kadence)
 *   elementor  — _elementor_data JSON (recursive widget traversal)
 *   wpbakery   — [vc_*] shortcodes in post_content
 *   divi       — [et_pb_*] shortcodes in post_content
 *   avada      — [fusion_*] shortcodes in post_content
 *
 * Post-launch (Phase 4):
 *   beaver     — _fl_builder_data serialised post_meta
 *   bricks     — _bricks_page_content_2 JSON post_meta
 *   oxygen     — ct_builder_shortcodes JSON post_meta
 *
 * Each builder returns an array of "items" — same shape as scan_start() uses:
 *   [ id, post_id, post_type, field, text ]
 *
 * The `field` value controls how class-fixer.php writes the correction back:
 *   post_title   → update post_title
 *   post_content → str_replace in post_content
 *   elementor    → str_replace in _elementor_data JSON, clear Elementor CSS cache
 */

defined( 'ABSPATH' ) || exit;

class Spellmatic_ContentExtractor {

    const ELEMENTOR_TEXT_KEYS = [
        'editor', 'title', 'description', 'caption', 'text', 'button_text',
        'heading', 'sub_heading', 'content', 'inner_text', 'label',
        'placeholder', 'tab_title', 'tab_content', 'slide_content',
    ];

    public static function get_items( WP_Post $post ): array {
        $builder = self::detect_builder( $post );
        $items = [];

        switch ( $builder ) {
            case 'elementor': $items = self::extract_elementor( $post ); break;
            case 'wpbakery':  $items = self::extract_shortcode_builder( $post, 'wpbakery' ); break;
            case 'divi':      $items = self::extract_shortcode_builder( $post, 'divi' ); break;
            case 'avada':     $items = self::extract_shortcode_builder( $post, 'avada' ); break;
            default:          $items = self::extract_standard( $post ); break;
        }

        // Add WooCommerce product-specific fields after standard/builder extraction.
        if ( 'product' === $post->post_type ) {
            $items = array_merge( $items, self::extract_woocommerce( $post ) );
        }

        return $items;
    }

    public static function detect_builder( WP_Post $post ): string {
        $meta_keys = array_keys( get_post_meta( $post->ID ) ?: [] );

        if ( in_array( '_elementor_data', $meta_keys, true ) ) {
            $raw = get_post_meta( $post->ID, '_elementor_data', true );
            if ( ! empty( $raw ) && $raw !== '[]' && $raw !== '' ) return 'elementor';
        }

        if ( in_array( '_fl_builder_data', $meta_keys, true ) )       return 'beaver';
        if ( in_array( '_bricks_page_content_2', $meta_keys, true ) ) return 'bricks';
        if ( in_array( 'ct_builder_shortcodes', $meta_keys, true ) )  return 'oxygen';

        $content = $post->post_content ?? '';
        if ( strpos( $content, '[vc_' )     !== false ) return 'wpbakery';
        if ( strpos( $content, '[et_pb_' )  !== false ) return 'divi';
        if ( strpos( $content, '[fusion_' ) !== false ) return 'avada';

        return 'standard';
    }

    private static function extract_standard( WP_Post $post ): array {
        $items = [];
        $title = trim( $post->post_title ?? '' );
        if ( $title !== '' ) $items[] = self::make_item( $post, 'post_title', $title );

        $text = self::clean_html( $post->post_content ?? '' );
        if ( $text !== '' ) $items[] = self::make_item( $post, 'post_content', $text );

        return $items;
    }

    private static function extract_elementor( WP_Post $post ): array {
        $items = [];
        $title = trim( $post->post_title ?? '' );
        if ( $title !== '' ) $items[] = self::make_item( $post, 'post_title', $title );

        $raw = get_post_meta( $post->ID, '_elementor_data', true );
        if ( empty( $raw ) ) return $items;

        // Try decoding the raw JSON first (preserves Unicode escapes like \u2014).
        // Fall back to wp_unslash() only if the raw string has WordPress-added slashes
        // that prevent json_decode from parsing it.
        $data = json_decode( $raw, true );
        if ( ! is_array( $data ) ) {
            $data = json_decode( wp_unslash( $raw ), true );
        }
        if ( ! is_array( $data ) ) return $items;

        // One item per widget text field — gives accurate offsets and widget-type labels.
        self::recurse_elementor_items( $post, $data, $items );

        return $items;
    }

    /**
     * Recurse the Elementor element tree and produce one scan item per widget text field.
     * Field: 'elementor_' + sanitize_key( widgetType )
     *   e.g. heading → 'elementor_heading'
     *        text-editor → 'elementor_text-editor'
     */
    private static function recurse_elementor_items( WP_Post $post, array $elements, array &$items ): void {
        foreach ( $elements as $el ) {
            if ( ! is_array( $el ) ) continue;

            $widget_type = $el['widgetType'] ?? $el['elType'] ?? 'widget';
            $el_id       = $el['id'] ?? ( 'w' . count( $items ) );

            if ( isset( $el['settings'] ) && is_array( $el['settings'] ) ) {
                foreach ( self::ELEMENTOR_TEXT_KEYS as $settings_key ) {
                    if ( empty( $el['settings'][ $settings_key ] ) || ! is_string( $el['settings'][ $settings_key ] ) ) continue;
                    $t = self::clean_html( $el['settings'][ $settings_key ] );
                    if ( $t === '' ) continue;

                    $items[] = [
                        'id'        => $post->ID . '_el_' . $el_id . '_' . $settings_key,
                        'post_id'   => $post->ID,
                        'post_type' => $post->post_type,
                        'field'     => 'elementor_' . sanitize_key( $widget_type ),
                        'text'      => $t,
                    ];
                }
            }

            if ( ! empty( $el['elements'] ) && is_array( $el['elements'] ) ) {
                self::recurse_elementor_items( $post, $el['elements'], $items );
            }
        }
    }

    private static function extract_shortcode_builder( WP_Post $post, string $builder ): array {
        $items = [];
        $title = trim( $post->post_title ?? '' );
        if ( $title !== '' ) $items[] = self::make_item( $post, 'post_title', $title );

        $text = self::strip_shortcode_tags( $post->post_content ?? '' );
        $text = self::clean_html( $text );
        // Replace non-breaking spaces and other unicode whitespace before collapsing.
        $text = preg_replace( '/[\x{00A0}\x{200B}\x{FEFF}]/u', ' ', $text );
        $text = trim( preg_replace( '/\s{2,}/', ' ', $text ) );
        if ( $text !== '' ) $items[] = self::make_item( $post, 'post_content', $text );

        return $items;
    }

    private static function extract_woocommerce( WP_Post $post ): array {
        $items = [];

        // Extract short description (post_excerpt).
        $short_desc = trim( $post->post_excerpt ?? '' );
        if ( $short_desc !== '' ) {
            $text = self::clean_html( $short_desc );
            if ( $text !== '' ) {
                $items[] = self::make_item( $post, 'short_description', $text );
            }
        }

        // Extract purchase note from post meta.
        $purchase_note = get_post_meta( $post->ID, '_purchase_note', true );
        if ( ! empty( $purchase_note ) ) {
            $text = self::clean_html( $purchase_note );
            if ( $text !== '' ) {
                $items[] = self::make_item( $post, 'purchase_note', $text );
            }
        }

        return $items;
    }

    public static function strip_shortcode_tags( string $content ): string {
        $content = preg_replace( '/\[[a-zA-Z_\-][^\]]*?\/\s*\]/', ' ', $content );
        $content = preg_replace( '/\[\/[a-zA-Z_\-][^\]]*\]/', ' ', $content );
        $content = preg_replace( '/\[[a-zA-Z_\-][^\]]*\]/', ' ', $content );
        return $content;
    }

    private static function clean_html( string $html ): string {
        $text = wp_strip_all_tags( $html );
        return trim( html_entity_decode( $text, ENT_QUOTES | ENT_HTML5, 'UTF-8' ) );
    }

    private static function make_item( WP_Post $post, string $field, string $text ): array {
        return [
            'id'        => $post->ID . '_' . $field,
            'post_id'   => $post->ID,
            'post_type' => $post->post_type,
            'field'     => $field,
            'text'      => $text,
        ];
    }
}
