<?php 

    // prevent direct file access
	if( !defined( 'ABSPATH' ) ) exit;
    
    // require necessary function
    function pixe_builder_table_init(){
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');

        global $wpdb;
        $charset_collate = $wpdb->get_charset_collate();

        // pixe_id : post_id
        $sql = "CREATE TABLE {$wpdb->prefix}pixebuilder (
            id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
            pixe_id bigint(20) UNSIGNED DEFAULT NULL,
            post_author bigint(20) UNSIGNED DEFAULT NULL,
            post_date_utc datetime DEFAULT NULL,
            post_content longtext,
            settings longtext,
            PRIMARY KEY (id)
        ) {$charset_collate};";
        dbDelta($sql);
    }

    // get object cache key
    function pixe_post_get_cache_key($args){
        $key = 'pixepost.v' . pixe_get_cache_version('post');

        if( !empty($args['select']) ){
            $key .= '|' . trim($args['select']);
        }
        if( !empty($args['id']) ){
            $key .= '|' . absint($args['id']);
        }
        if( !empty($args['ids']) && is_array($args['ids']) ){
            sort($args['ids']);
            $key .= '|' . implode(',', $args['ids']);
        }
        if( !empty($args['pixe_id']) ){
            $key .= '|p' . absint($args['pixe_id']);
        }
        if( !empty($args['order']) ){
            $key .= '|' . trim($args['order']);
        }

        return $key;
    }

    // use to insert new data
    function pixe_set_builder_data($data){
        global $wpdb;

        $id = '';
        if( !empty($data['id']) ){
            $id = $data['id'];
            unset($data['id']);
        }

        // create data format
        $formats = array();
        foreach( $data as $slug => $value ){

            // prepare json data
            if( is_array($value) ){
                $data[$slug] = json_encode($value);
            }
            
            // set format
            if( in_array($slug, array('pixe_id', 'post_author')) ){
                $formats[] = '%d';
            }else{
                $formats[] = '%s';
            }
        }

        // insert data
        if( !empty($id) ){
            $wpdb->update("{$wpdb->prefix}pixebuilder", $data, array('id' => $id), $formats, array('%d')); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Cache automatically invalidated on data changes
        }else{
            $wpdb->insert("{$wpdb->prefix}pixebuilder", $data, $formats); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
        }

        // delete cache after update
        pixe_increase_cache_version('post');

        return $wpdb->insert_id;
    }
    
    // get revision data
    function pixe_get_revision_data($id, $pixe_id){
        if( empty($id) || empty($pixe_id) ) return '';

        // get result from cache first
        $cache_key = pixe_post_get_cache_key(array('id' => $id, 'pixe_id' => $pixe_id));
        $result = wp_cache_get($cache_key, 'pixebuilder');
        if( !empty($result) ) return $result;

        // no cache, query from database
        global $wpdb;
        $result = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}pixebuilder WHERE id = %d AND pixe_id = %d", $id, $pixe_id)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
        wp_cache_set($cache_key, $result, 'pixebuilder', DAY_IN_SECONDS);

        return $result;
    }

    // save builder status
    add_action('wp_ajax_pixe_save_builder_status', 'pixe_save_builder_status');
    function pixe_save_builder_status(){

        // check nonce
        if( !check_ajax_referer('pixe-editor', 'security', false) ){
            die(json_encode(array(
                'status' => 'failed',
                'head' => pixe_get_error_message('nonce-head'),
                'message'=> pixe_get_error_message('nonce-message')
            )));
        }

        // check permission
        if( !current_user_can('manage_options') ){
            die(json_encode(array(
                'status' => 'failed',
                'head' => pixe_get_error_message('permission-head'),
                'message'=> pixe_get_error_message('permission-message')
            )));
        }

        // check data
        if( empty($_POST['builder-status']) ){
            die(json_encode(array(
                'status' => 'failed',
                'head' => esc_html__('Invalid Data', 'pixebuilder'),
                'message'=> esc_html__('Please refresh the page and try again.' ,'pixebuilder')
            )));
        }

        // store data per data type
        $status = map_deep(wp_unslash($_POST['builder-status']), 'sanitize_text_field');
        update_option('pixe-builder-status', $status, false);

        // return data
        die(json_encode(array(
            'status' => 'success'
        )));
    }
            
    // save post editor
    function pixe_save_post_editor(){

        // check nonce
        if( !check_ajax_referer('pixe-editor', 'security', false) ){
            die(json_encode(array(
                'status' => 'failed',
                'head' => pixe_get_error_message('nonce-head'),
                'message'=> pixe_get_error_message('nonce-message')
            )));
        }

        // check data
        if( empty($_POST['id']) ){
            die(json_encode(array(
                'status' => 'failed',
                'head' => pixe_get_error_message('invalid-format-head'),
                'message'=> pixe_get_error_message('invalid-format-message')
            )));
        }

        $currentuser = wp_get_current_user();
        $currenttime = current_time('mysql', true);
        $deleted_revision = '';

        $id = absint(wp_unslash($_POST['id']));

        // check permission
        if( !current_user_can('edit_post', $id) ){
            die(json_encode(array(
                'status' => 'failed',
                'head' => pixe_get_error_message('permission-head'),
                'message'=> pixe_get_error_message('permission-message')
            )));
        }

        // builder data
        $content = empty($_POST['content'])? '': json_decode(wp_unslash($_POST['content']), true); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized below using array_walk_recursive
        if( !is_array($content) ) { $content = array(); }
        array_walk_recursive($content, 'pixe_sanitize_builder_data');
        
        $settings = empty($_POST['settings'])? '': map_deep(wp_unslash($_POST['settings']), 'sanitize_textarea_field');
        $style = empty($_POST['styles'])? '': map_deep(wp_unslash($_POST['styles']), 'sanitize_textarea_field');
        $dependency = empty($_POST['dependency'])? '': map_deep(wp_unslash($_POST['dependency']), 'sanitize_textarea_field');

        // update post status 
        $post_status = empty($_POST['status'])? '': sanitize_text_field(wp_unslash($_POST['status']));
        if( in_array($post_status, array('publish', 'draft')) ){
            wp_update_post(array(
                'ID' => $id,
                'post_status' => $post_status
            ));
        }
        
        // for autosave
        if( $post_status == 'autosave' ){
            
            update_post_meta($id, '_autosave-pixe-builder-data', array(
                'post_author' => $currentuser->ID,
                'post_date_utc' => $currenttime
            ));

            // update post meta
            if( !empty($content) ){
                update_post_meta($id, 'use-pixe-builder', '1');
                update_post_meta($id, '_autosave-pixe-builder-main', $content);
            }else{
                delete_post_meta($id, '_autosave-pixe-builder-main');
            }

            // settings
            if( !empty($settings) ){
                update_post_meta($id, '_autosave-pixe-builder-settings', $settings);
            }else{
                delete_post_meta($id, '_autosave-pixe-builder-settings');
            }

            // style
            if( !empty($style) ){
                update_post_meta($id, '_autosave-pixe-builder-styles', $style);
            }else{
                delete_post_meta($id, '_autosave-pixe-builder-styles');
            }

            // dependency
            if( !empty($dependency) ){
                update_post_meta($id, '_autosave-pixe-builder-dependency', $dependency);
            }else{
                delete_post_meta($id, '_autosave-pixe-builder-dependency');
            }

            $revision = array(
                'id' => 'autosave',
                'time' => $currenttime,
                'author' => $currentuser->display_name,
                'thumbnail' => get_avatar_url($currentuser->ID, array('size' => 60))
            );
        
        // normal save
        }else{

            // update post meta for main editor and style
            if( !empty($content) ){
                update_post_meta($id, 'use-pixe-builder', '1');
                update_post_meta($id, 'pixe-builder-main', wp_slash($content));
            }else{
                delete_post_meta($id, 'pixe-builder-main');
            }

            // settings
            if( !empty($settings) ){
                update_post_meta($id, 'pixe-builder-settings', $settings);
            }else{
                delete_post_meta($id, 'pixe-builder-settings');
            }
            
            // style
            if( !empty($style) ){
                update_post_meta($id, 'pixe-builder-styles', $style);
            }else{
                delete_post_meta($id, 'pixe-builder-styles');
            }

            // dependency
            if( !empty($dependency) ){
                update_post_meta($id, 'pixe-builder-dependency', $dependency);
            }else{
                delete_post_meta($id, 'pixe-builder-dependency');
            }

            // delete autosave data
            delete_post_meta($id, '_autosave-pixe-builder-data');
            delete_post_meta($id, '_autosave-pixe-builder-main');
            delete_post_meta($id, '_autosave-pixe-builder-settings');
            delete_post_meta($id, '_autosave-pixe-builder-styles');
            delete_post_meta($id, '_autosave-pixe-builder-dependency');

            // insert revision to database
            $row_id = pixe_set_builder_data(array(
                'pixe_id' => $id,
                'settings' => $settings,
                'post_author' => $currentuser->ID,
                'post_content' => $content,
                'post_date_utc' => $currenttime
            ));

            // remove excess revision
            $deleted_revision = pixe_remove_excess_revision($id);

            // current revision data
            $revision = array(
                'id' => $row_id,
                'time' => $currenttime,
                'author' => $currentuser->display_name,
                'thumbnail' => get_avatar_url($currentuser->ID, array('size' => 60))
            );
        }

        // store data
        $ret = array(
            'status' => 'success', 
            'revision' => $revision, 
            'deleted-revision' => $deleted_revision
        );
        
        die(json_encode($ret));
    }

    function pixe_remove_excess_revision($id){
        $deleted_ids = array();
        $revision_amount = pixe_get_settings('revision-amount', -1);

        if( $revision_amount > 0 ){
            global $wpdb;

            // query all ids
            $results = $wpdb->get_results($wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                "SELECT id FROM {$wpdb->prefix}pixebuilder WHERE pixe_id = %d ORDER BY id DESC;", 
            $id));

            // get excess revision
            $count = 0;
            foreach( $results as $result ){ $count++;
                if( $count <= $revision_amount ) continue;
                $deleted_ids[] = $result->id;
            }

            // remove excess revision
            if( !empty($deleted_ids) ){
                $placeholder = implode(',', array_fill(0, count($deleted_ids), '%d'));
                $wpdb->query($wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Cache automatically invalidated on data changes
                    "DELETE FROM {$wpdb->prefix}pixebuilder WHERE id IN ({$placeholder});", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- Dynamic placeholder list generated safely
                $deleted_ids)); 

                // delete cache after update
                pixe_increase_cache_version('post');
            }

        }

        return $deleted_ids;
    }
    
    // combobox list
    add_action('wp_ajax_pixe_get_combobox_list', 'pixe_ajax_get_combobox_list');
    function pixe_ajax_get_combobox_list(){

        // check nonce
        if( !check_ajax_referer('pixe-editor', 'security', false) ){
            die(json_encode(array(
                'status' => 'failed',
                'head' => pixe_get_error_message('nonce-head'),
                'message'=> pixe_get_error_message('nonce-message')
            )));
        }

        $type = empty($_POST['type'])? '': sanitize_text_field(wp_unslash($_POST['type']));
        $value = empty($_POST['value'])? '': sanitize_text_field(wp_unslash($_POST['value']));

        $ret = array();
        switch($type){
            case 'menu':
                $ret = pixe_get_menu_list();
                break;
            case 'user-role':
                $ret = pixe_get_user_role_list();
                break;
            case 'widget':
                $ret = pixe_get_widget_area();
                break;
            case 'post':
                if( !empty($value) ){
                    $ret = pixe_get_post_list($value);
                }
                break;
            case 'taxonomy':
                if( !empty($value) ){
                    $ret = pixe_get_post_taxonomy_list($value);
                }
                break;
            case 'category':
                if( !empty($value) ){
                    $ret = pixe_get_post_category_list($value);
                }
                break;
        }

        die(json_encode($ret));
    }