<?php
/**
 * Logger: Plugins and Themes changes.
 *
 * @since      5.5
 * @package    mainwp/child
 */

namespace MainWP\Child\Changes;

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

/**
 * Class Changes_Handle_WP_Plugins_Themes
 */
class Changes_Handle_WP_Plugins_Themes {

    /**
     * Store Themes.
     *
     * @var array
     */
    private static $current_themes = null;

    /**
     * Store Plugins.
     *
     * @var array
     *
     * @since 6.0.7
     */
    private static $current_plugins = null;

    /**
     * Inits hooks
     *
     * @return void
     */
    public static function init_hooks() {
        \add_action( 'shutdown', array( __CLASS__, 'callback_change_wp_shutdown' ) );
        \add_action( 'upgrader_pre_install', array( __CLASS__, 'init_before' ) );
        // Fires after the upgrades done.
        \add_action( 'upgrader_process_complete', array( __CLASS__, 'callback_change_upgrader_complete' ), 10, 2 );
    }

    /**
     * Hook WP shutdown.
     */
    public static function callback_change_wp_shutdown() { //phpcs:ignore --NOSONAR -complex.

        $post_vars   = filter_input_array( INPUT_POST );
        $get_vars    = filter_input_array( INPUT_GET );
        $script_name = isset( $_SERVER['SCRIPT_NAME'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SCRIPT_NAME'] ) ) : false;

        $action = '';
        if ( isset( $get_vars['action'] ) && '-1' !== $get_vars['action'] ) {
            $action = \sanitize_text_field( \wp_unslash( $get_vars['action'] ) );
        } elseif ( isset( $post_vars['action'] ) && '-1' !== $post_vars['action'] ) {
            $action = \sanitize_text_field( \wp_unslash( $post_vars['action'] ) );
        }

        if ( isset( $get_vars['action2'] ) && '-1' !== $get_vars['action2'] ) {
            $action = \sanitize_text_field( \wp_unslash( $get_vars['action2'] ) );
        } elseif ( isset( $post_vars['action2'] ) && '-1' !== $post_vars['action2'] ) {
            $action = \sanitize_text_field( \wp_unslash( $post_vars['action2'] ) );
        }

        $type = '';
        if ( ! empty( $script_name ) ) {
            $type = basename( $script_name, '.php' );
        }
        $is_plugins = 'plugins' === $type;
        // Activate plugin.
        if ( $is_plugins && in_array( $action, array( 'activate', 'activate-selected' ), true ) && current_user_can( 'activate_plugins' ) ) {
            if ( isset( $get_vars['plugin'] ) ) {
                if ( ! isset( $get_vars['checked'] ) ) {
                    $get_vars['checked'] = array();
                }
                $get_vars['checked'][] = \sanitize_text_field( \wp_unslash( $get_vars['plugin'] ) );
            }

            if ( isset( $post_vars['plugin'] ) ) {
                if ( ! isset( $post_vars['checked'] ) ) {
                    $post_vars['checked'] = array();
                }
                $post_vars['checked'][] = \sanitize_text_field( \wp_unslash( $post_vars['plugin'] ) );
            }

            if ( isset( $get_vars['checked'] ) && ! empty( $get_vars['checked'] ) ) {
                foreach ( $get_vars['checked'] as $plugin ) {
                    if ( ! is_wp_error( validate_plugin( $plugin ) ) ) {
                        $plugin_file = WP_PLUGIN_DIR . '/' . $plugin;
                        $plugin_data = \get_plugin_data( $plugin_file, false, true );
                        $log_data    = array(
                            'slug'       => $plugin,
                            'plugindata' => (object) array(
                                'name'      => $plugin_data['Name'],
                                'pluginuri' => $plugin_data['PluginURI'],
                                'version'   => $plugin_data['Version'],
                                'author'    => $plugin_data['Author'],
                            ),
                        );
                        Changes_Logs_Logger::log_change( 1461, $log_data );
                    }
                }
            } elseif ( isset( $post_vars['checked'] ) && ! empty( $post_vars['checked'] ) ) {
                foreach ( $post_vars['checked'] as $plugin ) {
                    if ( ! is_wp_error( validate_plugin( $plugin ) ) ) {
                        $plugin_file = WP_PLUGIN_DIR . '/' . $plugin;
                        $plugin_data = get_plugin_data( $plugin_file, false, true );
                        $log_data    = array(
                            'slug'       => $plugin,
                            'plugindata' => (object) array(
                                'name'      => $plugin_data['Name'],
                                'pluginuri' => $plugin_data['PluginURI'],
                                'version'   => $plugin_data['Version'],
                                'author'    => $plugin_data['Author'],
                            ),
                        );
                        Changes_Logs_Logger::log_change( 1461, $log_data );
                    }
                }
            }
        }
    }

    /**
     * Triggered when a user accesses the admin area.
     */
    public static function init_before() {

        if ( ! function_exists( '\get_plugins' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php'; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.FileIncludeFound -- NOSONAR - ok.
        }

        self::get_current_themes();
        self::get_current_plugins();
    }


    /**
     * Get current themes BEFORE themes changes.
     *
     * @return array
     */
    private static function get_current_themes(): array {
        if ( null === self::$current_themes ) {
            self::$current_themes = \wp_get_themes();
        }

        return self::$current_themes;
    }

    /**
     * Get current plugins before plugins changes.
     *
     * @return array
     */
    private static function get_current_plugins(): array {
        if ( null === self::$current_plugins ) {
            self::$current_plugins = \get_plugins();
        }

        return self::$current_plugins;
    }


    /**
     * Fires when plugins, themes are updated or installed
     *
     * @param Mixed $upgrader_instance Plugin_Upgrader instance, or Theme_Upgrader, Core_Upgrade instance.
     * @param array $opt_data Array of item update data.
     *
     * @return void
     */
    public static function callback_change_upgrader_complete( $upgrader_instance, $opt_data ): void {

        if ( empty( $upgrader_instance ) || empty( $opt_data ) ) {
            return;
        }

        // Ignore if changes installed events triggered before.
        if ( Changes_Logs_Logger::is_handled_logs_type( 1965 ) || Changes_Logs_Logger::is_handled_logs_type( 1975 ) ) {
            return;
        }

        if ( empty( $opt_data['type'] ) || empty( $opt_data['action'] ) ) {
            return;
        }

        if ( isset( $opt_data['type'] ) ) {
            if ( 'plugin' === $opt_data['type'] ) {
                self::change_plugin_install( $upgrader_instance, $opt_data );
                self::change_plugin_update( $upgrader_instance, $opt_data );
                self::change_bulk_plugin_update( $upgrader_instance, $opt_data );
            } elseif ( 'theme' === $opt_data['type'] ) {
                self::change_theme_install( $upgrader_instance, $opt_data );
                self::change_theme_upgrade( $upgrader_instance, $opt_data );
            }
        }
    }

    /**
     * Handle event when theme is installed
     *
     * @param mixed $upgrader_instance Plugin_Upgrader instance or Theme_Upgrader, Core_Upgrade instance.
     * @param array $opt_data Array of bulk item update data.
     *
     * @return void
     */
    public static function change_theme_install( $upgrader_instance, $opt_data ): void {

        // Must be type 'theme' and action 'install'.
        if ( 'theme' !== $opt_data['type'] || 'install' !== $opt_data['action'] ) {
            return;
        }

        if ( empty( $upgrader_instance->new_theme_data ) ) {
            return;
        }

        // Install theme.
        $destination_name = $upgrader_instance->result['destination_name'] ?? '';

        if ( empty( $destination_name ) ) {
            return;
        }

        $theme = \wp_get_theme( $destination_name );

        if ( ! $theme->exists() ) {
            return;
        }

        $new_theme_data = $upgrader_instance->new_theme_data;

        Changes_Logs_Logger::log_change(
            1975,
            array(
                'theme' => (object) array(
                    'name'        => $new_theme_data['Name'],
                    'themeuri'    => $theme->ThemeURI, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
                    'description' => $theme->get( 'Description' ),
                    'author'      => $new_theme_data['Author'],
                    'version'     => $new_theme_data['Version'],
                ),
            )
        );
    }

    /**
     * Handle event when theme is upgraded
     *
     * @param mixed $upgrader_instance Plugin_Upgrader instance or Theme_Upgrader, Core_Upgrade instance.
     * @param array $opt_data Array of update data.
     *
     * @return void
     */
    public static function change_theme_upgrade( $upgrader_instance, $opt_data ): void { //phpcs:ignore --NOSONAR -complex.

        // Must be 'theme' and 'update'.
        if ( 'theme' !== $opt_data['type'] || 'update' !== $opt_data['action'] ) {
            return;
        }

        if ( isset( $opt_data['bulk'] ) && $opt_data['bulk'] && isset( $opt_data['themes'] ) ) {
            $arr_themes = (array) $opt_data['themes'];
        } else {
            $arr_themes = array(
                $opt_data['theme'],
            );
        }

        $auto_updated = \is_object( $upgrader_instance ) && \property_exists( $upgrader_instance->skin, 'skin' ) && \is_a( $upgrader_instance->skin, 'Automatic_Upgrader_Skin' ) ? 1 : 0;

        $old_themes = self::get_current_themes();

        if ( ! is_array( $old_themes ) ) {
            $old_themes = array();
        }
        foreach ( $arr_themes as $one_updated_theme ) {
            $theme = \wp_get_theme( $one_updated_theme );

            if ( ! is_a( $theme, 'WP_Theme' ) ) {
                continue;
            }

            $old_theme = isset( $old_themes[ $one_updated_theme ] ) ? $old_themes[ $one_updated_theme ] : false;

            $old_version = '';
            if ( ! empty( $old_theme ) && $old_theme instanceof \WP_Theme ) {
                $old_version = $old_theme->get( 'Version' );
            }

            $theme_name    = $theme->get( 'Name' );
            $theme_version = $theme->get( 'Version' );

            if ( ! $theme_name || ! $theme_version || $theme_version === $old_version ) {
                continue;
            }

            Changes_Logs_Logger::log_change(
                1980,
                array(
                    'theme'        => (object) array(
                        'name'        => $theme_name,
                        'themeuri'    => $theme->ThemeURI, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
                        'description' => $theme->Description, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
                        'author'      => $theme->Author, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
                        'version'     => $theme_version,
                    ),
                    'auto_updated' => $auto_updated,
                    'oldversion'   => $old_version,
                )
            );
        }
    }

    /**
     * Handle event when plugin is installed
     *
     * @param mixed $plugin_upgrader_instance Plugin_Upgrader instance or Theme_Upgrader, Core_Upgrade instance.
     * @param array $opt_data Array update data.
     *
     * @return void
     */
    public static function change_plugin_install( $plugin_upgrader_instance, $opt_data ): void {

        // return if not plugin install.
        if ( ! isset( $opt_data['action'] ) || 'install' !== $opt_data['action'] || $plugin_upgrader_instance->bulk ) {
            return;
        }

        $upgrader_skin_options = isset( $plugin_upgrader_instance->skin->options ) && is_array( $plugin_upgrader_instance->skin->options ) ? $plugin_upgrader_instance->skin->options : array();
        $upgrader_skin_result  = isset( $plugin_upgrader_instance->skin->result ) && is_array( $plugin_upgrader_instance->skin->result ) ? $plugin_upgrader_instance->skin->result : array();
        $new_plugin_data       = $plugin_upgrader_instance->new_plugin_data ?? array();
        $plugin_slug           = $upgrader_skin_result['destination_name'] ?? '';

        $plugin = $plugin_upgrader_instance->plugin_info();

        $context = array(
            'plugin_slug'         => $plugin_slug,
            'plugin_name'         => $new_plugin_data['Name'] ?? '',
            'plugin_title'        => $new_plugin_data['Title'] ?? '',
            'plugin_url'          => $new_plugin_data['PluginURI'] ?? '',
            'plugin_version'      => $new_plugin_data['Version'] ?? '',
            'plugin_author'       => $new_plugin_data['Author'] ?? '',
            'plugin_requires_wp'  => $new_plugin_data['RequiresWP'] ?? '',
            'plugin_requires_php' => $new_plugin_data['RequiresPHP'] ?? '',
            'plugin_network'      => ( $new_plugin_data['Network'] ?? false ) ? \esc_html__( 'True', 'mainwp-child' ) : \esc_html__( 'False', 'mainwp-child' ),
        );

        if ( isset( $new_plugin_data['UpdateURI'] ) ) {
            $context['plugin_update_uri'] = $new_plugin_data['UpdateURI'];
        }

        $install_source = 'web';
        if ( isset( $upgrader_skin_options['type'] ) ) {
            $install_source = \strtolower( (string) $upgrader_skin_options['type'] );
        }

        $context['plugin_install_source'] = $install_source;

        // If uploaded plugin store name of ZIP.
        // phpcs:ignore WordPress.Security.NonceVerification.Missing
        if ( 'upload' === $install_source && isset( $_FILES['pluginzip']['name'] ) ) {
            // phpcs:ignore WordPress.Security.NonceVerification.Missing
            $plugin_upload_name            = sanitize_text_field( $_FILES['pluginzip']['name'] );
            $context['plugin_upload_name'] = $plugin_upload_name;
        }

        if ( ! is_a( $plugin_upgrader_instance->skin->result, 'WP_Error' ) ) {

            // Check if the plugin is already installed and we are here because of an update via upload.
            $current_plugins = self::get_current_plugins();

            if ( isset( $current_plugins[ $plugin ] ) ) {
                return;
            }

            Changes_Logs_Logger::log_change(
                1965,
                array(
                    'plugindata' => (object) array(
                        'name'      => $context['plugin_name'],
                        'pluginuri' => $context['plugin_url'],
                        'version'   => $context['plugin_version'],
                        'author'    => $context['plugin_author'],
                        'network'   => $context['plugin_network'],
                        'slug'      => $context['plugin_slug'],
                        'title'     => $context['plugin_title'],
                    ),
                )
            );
        }
    }

    /**
     * Logs event when single plugin is updated
     *
     * @param mixed $plugin_upgrader_instance Plugin_Upgrader instance or Theme_Upgrader, Core_Upgrade instance.
     * @param array $opt_data Array of bulk item update data.
     *
     * @return void
     */
    public static function change_plugin_update( $plugin_upgrader_instance, $opt_data ): void {
        if ( ! isset( $opt_data['action'] ) || 'update' !== $opt_data['action'] || $plugin_upgrader_instance->bulk ) {
            return;
        }

        // No plugin info in instance, so get it ourself.
        $plugin_data = array();
        if ( file_exists( \WP_PLUGIN_DIR . '/' . $opt_data['plugin'] ) ) {
            $plugin_data = \get_plugin_data( \WP_PLUGIN_DIR . '/' . $opt_data['plugin'], true, false );
        }

        $plugin_slug = dirname( $opt_data['plugin'] );

        $context = array(
            'plugin_slug'        => $plugin_slug,
            'plugin_name'        => $plugin_data['Name'] ?? '',
            'plugin_title'       => $plugin_data['Title'] ?? '',
            'plugin_description' => $plugin_data['Description'] ?? '',
            'plugin_author'      => $plugin_data['Author'] ?? '',
            'plugin_version'     => $plugin_data['Version'] ?? '',
            'plugin_url'         => $plugin_data['PluginURI'] ?? '',
            'plugin_network'     => ( $plugin_data['Network'] ?? false ) ? \esc_html__( 'True', 'mainwp-child' ) : \esc_html__( 'False', 'mainwp-child' ),
        );

        // If it is set.
        if ( isset( $plugin_data['UpdateURI'] ) ) {
            $context['plugin_update_uri'] = $plugin_data['UpdateURI'];
        }

        if ( ! \is_wp_error( \validate_plugin( $opt_data['plugin'] ) ) ) {
            $current_plugins = self::get_current_plugins();
            $auto_updated    = \is_object( $plugin_upgrader_instance ) && \property_exists( $plugin_upgrader_instance->skin, 'skin' ) && \is_a( $plugin_upgrader_instance->skin, 'Automatic_Upgrader_Skin' ) ? 1 : 0;

            $current_version = ( isset( $current_plugins[ $opt_data['plugin'] ] ) ) ? $current_plugins[ $opt_data['plugin'] ]['Version'] : false;

            if ( $current_version !== $context['plugin_version'] ) {
                Changes_Logs_Logger::log_change(
                    1970,
                    array(
                        'plugindata'   => (object) array(
                            'name'      => $context['plugin_name'],
                            'pluginuri' => $context['plugin_url'],
                            'version'   => $context['plugin_version'],
                            'author'    => $context['plugin_author'],
                            'network'   => $context['plugin_network'],
                            'slug'      => $context['plugin_slug'],
                            'title'     => $context['plugin_title'],
                        ),
                        'oldversion'   => $current_version,
                        'auto_updated' => $auto_updated,
                    )
                );
            }
        }
    }

    /**
     * Capture event when single plugin is updated
     *
     * @param mixed $plugin_upgrader_instance Plugin_Upgrader instance or Theme_Upgrader, Core_Upgrade instance.
     * @param array $opt_data                 Array of bulk item update data.
     *
     * @return void
     */
    public static function change_bulk_plugin_update( $plugin_upgrader_instance, $opt_data ): void {

        if ( ! isset( $opt_data['bulk'] ) || ! $opt_data['bulk'] || ! isset( $opt_data['action'] ) || 'update' !== $opt_data['action'] ) {
            return;
        }

        $plugins_updated = isset( $opt_data['plugins'] ) ? (array) $opt_data['plugins'] : array();

        foreach ( $plugins_updated as $plugin ) {
            $opt_data['plugin'] = $plugin;

            $plugin_data = array();
            if ( file_exists( \WP_PLUGIN_DIR . '/' . $opt_data['plugin'] ) ) {
                $plugin_data = \get_plugin_data( \WP_PLUGIN_DIR . '/' . $opt_data['plugin'], true, false );
            }

            $plugin_slug = dirname( $opt_data['plugin'] );

            $context = array(
                'plugin_slug'        => $plugin_slug,
                'plugin_name'        => $plugin_data['Name'] ?? '',
                'plugin_title'       => $plugin_data['Title'] ?? '',
                'plugin_description' => $plugin_data['Description'] ?? '',
                'plugin_author'      => $plugin_data['Author'] ?? '',
                'plugin_version'     => $plugin_data['Version'] ?? '',
                'plugin_url'         => $plugin_data['PluginURI'] ?? '',
                'plugin_network'     => ( $plugin_data['Network'] ?? false ) ? \esc_html__( 'True', 'mainwp-child' ) : \esc_html__( 'False', 'mainwp-child' ),
            );

            if ( isset( $plugin_data['UpdateURI'] ) ) {
                $context['plugin_update_uri'] = $plugin_data['UpdateURI'];
            }

            if ( ! \is_wp_error( \validate_plugin( $opt_data['plugin'], ) ) ) {
                $current_plugins = self::get_current_plugins();

                $auto_updated = \is_object( $plugin_upgrader_instance ) && \property_exists( $plugin_upgrader_instance->skin, 'skin' ) && \is_a( $plugin_upgrader_instance->skin, 'Automatic_Upgrader_Skin' ) ? 1 : 0;

                $current_version = ( isset( $current_plugins[ $opt_data['plugin'] ] ) ) ? $current_plugins[ $opt_data['plugin'] ]['Version'] : false;

                if ( $current_version !== $context['plugin_version'] ) {
                    Changes_Logs_Logger::log_change(
                        1970,
                        array(
                            'plugindata'   => (object) array(
                                'name'      => $context['plugin_name'],
                                'pluginuri' => $context['plugin_url'],
                                'version'   => $context['plugin_version'],
                                'author'    => $context['plugin_author'],
                                'network'   => $context['plugin_network'],
                                'slug'      => $context['plugin_slug'],
                                'title'     => $context['plugin_title'],
                            ),
                            'oldversion'   => $current_version,
                            'auto_updated' => $auto_updated,
                        )
                    );
                }
            }
        }
    }
}
