<?php namespace Webien\SearchAddon;

use ElementorPro\Modules\ThemeBuilder\Module; // for preview in resultlist

class SearchAddon
{
    private $search_word;
    private $quantity;
    private $variables;
    private $post_types;
    private $templates;
    private $resultType;
    private $offset;
    public $relevanssi_sql;
    public static $relevanssi_hits;
    public static $relevanssi_active;


    function __construct()
    {
        // If running WP CLI, dont mess things up with this plugin.
        // Won't work as expected with some Relevanssi cli commands
        if (defined('WP_CLI') && WP_CLI) {
            return;
        }

        $this->register_hooks();
        $this->is_relevanssi_installed();
    }

    //-------------------------------------------------------------------//
    // Hooks
    //-------------------------------------------------------------------//

    private function register_hooks()
    {
        // Ajax calls
        add_action('wp_ajax_webien_relevanssi_search', [$this, 'ajax_do_search']);
        add_action('wp_ajax_nopriv_webien_relevanssi_search', [$this, 'ajax_do_search']);


        // Relevanssi filters
        add_filter('relevanssi_match', [$this, 'rlv_customfield_boost']);
        add_filter('relevanssi_query_filter', [$this, 'modify_relevanssi_sql'], 10);
        add_filter('relevanssi_modify_wp_query', [$this, 'modify_query_before_relevanssi']);
        add_filter('relevanssi_hits_filter', [$this, 'parse_search_results'], 10);
        add_filter('relevanssi_orderby', [$this, 'rlv_kill_menu_order'], 10);
        add_filter( 'relevanssi_content_to_index', [$this, 'rlv_content_to_index'], 10, 2 );



        add_filter( 'relevanssi_multisite_public_status', '__return_true' ); // Better support for multisite
        add_filter('relevanssi_remove_punctuation', [$this, 'savehyphens_1'], 9); // https://www.relevanssi.com/knowledge-base/words-ampersands-cant-found/
        add_filter('relevanssi_remove_punctuation', [$this, 'savehyphens_2'], 11);
        //add_filter( 'relevanssi_indexing_data', [$this, 'check_indexing_words'], 10, 2 );
        //add_filter( 'relevanssi_fallback', [$this, 'rlv_didyoumean_fallback'] );
        //add_filter( 'relevanssi_get_words_having', function() { return 1; } ); // threshold for did you mean words

        // Elementor hooks
        add_filter( 'elementor_pro/editor/localize_settings', [ $this, 'localize_settings' ], 20, 1 ); // Sets dropdown placeholder to "Standard" instead of "All"

        // Woo commerce
        add_filter( 'woocommerce_redirect_single_search_result', '__return_false' );

        // JS object scripts
        add_action('wp_footer', [$this, 'addJsObjectScript'], 100);

    }


    //-------------------------------------------------------------------//
    // Render
    //-------------------------------------------------------------------//

    // Render input form from settings
    public static function render_search_form( $settingsArr, $echo=true ) {

        // Render like
        // \Webien\SearchAddon\SearchAddon::render_search_form(['post_types'=>'post,page,product'], true);

        // Check settings and set defaults
        $settings = [
            'results_quantity' => isset($settingsArr['results_quantity']) ? $settingsArr['results_quantity'] : 3,
            'post_types' => isset($settingsArr['post_types']) ? $settingsArr['post_types'] : 'post',
            'post_types_templates' => isset($settingsArr['post_types_templates']) ? $settingsArr['post_types_templates'] : [],
            'post_type_more_text' => isset($settingsArr['post_type_more_text']) ? $settingsArr['post_type_more_text'] : '',
            'redirect' => isset($settingsArr['redirect']) ? $settingsArr['redirect'] : '',
            'popup_focus' => isset($settingsArr['popup_focus']) ? $settingsArr['popup_focus'] : '',
            'results_align' => isset($settingsArr['results_align']) ? $settingsArr['results_align'] : '',
            'results_height' => !empty($settingsArr['results_height']),
            'takeover_desktop' => isset($settingsArr['takeover_desktop']) ? $settingsArr['takeover_desktop'] : '',
            'takeover_tablet' => isset($settingsArr['takeover_tablet']) ? $settingsArr['takeover_tablet'] : '',
            'takeover_mobile' => isset($settingsArr['takeover_mobile']) ? $settingsArr['takeover_mobile'] : '',
            'input_placeholder_text' => isset($settingsArr['input_placeholder_text']) ? $settingsArr['input_placeholder_text'] : __('Sök..', 'webien'),
            'button_icon_position' => isset($settingsArr['button_icon_position']) ? $settingsArr['button_icon_position'] : 'left',
            'button_submit_position' => isset($settingsArr['button_submit_position']) ? $settingsArr['button_submit_position'] : 'right',
            'button_icon' => isset($settingsArr['button_icon']) ? $settingsArr['button_icon'] : 'fa fa-search',
            'button_text' => isset($settingsArr['button_text']) ? $settingsArr['button_text'] : '',
            'min_input' => isset($settingsArr['min_input']) ? $settingsArr['min_input'] : 3,
        ];

        if(!$echo) {
            ob_start();
        }

        include(WEBIEN_SEARCH_ADDON_PLUGIN_PATH . '/src/templates/search-input.php');

        if(!$echo) {
            $markup = ob_get_clean();
            return $markup;
        }
    }



