<?php

/**
 * Model for retrieving Information about the Event Espresso status.
 */
class EEM_System_Status
{
    protected static ?EEM_System_Status $_instance = null;


    /**
     * This function is a singleton method used to instantiate the EEM_Attendee object
     *
     * @return EEM_System_Status
     */
    public static function instance()
    {
        // check if instance of EEM_System_Status already exists
        if (self::$_instance === null) {
            // instantiate EEM_System_Status
            self::$_instance = new self();
        }
        return self::$_instance;
    }


    private function __construct()
    {
    }


    /**
     * @return array where each key is a function name on this class, and each value is SOMETHING--
     * it might be a value, an array, or an object
     */
    public function get_system_stati()
    {
        return apply_filters(
            'FHEE__EEM_System_Status__get_system_stati',
            [
                'ee_version'            => $this->get_ee_version(),
                'ee_activation_history' => $this->get_ee_activation_history(),
                'ee_config'             => $this->get_ee_config(),
                'ee_migration_history'  => $this->get_ee_migration_history(),
                'active_plugins'        => $this->get_active_plugins(),
                'wp_settings'           => $this->get_wp_settings(),
                'wp_maintenance_mode'   => $this->get_wp_maintenance_mode(),
                'https_enabled'         => $this->get_https_enabled(),
                'logging_enabled'       => $this->get_logging_enabled(),
                'remote_posting'        => $this->get_remote_posting(),
                'php_version'           => $this->php_version(),
                'php.ini_settings'      => $this->get_php_ini_all(),
                'php_info'              => $this->get_php_info(),
            ],
            $this
        );
    }


    /**
     * @return string
     */
    public function get_ee_version()
    {
        return espresso_version();
    }


    /**
     * @return string
     */
    public function php_version()
    {
        return phpversion();
    }


    /**
     * @return array, where each key is a plugin name (lower-cased), values are sub-arrays.
     * Sub-arrays like described in wp function get_plugin_data. Ie,     *
     *  'Name' => 'Plugin Name',
     * 'PluginURI' => 'Plugin URI',
     * 'Version' => 'Version',
     * 'Description' => 'Description',
     * 'Author' => 'Author',
     * 'AuthorURI' => 'Author URI',
     * 'TextDomain' => 'Text Domain',
     * 'DomainPath' => 'Domain Path',
     * 'Network' => 'Network',
     */
    public function get_active_plugins()
    {
        $active_plugins = (array) get_option('active_plugins', []);
        if (is_multisite()) {
            $active_plugins = array_merge($active_plugins, get_site_option('active_sitewide_plugins', []));
        }
        $active_plugins = array_map('strtolower', $active_plugins);
        $plugin_info    = [];
        foreach ($active_plugins as $plugin) {
            $plugin_data = @get_plugin_data(WP_PLUGIN_DIR . '/' . $plugin);

            $plugin_info[ $plugin ] = $plugin_data;
        }
        return $plugin_info;
    }


    /**
     * @return array with keys 'home_url' and 'site_url'
     */
    public function get_wp_settings()
    {
        $wp_memory_int = $this->let_to_num(WP_MEMORY_LIMIT);
        if ($wp_memory_int < 67108864) {
            $wp_memory_to_display = '<mark class="error">';
            $wp_memory_to_display .= sprintf(
                esc_html__(
                    '%s - We recommend setting memory to at least 64MB. See: %s Increasing memory allocated to PHP %s',
                    'event_espresso'
                ),
                WP_MEMORY_LIMIT,
                '<a href="http://codex.wordpress.org/Editing_wp-config.php#Increasing_memory_allocated_to_PHP">',
                '</a>"'
            );
            $wp_memory_to_display .= '</mark>';
        } else {
            $wp_memory_to_display = '<mark class="yes">' . size_format($wp_memory_int) . '</mark>';
        }
        return [
            'name'                => get_bloginfo('name', 'display'),
            'is_multisite'        => is_multisite(),
            'version'             => get_bloginfo('version', 'display'),
            'home_url'            => home_url(),
            'site_url'            => site_url(),
            'WP_DEBUG'            => WP_DEBUG,
            'permalink_structure' => get_option('permalink_structure'),
            'theme'               => wp_get_theme(),
            'gmt_offset'          => get_option('gmt_offset'),
            'timezone_string'     => get_option('timezone_string'),
            'admin_email'         => get_bloginfo('admin_email', 'display'),
            'language'            => get_bloginfo('language', 'display'),
            'wp_max_upload_size'  => size_format(wp_max_upload_size()),
            'wp_memory'           => $wp_memory_to_display,
        ];
    }


