<?php
/**
 * MainWP Child Bulk Settings Manager
 *
 * This file handles connecting to the child site as a browser in order performs an HTTP request using the POST method and returns its response.
 *
 * @package MainWP\Child
 */

namespace MainWP\Child;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * Class MainWP_Child_Bulk_Settings_Manager
 *
 * Handles connecting to the child site as a browser in order performs an HTTP request using the POST method and returns its response.
 */
class MainWP_Child_Bulk_Settings_Manager {

    /**
     * Public static variable to hold the single instance of the class.
     *
     * @var mixed Default null
     */
    public static $instance = null;

    /**
     * Public statis variable containing the synchronization information.
     *
     * @var array Synchronization information.
     */
    public static $information = array();

    /**
     * Public variable to hold the information about the language domain.
     *
     * @var string 'mainwp-child' languge domain.
     */
    public $plugin_translate = 'mainwp-child';

    /**
     * Create public static instance for MainWP_Child_Bulk_Settings_Manager.
     *
     * @return MainWP_Child_Bulk_Settings_Manager|null
     */
    public static function instance() {
        if ( null === static::$instance ) {
            static::$instance = new self();
        }

        return static::$instance;
    }

    /**
     * Save Settings & Visit Site as Browser actions.
     *
     * @uses \MainWP\Child\MainWP_Helper::write()
     */
    public function action() {

        /**
         * MainWP bulk settings manager fatal error handler.
         */
        function mainwp_bulk_settings_manager_handle_fatal_error() {
            $error = error_get_last();
            if ( isset( $error['type'] ) && in_array( $error['type'], array( 1, 4, 16, 64, 256 ) ) && isset( $error['message'] ) ) {
                MainWP_Helper::write( array( 'error' => 'MainWP_Child fatal error : ' . $error['message'] . ' Line: ' . $error['line'] . ' File: ' . $error['file'] ) );
            }
        }

        register_shutdown_function( '\MainWP\Child\mainwp_bulk_settings_manager_handle_fatal_error' );

        $mwp_action = MainWP_System::instance()->validate_params( 'action' );
        switch ( $mwp_action ) {
            case 'skeleton_key_visit_site_as_browser': // deprecated.
                $information = $this->visit_site_as_browser();
                break;
            case 'bulk_settings_manager_visit_site_as_browser':
                $information = $this->visit_site_as_browser();
                break;
            case 'save_settings':
                $information = $this->save_settings();
                break;
            default:
                $information = array( 'error' => 'Unknown action' );
        }

        MainWP_Helper::write( $information );
        exit();
    }