    //-------------------------------------------------------------------//
    // Ajax search
    //-------------------------------------------------------------------//

    public function ajax_do_search()
    {
        $this->search_word = urldecode($_GET['value']);
        $this->post_types = $_GET['post_type'] ? explode(',', $_GET['post_type']) : [];
        //$this->templates = isset($_GET['templates']) ? $_GET['templates'] : [];
        $this->variables = isset($_GET['variables']) ? $_GET['variables'] : [];

        $this->quantity = isset($_GET['quantity']) ? (int) $_GET['quantity'] : 20;
        $this->resultType = $_GET['type'];
        $this->offset = isset($_GET['offset']) ? (int) $_GET['offset'] : 0;

        // If post types is any, do a scrape to get all custom post types as well
        if($this->post_types[0] === 'any'){
            $args = ['public' => true, 'exclude_from_search' => false];
            $post_types = get_post_types( $args, 'names', 'and' );
            $this->post_types = array_values($post_types);
        }


        $args = [
            's' => $this->search_word,
            'posts_per_page' => $this->quantity,
            'post_type' => $this->post_types,
            'post_status' => ['publish','inherit'], // "inherit" is to get attachments right
            'offset' => $this->offset
        ];

        $query = new \WP_Query();
        $query->parse_query($args);

        apply_filters( 'relevanssi_modify_wp_query', $query ); // Make sure we can filter query vars from here
        relevanssi_do_query($query);

        // After this the above stated relevanssi hook "relevanssi_hits_filter" will kick in
        // So therae's no need to continue here

        die();

    }

    //-------------------------------------------------------------------//
    // Parse results - will hook on to Relevanssi results and structure them as we want
    //-------------------------------------------------------------------//