    /**
     * Gets an array of information about the history of ee versions installed
     *
     * @return array
     */
    public function get_ee_activation_history()
    {
        return get_option('espresso_db_update');
    }


    /**
     * Gets an array where keys are ee versions, and their values are arrays indicating all the different times that
     * version was installed
     *
     * @return EE_Data_Migration_Script_Base[]
     */
    public function get_ee_migration_history()
    {
        $options                       = EE_Data_Migration_Manager::instance()->get_all_migration_script_options();
        $presentable_migration_scripts = [];
        foreach ($options as $option_array) {
            $presentable_migration_scripts[ str_replace(
                EE_Data_Migration_Manager::data_migration_script_option_prefix,
                "",
                $option_array['option_name']
            ) ] = maybe_unserialize($option_array['option_value']);
        }
        return $presentable_migration_scripts;
//      return get_option(EE_Data_Migration_Manager::data_migrations_option_name);//EE_Data_Migration_Manager::instance()->get_data_migrations_ran();
    }


    /**
     * @return array like EE_Config class
     */
    public function get_ee_config()
    {
        return EE_Config::instance();
    }


    /**
     * Gets an array of php setup info, pilfered from http://www.php.net/manual/en/function.phpinfo.php#87463
     *
     * @return array like the output of phpinfo(), but in an array
     */
    public function get_php_info()
    {
        ob_start();
        phpinfo(-1);

        $pi = preg_replace(
            [
                '#^.*<body>(.*)</body>.*$#ms',
                '#<h2>PHP License</h2>.*$#ms',
                '#<h1>Configuration</h1>#',
                "#\r?\n#",
                "#</(h1|h2|h3|tr)>#",
                '# +<#',
                "#[ \t]+#",
                '#&nbsp;#',
                '#  +#',
                '# class=".*?"#',
                '%&#039;%',
                '#<tr>(?:.*?)" src="(?:.*?)=(.*?)" alt="PHP Logo" /></a>'
                . '<h1>PHP Version (.*?)</h1>(?:\n+?)</td></tr>#',
                '#<h1><a href="(?:.*?)\?=(.*?)">PHP Credits</a></h1>#',
                '#<tr>(?:.*?)" src="(?:.*?)=(.*?)"(?:.*?)Zend Engine (.*?),(?:.*?)</tr>#',
                "# +#",
                '#<tr>#',
                '#</tr>#',
            ],
            [
                '$1',
                '',
                '',
                '',
                '</$1>' . "\n",
                '<',
                ' ',
                ' ',
                ' ',
                '',
                ' ',
                '<h2>PHP Configuration</h2>' . "\n" . '<tr><td>PHP Version</td><td>$2</td></tr>' .
                "\n" . '<tr><td>PHP Egg</td><td>$1</td></tr>',
                '<tr><td>PHP Credits Egg</td><td>$1</td></tr>',
                '<tr><td>Zend Engine</td><td>$2</td></tr>' . "\n" .
                '<tr><td>Zend Egg</td><td>$1</td></tr>',
                ' ',
                '%S%',
                '%E%',
            ],
            ob_get_clean()
        );

        $sections = explode('<h2>', strip_tags((string) $pi, '<h2><th><td>'));
        unset($sections[0]);

        $pi = [];
        foreach ($sections as $section) {
            $n = substr($section, 0, strpos($section, '</h2>'));
            preg_match_all(
                '#%S%(?:<td>(.*?)</td>)?(?:<td>(.*?)</td>)?(?:<td>(.*?)</td>)?%E%#',
                $section,
                $askapache,
                PREG_SET_ORDER
            );
            foreach ($askapache as $m) {
                $m2 = isset($m[2]) ? $m[2] : null;
            }
            $pi[ $n ][ $m[1] ] = (! isset($m[3]) || $m2 == $m[3]) ? $m2 : array_slice($m, 2);
        }

        return $pi;
    }