    /**
     * Visit site as a browser.
     *
     * @return array|string[] Response array or Error message string within an array.
     *
     * @uses \MainWP\Child\MainWP_Helper::get_class_name()
     */
    protected function visit_site_as_browser() { // phpcs:ignore -- NOSONAR - ignore complex method notice.
        // phpcs:disable WordPress.Security.NonceVerification
        if ( ! isset( $_POST['url'] ) || ! is_string( wp_unslash( $_POST['url'] ) ) || strlen( wp_unslash( $_POST['url'] ) ) < 2 ) { //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
            return array( 'error' => 'Missing url' );
        }

        if ( ! isset( $_POST['args'] ) || ! is_array( $_POST['args'] ) ) {
            return array( 'error' => 'Missing args' );
        }

        $_POST = stripslashes_deep( wp_unslash( $_POST ) );

        $args = isset( $_POST['args'] ) ? wp_unslash( $_POST['args'] ) : array(); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

        $current_user = wp_get_current_user();

        $url = isset( $_POST['url'] ) ? '/' . sanitize_text_field( wp_unslash( $_POST['url'] ) ) : '';

        $expiration = time() + 600;
        $manager    = \WP_Session_Tokens::get_instance( $current_user->ID );
        $token      = $manager->create( $expiration );

        $secure = is_ssl();
        if ( $secure ) {
            $auth_cookie_name = SECURE_AUTH_COOKIE;
            $scheme           = 'secure_auth';
        } else {
            $auth_cookie_name = AUTH_COOKIE;
            $scheme           = 'auth';
        }
        $auth_cookie                  = wp_generate_auth_cookie( $current_user->ID, $expiration, $scheme, $token );
        $logged_in_cookie             = wp_generate_auth_cookie( $current_user->ID, $expiration, 'logged_in', $token );
        $_COOKIE[ $auth_cookie_name ] = $auth_cookie;
        $_COOKIE[ LOGGED_IN_COOKIE ]  = $logged_in_cookie;
        $post_args                    = array();
        $post_args['body']            = array();
        $post_args['redirection']     = 5;
        $post_args['decompress']      = false;
        $post_args['cookies']         = array(
            new \WP_Http_Cookie(
                array(
                    'name'  => $auth_cookie_name,
                    'value' => $auth_cookie,
                )
            ),
            new \WP_Http_Cookie(
                array(
                    'name'  => LOGGED_IN_COOKIE,
                    'value' => $logged_in_cookie,
                )
            ),
        );

        $skip_invalid_nonce = false;
        if ( isset( $_REQUEST['skip_invalid_nonce'] ) && ! empty( $_REQUEST['skip_invalid_nonce'] ) ) {
            $skip_invalid_nonce = true;
        }

        // phpcs:enable

        if ( isset( $args['get'] ) ) {
            $get_args = $args['get'];
            parse_str( $args['get'], $get_args );
        }

        if ( ! isset( $get_args ) || ! is_array( $get_args ) ) {
            $get_args = array();
        }

        $get_args['bulk_settings_manageruse_nonce_key']  = intval( time() );
        $get_args['bulk_settings_manageruse_nonce_hmac'] = hash_hmac( 'sha256', $get_args['bulk_settings_manageruse_nonce_key'], NONCE_KEY );

        if ( true === $skip_invalid_nonce ) {
            $get_args['bulk_settings_skip_invalid_nonce'] = $skip_invalid_nonce;
        }

        $good_nonce = null;
        if ( isset( $args['nonce'] ) && ! empty( $args['nonce'] ) ) {
            parse_str( $args['nonce'], $temp_nonce );
            $good_nonce = $this->wp_create_nonce_recursive( $temp_nonce );
            $get_args   = array_merge( $get_args, $good_nonce );
        }

        if ( isset( $args['post'] ) ) {
            parse_str( $args['post'], $temp_post );
            if ( ! isset( $temp_post ) || ! is_array( $temp_post ) ) {
                $temp_post = array();
            }

            if ( ! empty( $good_nonce ) ) {
                $temp_post = array_merge( $temp_post, $good_nonce );
            }

            if ( true === $skip_invalid_nonce ) {
                $temp_post['bulk_settings_skip_invalid_nonce'] = $skip_invalid_nonce;
            }

            $post_args['body'] = $temp_post;
        }

        $post_args['timeout'] = 25;

        $full_url = add_query_arg( $get_args, get_site_url() . $url );

        add_filter( 'http_request_args', array( MainWP_Helper::get_class_name(), 'reject_unsafe_urls' ), 99, 2 );

        $response = wp_remote_post( $full_url, $post_args );

        if ( is_wp_error( $response ) ) {
            return array( 'error' => 'wp_remote_post error: ' . $response->get_error_message() );
        }

        $received_content = wp_remote_retrieve_body( $response );

        if ( preg_match( '/<mainwp>(.*)<\/mainwp>/', $received_content, $received_result ) > 0 ) {
            $received_content_mainwp = json_decode( base64_decode( $received_result[1] ), true ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode function is used for http encode compatible..
            if ( isset( $received_content_mainwp['error'] ) ) {
                return array( 'error' => $received_content_mainwp['error'] );
            }
        }

        $search_ok_counter   = 0;
        $search_fail_counter = 0;

        if ( isset( $args['search']['ok'] ) ) {
            foreach ( $args['search']['ok'] as $search ) {
                if ( preg_match( '/' . preg_quote( $search, '/' ) . '/i', $received_content ) ) {
                    ++$search_ok_counter;
                }
            }
        }

        if ( isset( $args['search']['fail'] ) ) {
            foreach ( $args['search']['fail'] as $search ) {
                if ( preg_match( '/' . preg_quote( $search, '/' ) . '/i', $received_content ) ) {
                    ++$search_fail_counter;
                }
            }
        }
        unset( $get_args['bulk_settings_manageruse_nonce_key'] );
        unset( $get_args['bulk_settings_manageruse_nonce_hmac'] );

        return array(
            'success'             => 1,
            'content'             => $received_content,
            'url'                 => $full_url,
            'get'                 => $get_args,
            'post'                => $post_args['body'],
            'search_ok_counter'   => $search_ok_counter,
            'search_fail_counter' => $search_fail_counter,
        );
    }

    /**
     * Create WP nonce.
     *
     * @param array $arr An array containing the nonce.
     *
     * @return array An array containing the nonce.
     */
    private function wp_create_nonce_recursive( $arr ) {
        foreach ( $arr as $key => $value ) {
            if ( is_array( $arr[ $key ] ) ) {
                $arr[ $key ] = $this->wp_create_nonce_recursive( $arr[ $key ] );
            } else {
                $arr[ $key ] = wp_create_nonce( $arr[ $key ] );
            }
        }

        return $arr;
    }

    /**
     * Save site settings.
     *
     * @return array|bool|string[] Result array ok|error or FALSE or $whitelist_options[].
     */
    public function save_settings() { //phpcs:ignore -- NOSONAR - complex.
        $settings = isset( $_POST['settings'] ) ? wp_unslash( $_POST['settings'] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

        if ( ! is_array( $settings ) || empty( $settings ) ) {
            return array( 'error' => esc_html__( 'Invalid data. Please check and try again.', 'mainwp-child' ) );
        }

        $whitelist_options = array(
            'general' => array( 'blogname', 'blogdescription', 'gmt_offset', 'date_format', 'time_format', 'start_of_week', 'timezone_string', 'WPLANG' ),
        );

        if ( ! is_multisite() ) {
            if ( ! defined( 'WP_SITEURL' ) ) {
                $whitelist_options['general'][] = 'siteurl';
            }
            if ( ! defined( 'WP_HOME' ) ) {
                $whitelist_options['general'][] = 'home';
            }

            $whitelist_options['general'][] = 'admin_email';
            $whitelist_options['general'][] = 'users_can_register';
            $whitelist_options['general'][] = 'default_role';
        }

        $whitelist_general = $whitelist_options['general'];

        if ( ! empty( $settings['WPLANG'] ) ) {
            require_once ABSPATH . 'wp-admin/includes/translation-install.php'; // NOSONAR - WP compatible.
            if ( wp_can_install_language_pack() ) {
                $language = wp_download_language_pack( $settings['WPLANG'] );
                if ( $language ) {
                    $settings['WPLANG'] = $language;
                }
            }
        }

        $updated = false;
        foreach ( $settings as $option => $value ) {
            if ( in_array( $option, $whitelist_general ) ) {
                if ( ! is_array( $value ) ) {
                    $value = trim( $value );
                }
                $value = wp_unslash( $value );
                update_option( $option, $value );
                $updated = true;
            }
        }

        if ( ! $updated ) {
            return false;
        }

        return array( 'result' => 'ok' );
    }
}