    public function parse_search_results($hits){

        // If in Relevanssi admin search, return default results (../wp-admin/index.php?page=relevanssi_admin_search)
        $referer = $_SERVER['HTTP_REFERER'] ?? false;
        if(str_contains($referer, 'relevanssi_admin_search')){
            return $hits;
        }

        $results = $hits[0];
        $searchterms = $hits[1];

/*
        // Order posts by relevance and then by postId, this is so we always get the same order
        usort($results, function ($a, $b): int {
            if ($a->relevance_score === $b->relevance_score) {
                return $a->ID <=> $b->ID;
            }
            return $b->relevance_score <=> $a->relevance_score;
        });
*/

        $post_types = [];
        $total_count = 0;
        $final_results = [];

        // Loop for filtering/sorting and counting post types
        foreach ($results as $post) {

            // Filter option to remove post from result
            if (!apply_filters('webien_search_post_filter', true, $post->ID, $this->variables)) {
                continue;
            }

            $total_count++;
            array_push($final_results, $post);

            $pt = get_post_type_object($post->post_type);

            if (isset($post_types[$pt->label])) {
                $post_types[$pt->label]['amount']++;
            }
            else {
                // Filter option if name needs to change
                $post_types[$pt->label]['title'] = apply_filters( 'webien/search/modify_post_type_title', $pt->label, $pt );

                $post_types[$pt->label]['slug'] = $post->post_type;
                $post_types[$pt->label]['amount'] = 1;
            }
        }

        // If we have results, also add the "All" tab
        if(!empty($post_types)){
            $post_types['any']['title'] = apply_filters( 'webien/search/modify_post_type_title', __('Alla', 'webien'), false );
            $post_types['any']['slug'] = 'any';

            // If Relevanssi throttle is reached add a + to count
            if($total_count === (int)get_option( 'relevanssi_throttle_limit') ){
                $total_count = $total_count.'+';
            }

            $post_types['any']['amount'] = $total_count;
        }

        // Move the all to the front
        $lastvalue = end($post_types);
        $lastkey = key($post_types);

        $arr1 = [$lastkey=>$lastvalue];
        array_pop($post_types);
        $post_types = !empty($post_types) ? array_merge($arr1,$post_types) : [];

        // Filter option if post types need to be edited a last time
        $post_types = apply_filters( 'webien/search/modify_post_type_array', $post_types);

        // Filter results manually, for example by posts protection levels etc.
        $final_results = apply_filters('webien/search/modify_final_results', $final_results);

        // Store results so we can access them elsewhere (Result widget)
        self::$relevanssi_hits = $final_results;

        // Only for Ajax search
        if(defined( 'DOING_AJAX' )) {

            // Return if empty
            if(empty($results) && !defined('WEBIEN_BUILDING_SEARCH_CACHE')){
                    wp_send_json_success([
                    'word' => $this->search_word,
                    'cache' => false,
                    'posts' => [],
                    'post_types' => $post_types
                ]);
            }

            $return_posts = [];
            $posts_to_parse = [];
            $i = 0;

            foreach ($final_results as $post) {
                if ($i >= $this->offset && $i < ($this->offset + $this->quantity)) {
                    array_push($posts_to_parse, floatval($post->ID));
                }
                $i++;
            }

            // Get full posts of the ones need presentation
            $args = [
                'post_status' => ['publish','inherit'], // "inherit" is to get attachments right
                'posts_per_page' => count($posts_to_parse),
                'post__in' => $posts_to_parse,
                'post_type' => $this->post_types,
                'orderby' => 'post__in'
            ];

            $suggestions = new \WP_Query( $args );

            if ( $suggestions->have_posts() ) {
                while ( $suggestions->have_posts() ) {

                    $suggestions->the_post();

                    // Filter possibility to exclude posts.. this is  not a recommended place to exlude content.
                    $save_post = apply_filters('webien/search/post_filter', true, $suggestions->post->ID, $this->variables);

                    if (!$save_post) {
                        continue;
                    }

                    // Get template from settings or option page
                    $templateId = false;
                    if($this->resultType === 'suggestion'){
                        $templateId = get_field('webien_search_template_suggestion_' . $suggestions->post->post_type, 'option') ?: false;
                        $noLink = get_field('webien_search_template_suggestion_leave_link_' . $suggestions->post->post_type, 'option') ?: false;
                    } else if($this->resultType === 'resultlist'){
                        $templateId = get_field('webien_search_template_' . $suggestions->post->post_type, 'option') ?: false;
                        $noLink = get_field('webien_search_template_leave_link_' . $suggestions->post->post_type, 'option') ?: false;
                    }

                    // Try to load template
                    if ($templateId) {
                        $template = \Elementor\Plugin::instance()->elements_manager->create_element_instance(
                            [
                                'elType' => 'widget',
                                'widgetType' => 'template',
                                'id' => 'template',
                                'settings' => [
                                    'template_id' => $templateId
                                ],
                            ],
                            []
                        );


                        ob_start();
                        $template->print_element();

                        $markup = ob_get_clean();

                        if(!$noLink){
                            $markup = str_replace('<a ', '<span ', $markup);
                            $markup = str_replace('</a>', '</span>', $markup);
                        }

                        $post_html = $markup;
                    }
                    else {
                        // If we could not load template then use standard markup
                        $post_html = '<div class="standard-markup '.$suggestions->post->post_type.'">';
                        $post_html .= '<h6>' . $suggestions->post->post_title . '</h6>';
                        $post_html .= '<p>' . $suggestions->post->post_excerpt . '</p>';
                        $post_html .= '</div>';
                        $post_html = apply_filters('webien/search/standard_markup_suggestion', $post_html, $suggestions->post, $this->search_word);
                    }

                    // Highlight
                    //$post_html = $this->highlight($post_html, $this->search_word);
                    if(!$noLink){
                        $link = get_the_permalink($suggestions->post->ID);
                    } else {
                        $link = false;
                    }

                    $return_posts[] = ['link' => $link, 'html' => $post_html];
                }

                wp_reset_postdata();
            }

            // Here's a chance to add results that for some reason cant get added from the other methods
            $return_posts = apply_filters( 'webien/search/final_suggestions', $return_posts, $this->search_word, $post_types, $posts_to_parse );


            // Try to cache result if suggestion
            if(($this->resultType === 'suggestion' && $this->offset === 0 && empty($this->variables)) ||
                (defined('WEBIEN_BUILDING_SEARCH_CACHE'))
            ){
                \Webien\SearchAddon\Cache::add_entry(
                    $this->search_word,
                    json_encode([
                        'success' => true,
                        'data' => [
                            'word' => $this->search_word,
                            'cache' => true,
                            'posts' => $return_posts,
                            'post_types' => $post_types
                        ]
                    ]),
                    $this->post_types
                );
            }

            // If logging is enabled, log search if conditions arer matched
            if($this->resultType === 'suggestion' && $this->offset === 0 && !defined('WEBIEN_BUILDING_SEARCH_CACHE')){
                $update_log = get_option( 'relevanssi_log_queries' );
                if ( 'on' === $update_log ) {
                    relevanssi_update_log( $this->search_word, count($return_posts) );
                }
            }


            if(!defined('WEBIEN_BUILDING_SEARCH_CACHE')) { // Dont return here if bulk caching
                wp_send_json_success([
                    'word' => $this->search_word,
                    'cache' => false,
                    'posts' => $return_posts,
                    'post_types' => $post_types
                ]);
            }
        }

        return $hits;
    }