    /**
     * Checks if site responds ot HTTPS
     *
     * @return boolean
     */
    public function get_https_enabled()
    {
        $home     = str_replace("http://", "https://", home_url());
        $response = wp_remote_get($home);
        if ($response instanceof WP_Error) {
            $error_string = '';
            foreach ($response->errors as $short_name => $description_array) {
                $error_string .= "<b>$short_name</b>: " . implode(", ", $description_array);
            }
            return $error_string;
        }
        return "ok!";
    }


    /**
     * Whether or not a .maintenance file is detected
     *
     * @return string descripting wp_maintenance_mode status
     */
    public function get_wp_maintenance_mode()
    {
        $opened = file_exists(ABSPATH . '.maintenance');
        return $opened ? sprintf(
            esc_html__(
                '%s.maintenance file detected.%s Wordpress may have a failed auto-update which could prevent Event Espresso from updating the database correctly.',
                'event_espresso'
            ),
            '<strong>',
            '</strong>'
        ) : esc_html__('.maintenance file not detected. WordPress is not in maintenance mode.', 'event_espresso');
    }


    /**
     * Whether or not logging is enabled
     *
     * @return string descripting logging's status
     */
    public function get_logging_enabled()
    {
        $opened = @fopen(EVENT_ESPRESSO_UPLOAD_DIR . '/logs/espresso_log.txt', 'a');
        return $opened
            ? esc_html__('Log Directory is writable', 'event_espresso')
            : sprintf(
                esc_html__('%sLog directory is NOT writable%s', 'event_espresso'),
                '<mark class="error"',
                '</mark>'
            );
    }


    /**
     *  Whether curl ro fsock works
     *
     * @return string describing posting's status
     */
    public function get_remote_posting()
    {
        $fsock_works = function_exists('fsockopen');
        $curl_works  = function_exists('curl_init');
        if ($fsock_works && $curl_works) {
            $status = esc_html__('Your server has fsockopen and cURL enabled.', 'event_espresso');
        } elseif ($fsock_works) {
            $status = esc_html__('Your server has fsockopen enabled, cURL is disabled.', 'event_espresso');
        } elseif ($curl_works) {
            $status = esc_html__('Your server has cURL enabled, fsockopen is disabled.', 'event_espresso');
        } else {
            $status = esc_html__(
                'Your server does not have fsockopen or cURL enabled - PayPal IPN and other scripts which communicate with other servers will not work. Contact your hosting provider.',
                'event_espresso'
            ) . '</mark>';
        }
        return $status;
    }


    /**
     * Gets all the php.ini settings
     *
     * @return array
     */
    public function get_php_ini_all()
    {
        return ini_get_all();
    }


    /**
     * Transforms the php.ini notation for numbers (like '2M') to an integer.
     *
     * @param type $size
     * @return int
     */
    public function let_to_num($size)
    {
        $l   = substr($size, -1);
        $ret = substr($size, 0, -1);
        // phpcs:disable PSR2.ControlStructures.SwitchDeclaration.TerminatingComment
        switch (strtoupper($l)) {
            case 'P':
                $ret *= 1024;
            case 'T':
                $ret *= 1024;
            case 'G':
                $ret *= 1024;
            case 'M':
                $ret *= 1024;
            case 'K':
                $ret *= 1024;
        }
        // phpcs:enable
        return $ret;
    }
}
