<?php namespace Webien\SearchAddon;
use Aws\S3\S3Client;
use Aws\S3\Exception\S3Exception;

class Cache
{
    private static $active;
    public static $searchWord;

    public static function register()
    {
        self::$active = get_field('webien_search_activate', 'option');

        // Add global js variables
        if(self::$active){
            add_action('wp_head', function(){
                $dir = wp_get_upload_dir();
                $active_folder = get_option('webien_search_cache_active_folder');
                $active_folder = $active_folder ? '/'.$active_folder.'/' : '/';
                $base_url = $dir['baseurl'] . '/webien/webien-search-cache' . $active_folder;

                if( defined('S3_UPLOADS_BUCKET') ){
                    $base_url = apply_filters('webien/search/cdn_url', 'https://s3.eu-north-1.amazonaws.com/' . S3_UPLOADS_BUCKET .  '/uploads/webien/webien-search-cache' . $active_folder);
                }

                $use_only_cache_results = apply_filters('webien/search/cached_suggestions_only', false);

                echo '<script>'.PHP_EOL;
                echo 'window.webienSearchCache="'. $base_url .'";'.PHP_EOL;
                echo 'window.webienSearchCacheFolder="'. $active_folder .'";'.PHP_EOL;
                if( defined('S3_UPLOADS_BUCKET') ){
                    echo 'window.webienSearchCacheS3=true;'.PHP_EOL;
                }
                if( $use_only_cache_results ){
                    echo 'window.cachedSuggestionsOnly=true;'.PHP_EOL;
                }
                echo '</script>';
            });
        }

        add_filter( 'relevanssi_indexing_data', [__CLASS__, 'clear_cache'], 10, 2 );

        add_filter( 'relevanssi_pre_indexing_query', [__CLASS__, 'remove_all_entries']); // Clear all cache when doing a Relevanssi re-indexing

        add_action('wp_ajax_webien_build_cache', [__CLASS__, 'build_cache'], 10);
        add_action('wp_ajax_nopriv_webien_build_cache', [__CLASS__, 'build_cache'], 10);

        add_action('wp_ajax_webien_clear_all_cache', [__CLASS__, 'remove_all_entries'], 10);
        add_action('wp_ajax_nopriv_webien_clear_all_cache', [__CLASS__, 'remove_all_entries'], 10);

    }

    // Setup database table (Called on plugin activation)
    static function setup_resources()
    {
        // Add database table
        global $wpdb;
        //$charset_collate = $wpdb->get_charset_collate();
        $charset_collate = 'CHARACTER SET utf8mb4 COLLATE utf8mb4_swedish_ci'; // Need to be like this to differentiate åäö searches
        $table_name = $wpdb->prefix.'webien_search_cache';

        $sql = "CREATE TABLE $table_name (
            `id` mediumint(9) NOT NULL AUTO_INCREMENT,
            `value` tinytext NOT NULL,
            `post_types` text NOT NULL,
            `filename` text NOT NULL,
            PRIMARY KEY  (`id`)
            ) $charset_collate;";

        require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
        dbDelta( $sql );

        // Update coallition of affected tables to work better with åäö searches
        $relevanssi_table = $wpdb->prefix.'relevanssi';
        $wpdb->query("ALTER TABLE $relevanssi_table CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_swedish_ci");
        $wpdb->query("ALTER TABLE $table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_swedish_ci");