    //-------------------------------------------------------------------//
    // Elementor stuff
    //-------------------------------------------------------------------//

    public function localize_settings( $settings ) {
        $settings = array_replace_recursive( $settings, [
            'i18n' => [
                'all' => __( 'Standard', 'webien' ),
            ],
        ] );
        return $settings;
    }




    //-------------------------------------------------------------------//
    // Misc Relevanssi stuff
    //-------------------------------------------------------------------//

    // Alter Relevanssi SQL to work with our URL parameters etc
    public function modify_relevanssi_sql($sql){

        // Make Relevanssi search all post types even if we have defined only one. This is so we can count posts by post types.
        if(!defined( 'DOING_AJAX' ) && empty($_GET['webien_pt'])) {
            $sql = preg_replace('/(posts.post_type NOT IN \(\'.*\'\))/', 'posts.post_type != ""', $sql);
        }
        if(defined( 'DOING_AJAX' )) {
            // Strip the "any" post type
            if (($this->post_types[0] === 'any')) {
                $sql = preg_replace('/(posts.post_type NOT IN \(\'.*\'\))/', 'posts.post_type != ""', $sql);
            }
        }
        
        return $sql;
    }

    // force sort by relevance if something is trying to block that
    function rlv_kill_menu_order($orderby) {
        return "relevance";
    }

    // Add/remove some content to index
    function rlv_content_to_index( $content, $post ) {
        // Add filenames to index if option is active
        if ( 'attachment' === $post->post_type && get_field('webien_search_index_filenames', 'option')) {
            $content .= ' ' . basename( $post->guid );
        }
        return $content;
    }

    // Check for some url params and update query
    function modify_query_before_relevanssi($query){

        if(isset($_GET['webien_pt']) && $_GET['webien_pt'] !== ''){
            $post_types = sanitize_text_field(wp_unslash($_GET['webien_pt']));
            $post_types = array_filter(array_map('trim', explode(',', $post_types)));

            if(!empty($post_types)){
                $query->set( 'post_types', $post_types );
                $query->set( 'post_type', $post_types );
            }
        }

        if(isset($_GET['pt'])){
            $post_type = sanitize_text_field(wp_unslash($_GET['pt']));

            if(empty($_GET['webien_pt']) && $post_type !== 'any'){
                $query->set( 'post_types', $post_type );
                $query->set( 'post_type', $post_type );
            }
        }

        // Add variables to query
        $variables = isset($_GET['var']) ? $_GET['var'] : [];
        if(!empty($variables)){
            $variables = explode('|', $variables);
            foreach($variables as $param){
                $arr = explode('=', $param);
                $this->variables[$arr[0]] = $arr[1];
            };
        }

        // Filter query vars from here
        $query = apply_filters( 'webien/search/modify_query', $query, $this->variables );

        return $query;
    }



    // Find match and highlight
    function highlight($text, $words) {
        return $text;
        preg_match_all('~\w+~iu', $words, $m);
        if(!$m)
            return $text;
        //$re = '~(' . implode('|', $m[0]) . ')~i';
        $return = preg_replace($re, '<span class="match">$0</span>', $text);
        return $return;
    }


    public function get_relevanssi_post_types(){
        $relevanssi_post_types = get_option('relevanssi_index_post_types');
        if (($key = array_search('bogus', $relevanssi_post_types)) !== false) {
            unset($relevanssi_post_types[$key]);
        }
        return $relevanssi_post_types;
    }

    // Helper function to check if relevanssi is active
    public function is_relevanssi_installed()
    {
        $is_active = !empty(
        array_intersect( ['relevanssi/relevanssi.php','relevanssi-premium/relevanssi.php'],
            apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) )
        );
        self::$relevanssi_active = $is_active;
    }

    function savehyphens_1($a) {
        $a = str_replace('-', 'HYPHEN', $a);
        $a = str_replace('–', 'HYPHEN', $a);
        $a = str_replace('—', 'HYPHEN', $a);
        return $a;
    }
    function savehyphens_2($a) {
        $a = str_replace('HYPHEN', '-', $a);
        return $a;
    }


    function rlv_customfield_boost( $match ) {
        $val = get_field('webien_search_custom_field_boost', 'option') ?: 1;
        if ( $match->customfield > 0 ) {
            $match->weight *= (int)$val;
        }
        return $match;
    }


    function addJsObjectScript()
    { ?>
    <script>
        const useWebienSearchToJs = <?= json_encode(apply_filters( 'webien/search/suggestions_to_js', false)); ?>;
        const webienSearchSuggestions = {
            results: {},
            status: {}
        };
    </script>
        <?php
    }



}