        // Create cache folder in uploads
        $upload_dir = wp_upload_dir();
        if(!empty( $upload_dir['basedir'] )){
            $mainFolder = $upload_dir['basedir'].'/webien';
            if ( ! file_exists( $mainFolder ) ) {
                wp_mkdir_p( $mainFolder );
            }
            $cacheFolder = $mainFolder.'/webien-search-cache';
            if ( ! file_exists( $cacheFolder ) ) {
                wp_mkdir_p( $cacheFolder );
            }
            $defaultJson = $cacheFolder. '/default.json';
            if ( ! file_exists( $defaultJson ) ) {
                $json = '{"success":true,"data":{"posts":[],"post_types":[]}}';
                $fp = fopen($defaultJson, 'w');
                fwrite($fp, $json);
                fclose($fp);
            }
        }
    }

    // Add new entry to database
    public static function add_entry($value,$result,$post_types)
    {
        if(!self::$active || empty($value)){
            return;
        }

        global $wpdb;
        $table_name = $wpdb->prefix . 'webien_search_cache';

        // order post types by name to allways get correct filenames
        sort($post_types);
        $pt = implode(',', $post_types);

        // We don't want duplicates, check if entry exists first in DB
        $exists = $wpdb->get_var( "SELECT id FROM $table_name WHERE value = '$value' AND post_types='$pt'" );

        if($exists !== NULL){
            return;
        }

        // Create json file with result
        $dir = wp_get_upload_dir();
        $sitename = preg_replace('/^www\./','',$_SERVER['HTTP_HOST']);
        $filename = base64_encode($value.'___'.$pt.'___'.$sitename);

        // Base64 can include slashes which will mess up paths..
        // We replace them here and also when we try to find the file
        $filename = str_replace('/', '-', $filename);

        $active_folder = get_option('webien_search_cache_active_folder');
        $active_folder = $active_folder ? '/'.$active_folder.'/' : '/';

        $fp = fopen($dir['basedir'].'/webien/webien-search-cache'.$active_folder.$filename.'.json', 'w');
        $fp_res = fwrite($fp, $result);
        fclose($fp);

        // If no default json, create it
        $defaultJson = $dir['basedir'].'/webien/webien-search-cache'.$active_folder.'default.json';
        if ( ! file_exists( $defaultJson ) ) {
            $json = '{"success":true,"data":{"posts":[],"post_types":[]}}';
            $fp = fopen($defaultJson, 'w');
            fwrite($fp, $json);
            fclose($fp);
        }

        // On file write success, add to to database table index
        if($fp_res){

            $wpdb->insert(
                $table_name,
                [
                    'value'     => $value,
                    'post_types'=> $pt,
                    'filename'  => $filename
                ]
            );
        }

    }

    // Search entry from database
    public static function get_entry($value, $post_types)
    {
        global $wpdb;
        $table_name = $wpdb->prefix . 'webien_search_cache';

        sort($post_types);
        $pt = implode(',', $post_types);

        $result = $wpdb->get_var( "SELECT id FROM $table_name WHERE value = '$value' AND post_types='$pt'" );

        // Returns string/NULL
        return $result;
    }


    // Remove specific saved entries
    public static function remove_some_entries($wordArr)
    {
        // Clear DB entries
        global $wpdb;
        $table_name = $wpdb->prefix . 'webien_search_cache';
        $words = implode("','",$wordArr);



        // Parse words and lookup filenames etc before deletion
        $results = $wpdb->get_results(
            $wpdb->prepare("SELECT filename FROM $table_name WHERE value IN ('".$words."')")
        );
        $files = [];

        foreach ($results as $res) {
            array_push($files, $res->filename.'.json');
        }

        $wpdb->query("DELETE FROM $table_name WHERE value IN ('".$words."')");

        $active_folder = get_option('webien_search_cache_active_folder') ? '/' . get_option('webien_search_cache_active_folder') . '/' : '/';

        // If uploads are on S3
        if( defined('S3_UPLOADS_BUCKET') ){

            $app = explode('/', S3_UPLOADS_BUCKET, 2);
            $bucket = $app[0];
            $folder = $app[1] . '/uploads/webien/webien-search-cache' . $active_folder;

            self::clear_cache_s3($bucket, $folder, $files);
        } else {
            // Clear local files if not S3
            $dir = wp_get_upload_dir();
            $existingFiles = glob($dir['basedir'] . '/webien/webien-search-cache' . $active_folder . '*');
            foreach($existingFiles as $file){
                if(basename($file) === 'default.json' || !in_array(basename($file), $files)){
                    continue;
                }
                if(is_file($file)) {
                    unlink($file);
                }
            }
        }

    }

    // Remove all saved entries
    public static function remove_all_entries()
    {
        // Clear DB entries
        global $wpdb;
        $table_name = $wpdb->prefix . 'webien_search_cache';
        $wpdb->query("TRUNCATE TABLE $table_name");

        $oldFolder = get_option( 'webien_search_cache_active_folder');
        $newFolder = $oldFolder ? floatval($oldFolder)+1 : 1;

        // Create new active folder
        $upload_dir = wp_upload_dir();
        if(!empty( $upload_dir['basedir'] )){
            $mainFolder = $upload_dir['basedir'].'/webien';
            if ( ! file_exists( $mainFolder ) ) {
                wp_mkdir_p( $mainFolder );
            }
            $cacheFolder = $mainFolder.'/webien-search-cache';
            if ( ! file_exists( $cacheFolder ) ) {
                wp_mkdir_p( $cacheFolder );
            }
            $activeFolder = $cacheFolder . '/' . $newFolder;
            if ( ! file_exists( $activeFolder ) ) {
                wp_mkdir_p( $activeFolder );
            }
            $defaultJson = $activeFolder. '/default.json';
            if ( ! file_exists( $defaultJson ) ) {
                $json = '{"success":true,"data":{"posts":[],"post_types":[]}}';
                $fp = fopen($defaultJson, 'w');
                fwrite($fp, $json);
                fclose($fp);
            }
        }

        update_option( 'webien_search_cache_active_folder', $newFolder );

        // If local, remove old folder
        if( !defined('S3_UPLOADS_BUCKET') ){
            $dir = wp_get_upload_dir();

            $oldFolder = $oldFolder ? '/'.$oldFolder.'/' : '/';
            $existingFiles = glob($dir['basedir'] . '/webien/webien-search-cache' . $oldFolder . '*');
            foreach($existingFiles as $file){
                if(is_file($file)) {
                    unlink($file);
                }
            }
            if ($oldFolder !== '/' && is_dir($dir['basedir'] . '/webien/webien-search-cache' . $oldFolder)) {
                rmdir($dir['basedir'] . '/webien/webien-search-cache' . $oldFolder);
            }
        }

        // Clear cache
        self::clearCache();

    }


    public static function clear_cache( $data, $post ) {

        // $data holds all words Relevanssi ig going to update index with
        // Look for those words in our cache table and clear them if they exists

        // If we are in admin rebuilding the entire index, dont clear any cache..
        if(isset($_SERVER['HTTP_REFERER']) && (strpos($_SERVER['HTTP_REFERER'], 'relevanssi.php&tab=indexing') !== false)){
            return $data;
        }

        $wordArr = [];
        foreach($data as $word=>$arr){
            array_push($wordArr, $word);
        }

        // Get partial words and add to array
        $subArr = [];
        foreach($wordArr as $word) {
            $arr = preg_split('//u', $word, -1, PREG_SPLIT_NO_EMPTY); // Split words to characters with expression for åäö etc
            $i = 0;
            $subWord = '';
            foreach ($arr as $char) {
                $i++;
                $subWord .= $char;
                if($i > 2){
                    array_push($subArr, $subWord);
                }
            }
        };


        $allWords = array_merge($wordArr, $subArr);
        $allWords = array_unique($allWords);

        global $wpdb;
        $table_name = $wpdb->prefix . 'webien_search_cache';
        $words = implode("','",$allWords);

        $results = $wpdb->get_results(
            $wpdb->prepare("SELECT value FROM $table_name WHERE value IN ('".$words."')")
        );


        $clearEntries = [];
        foreach($results as $res){
            array_push($clearEntries, $res->value);
        }

        if(!empty($clearEntries)){
            self::remove_some_entries($clearEntries);
        }

        return $data;
    }


    private static function clear_cache_s3($bucket,$folder,$files){

        // Set credentials from env file
        $credentials = [
            'key' => isset($_ENV['AWS_KEY']) ? $_ENV['AWS_KEY'] : false,
            'secret' => isset($_ENV['AWS_SECRET']) ? $_ENV['AWS_SECRET'] : false,
            'region' => 'eu-north-1'
        ];

        $credentials = apply_filters('webien/search/aws_credentials', $credentials);

        $s3 = new S3Client([
            'region' => $credentials['region'],
            'version' => 'latest',
            'credentials' => [
                'key'    => $credentials['key'],
                'secret' => $credentials['secret'],
            ],
        ]);

        $keys = $s3->listObjects([
            'Bucket' => $bucket,
            "Prefix" => $folder,
            "MaxKeys" => 999999,
        ]);

        if(isset($keys['Contents']) && !empty($keys['Contents'])){
            foreach ($keys['Contents'] as $key)
            {
                // if only some entries should be deleted..
                if($files && !in_array(basename($key['Key']), $files)){
                    continue;
                }

                // Leave default.json
                if(basename($key['Key']) === 'default.json'){
                    continue;
                }

                $s3->deleteObjects([
                    'Bucket'  => $bucket,
                    'Delete' => [
                        'Objects' => [
                            [
                                'Key' => $key['Key']
                            ]
                        ]
                    ]
                ]);
            }
        }
    }


    public static function build_cache()
    {

        if(!self::$active){
            wp_send_json_success(['activate'=> true]);
        }


        $offset = isset($_GET['offset']) ? (int) $_GET['offset'] : 0;
        $first = isset($_GET['first']) && ($_GET['first'] === 'true') ? true : false;
        $total = isset($_GET['total']) ? (int) $_GET['total'] : 0;

        if(!defined('WEBIEN_BUILDING_SEARCH_CACHE')){
            define('WEBIEN_BUILDING_SEARCH_CACHE', true);
        }

        global $wpdb;
        $table_relevanssi = $wpdb->prefix . 'relevanssi';
        $table_webien = $wpdb->prefix . 'webien_search_cache';

        $num = get_field('webien_search_cache_threshold', 'option');

        // Clear all cache first
        if($first === true){
            self::remove_all_entries();

            $sql = "SELECT COUNT(term) FROM
            (
                SELECT term, COUNT(term) count
                  FROM $table_relevanssi
                 GROUP BY term
                HAVING COUNT(term) >=$num
            ) d1";

            $total_words = $wpdb->get_var( $sql );

            $total_cached = $wpdb->get_var( "SELECT COUNT(*) FROM $table_webien" );

            // This build will check where we are in the index and continue from there..
            // To prevent clearing the entire cache every time

            wp_send_json_success([
                'isfirst'   => true,
                'total' => $total_words,
                'nextoffset' => $total_cached,
                'msg'   => __('Hittade '. number_format($total_words, 0, ',', ' ') . ' ord. Startar från: '.number_format($total_cached, 0, ',', ' ') ,'webien')
            ]);
        }

        // Get words to generate search results for
        $limit =  apply_filters('webien/search/queries_per_request', 2); // Search queries per ajax request
        $sql = "SELECT term
            FROM $table_relevanssi 
            GROUP BY term
            HAVING COUNT(term) >=$num
            LIMIT $limit
            OFFSET $offset";

        $results = $wpdb->get_results(
            $wpdb->prepare($sql)
        );

        $cache_partial_words = apply_filters('webien/search/build_cache_partial_words', false);

        $mainArr = [];
        foreach($results as $word) {
            // Create sub version on word if activated
            if($cache_partial_words){
                $arr = str_split($word->term);

                $i = 0;
                $subWord = '';
                foreach ($arr as $char) {
                    $i++;
                    $subWord .= $char;
                    if($i > 2){
                        array_push($mainArr, $subWord);
                    }
                }
            } else {
                // Else just add the full word
                array_push($mainArr, $word->term);
            }
        };

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

        $result_count = get_field('webien_search_cache_result_count', 'option');


        foreach($mainArr as $word){

            $args = [
                's' => $word,
                'posts_per_page' => $result_count,
                'post_type' => $relevanssi_post_types,
                'post_status' => 'publish',
            ];

            self::$searchWord = $word;

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

            relevanssi_do_query($query);
            // After this the relevanssi hook "relevanssi_query_filter" will kick in, optimize the sql and return a result
            // So therae's no need to continue here
        }

        $nextOffset = $offset+$limit;

        if($nextOffset >= $total){
            $islast = true;
        } else {
            $islast = false;
        }

        wp_send_json_success([
            'isfirst'   => false,
            'islast'   => $islast,
            'nextoffset' => $nextOffset,
            'msg'   => $nextOffset . ' '. __('ord indexerade..' ,'webien')
        ]);

    }


    public static function clearCache(){
        if(is_plugin_active('nginx-helper/nginx-helper.php')){
            global $nginx_purger;
            $nginx_purger->purge_all();
        }
    }


}