<?php

namespace CleantalkSP\SpbctWP\VulnerabilityAlarm;

use CleantalkSP\SpbctWP\VulnerabilityAlarm\Dto\ApiResults;
use CleantalkSP\SpbctWP\VulnerabilityAlarm\Dto\PluginReport;
use CleantalkSP\SpbctWP\VulnerabilityAlarm\Dto\ThemeReport;
use CleantalkSP\SpbctWP\VulnerabilityAlarm\Exceptions\VulnerabilityAlarmServiceException;

class VulnerabilityAlarm
{
    const VULNERABILITY_LIST = 'spbc_vulnerabilities';

    /**
     * @var array
     */
    public static $plugins = [];

    /**
     * @var array
     */
    public static $themes = [];

    /**
     * * * * Common * * *
     */

    /**
     * Get vulnerabilities from cloud and write to storage
     * @throws VulnerabilityAlarmServiceException
     */
    public static function updateWPModulesVulnerabilities()
    {
        VulnerabilityAlarmService::updateVulnerabilitiesLibrary();
    }

    /**
     * Send report to cloud about installed vulnerability modules on site
     * @psalm-suppress PossiblyUnusedMethod
     * @todo replace the suppressing above
     */
    public static function sendReport()
    {
    }

    /**
     * @param string $module_type
     * @param string $plugin_slug
     * @param string $plugin_id
     * @param string $psc_cert_name
     * @param string $research_url
     * @return string|null
     */
    public static function showSafeBadge($module_type = 'plugin', $plugin_slug = '', $plugin_id = '', $psc_cert_name = '', $research_url = '')
    {
        if ( ! empty($psc_cert_name) ) {
            return VulnerabilityAlarmView::showSafeBadgePSC($module_type, $plugin_slug, $plugin_id, $psc_cert_name, $research_url);
        }
        return null;
    }

    /**
     * * * * Plugins * * *
     */

    /**
     * @param $plugin_slug
     * @param $plugin_version
     * @return Dto\PluginReport|ThemeReport|false
     */
    public static function checkPluginVulnerabilityStatic($plugin_slug, $plugin_version)
    {
        /** @var ApiResults|null $list */
        $list = get_option(self::VULNERABILITY_LIST);
        if ( $list === false ) {
            return false;
        }
        static::$plugins = $list->plugins;
        return VulnerabilityAlarmService::getPluginReportStatic($plugin_slug, $plugin_version, false);
    }

    public static function getPluginAlarmHTML($plugin_file, $plugin_data)
    {
        return VulnerabilityAlarmView::showPluginAlarm($plugin_file, $plugin_data);
    }

    /**
     * Retrieves a list of plugins considered safe based on vulnerability reports.
     *
     * This method evaluates each plugin provided in the $plugins parameter against vulnerability reports
     * obtained from a cloud service. It first checks if there are any static reports available locally.
     * If not, or if the $refresh_static_data_before flag is set, it makes a call to the cloud service to
     * obtain the latest vulnerability reports for the plugins.
     *
     * Each plugin is then checked against these reports to determine if it is considered safe. A plugin is
     * deemed safe if it does not appear in the vulnerability reports or if it meets certain criteria specified
     * in the reports (e.g., being of a version that is not affected by known vulnerabilities).
     *
     * @param array $plugins An array of plugins to check. Each plugin should be an associative array containing
     *                       at least the plugin slug.
     * @param bool $refresh_static_data_before Whether to refresh the static data with the latest reports from
     *                                         the cloud service before performing the check. Defaults to true.
     * @return array An array of plugins that are considered safe. Each element in the array is an associative
     *               array containing the plugin's slug, its ID, any PSC certification
     *               it may have, and the full report object from the vulnerability check.
     */
    public static function getSafePlugins($plugins, $refresh_static_data_before = true)
    {
        $safe_plugins = array();
        $plugins_reports_to_use = array();

        //check if static reports available
        $static_reports = get_option(self::VULNERABILITY_LIST);
        if ( $static_reports instanceof ApiResults ) {
            $plugins_reports_to_use = $static_reports->plugins;
        }

        // if no static data provided or the flag is set, run api call before check
        if ( $refresh_static_data_before ) {
            $plugins_to_cloud = [];
            // Prepare data to the cloud call
            foreach ( $plugins as $plugin ) {
                $slug = static::getPluginSlug($plugin);
                if ( !empty($slug) ) {
                    $plugins_to_cloud[] = [$slug];
                }
            }
            // Get cloud report about plugins
            try {
                $api_report = VulnerabilityAlarmService::getReport(['plugins' => $plugins_to_cloud]);
                $plugins_reports_to_use = $api_report->plugins;
            } catch (\Exception $_exception) {
                return $safe_plugins;
            }
        }

        // Check if the cloud report contains any plugins.
        if ( count($plugins_reports_to_use) ) {
            // Update the static $plugins property with the report's plugin data.
            static::$plugins = $plugins_reports_to_use;
            // Iterate over each plugin provided to the method.
            foreach ( $plugins as $plugin ) {
                // Retrieve the slug of the current plugin.
                $plugin_slug = static::getPluginSlug($plugin);
                // Retrieve the version of the current plugin.
                $plugin_version = static::getPluginVersion($plugin);
                // Check if the current plugin is considered safe based on the cloud report.
                $safe_plugin_report = VulnerabilityAlarmService::getPluginReportStatic($plugin_slug, $plugin_version, true);
                // If the plugin is safe, proceed to add it to the list of safe plugins.
                if ( $safe_plugin_report ) {
                    // Retrieve the ID of the current plugin.
                    $plugin_id = static::getPluginReportId($safe_plugin_report);
                    // Get PSC if provided
                    $psc = static::getPluginReportPSC($safe_plugin_report);
                    // Add the plugin to the list of safe plugins with its slug and ID.
                    $safe_plugins[] = [
                        'slug' => $plugin_slug,
                        'id' => $plugin_id,
                        'psc' => $psc,
                        'report_object' => $safe_plugin_report,
                        'current_version' => $plugin_version
                    ];
                }
            }
        }
        return $safe_plugins;
    }

    /**
     * Retrieves a list of plugins with a valid PSC (Plugin Security Certificate) only.
     *
     * This method first gathers a list of all installed plugins using the get_plugins() function. It then filters
     * these plugins through the getSafePlugins() method to identify which of them are considered safe based on
     * vulnerability reports. Among these safe plugins, it further filters to return only those with a valid PSC.
     *
     * Each plugin in the returned list includes its ID, slug, PSC, and the minimum safe version as determined by
     * the vulnerability reports. This method is useful for scenarios where you need to display or process only
     * plugins that have been certified for security.
     *
     * @return array An array of plugins that are considered safe and have a valid PSC. Each element in the array
     *               is an associative array containing the plugin's ID, slug, PSC, and version.
     */
    public static function getPSCSafePluginsOnly()
    {
        $installed_plugins = get_plugins();
        $safe_plugins = VulnerabilityAlarm::getSafePlugins($installed_plugins, false);

        $psc_safe_plugins = array();
        // Iterate over each plugin considered safe
        foreach ($safe_plugins as $safe_plugin) {
            // extract array item to report object
            $report_object = isset($safe_plugin['report_object']) && $safe_plugin['report_object'] instanceof PluginReport
                ? $safe_plugin['report_object']
                : false;
            $psc = $report_object
                ? $report_object->psc
                : '';
            $psc_safe_plugins[] = array(
                'id' => $safe_plugin['id'],
                'slug' => $safe_plugin['slug'],
                'psc' => $psc,
                'version' => $safe_plugin['current_version']
            );
        }
        return $psc_safe_plugins;
    }

    /**
     * Safely get plugin report ID that provided form API response
     * @param PluginReport $plugin_report
     * @return string
     */
    public static function getPluginReportId($plugin_report)
    {
        $id = '';

        if ( empty($plugin_report) || ! $plugin_report instanceof PluginReport) {
            return $id;
        }

        if ( property_exists($plugin_report, 'id')) {
            $id = $plugin_report->id;
        }
        return sanitize_title($id);
    }

    /**
     * Safely get plugin report PSC that provided form API response
     * @param PluginReport $plugin_report
     * @return string
     */
    public static function getPluginReportPSC($plugin_report)
    {
        $psc = '';
        if (!empty($plugin_report->psc) && $plugin_report->app_status === 'safe_psc') {
            $psc = $plugin_report->psc;
        }
        return esc_html($psc);
    }

    /**
     * Safely get plugin slug from provided dataset
     * @@param array|object $plugin Array if provided WP plugin dataset as array, object otherwise
     * @return string
     */
    private static function getPluginSlug($plugin)
    {
        $slug = '';
        if ( !is_array($plugin) ) {
            if ( gettype($plugin) === 'object' ) {
                if ( property_exists($plugin, 'slug') ) {
                    $slug = $plugin->slug;
                } elseif ( property_exists($plugin, 'name') ) {
                    $slug = $plugin->name;
                }
            }
        } else {
            $slug = isset($plugin['slug']) ? (string)$plugin['slug'] : sanitize_title($plugin['Name']);
        }
        return $slug;
    }

    /**
     * @param array|object $plugin Array if provided WP plugin dataset as array, object otherwise
     * @return string
     */
    private static function getPluginVersion($plugin)
    {
        $version = '';
        if ( !is_array($plugin) ) {
            if ( gettype($plugin) === 'object' ) {
                if (property_exists($plugin, 'version')) {
                    $version = $plugin->version;
                }
            }
        } else {
            $version = isset($plugin['version']) ? (string)$plugin['version'] : '';
            $version = empty($version) && isset($plugin['Version']) ? (string)$plugin['Version'] : $version;
        }
        return esc_html($version);
    }

    /**
     * Checks a single plugin's version against a cloud-based vulnerability report.
     *
     * This method attempts to retrieve a vulnerability report for a specific plugin module from a cloud service.
     * It sends the plugin module information to the cloud service and expects to receive a report detailing
     * any known vulnerabilities for that module. If the report is successfully retrieved, the method then checks
     * if the provided version of the plugin is listed as vulnerable within the report. This is done by delegating
     * the version check to the VulnerabilityAlarmService::isModuleVersionIsVulnerable method, which evaluates
     * the plugin version against the vulnerabilities listed in the report.
     *
     * @param string $module The slug or identifier of the plugin module to check.
     * @param string $version The version of the plugin module to evaluate for vulnerabilities.
     * @return bool Returns true if the plugin version is found to be vulnerable based on the cloud report,
     *              false otherwise. Also returns false if the report could not be retrieved or if the plugin
     *              is not found within the report.
     */
    public static function checkSinglePluginViaAPI($module, $version)
    {
        // Get cloud report about plugin
        try {
            $report = VulnerabilityAlarmService::getReport([[[$module]]]);
        } catch (\Exception $_exception) {
            return false;
        }

        if (!isset($report->plugins[0])) {
            return false;
        }

        return VulnerabilityAlarmService::isModuleVersionIsVulnerable($report->plugins[0], $version);
    }

    /**
     * * * * Themes * * *
     */

    /**
     * @param string $theme_slug
     * @param string $theme_version
     *
     * @return Dto\PluginReport|ThemeReport|false|mixed
     */
    public static function checkThemeVulnerabilityStatic($theme_slug, $theme_version)
    {
        /** @var ApiResults|null $list */
        $list = get_option(self::VULNERABILITY_LIST);
        if ( $list === false ) {
            return false;
        }
        static::$themes = $list->themes;
        return VulnerabilityAlarmService::getThemeReportStatic($theme_slug, $theme_version);
    }

    /**
     * Displays an alarm for a theme identified as vulnerable.
     *
     * This method is a wrapper around the VulnerabilityAlarmView::showThemeAlarm() method. It is responsible for
     * displaying an alarm for a theme that has been identified as vulnerable. The method takes a theme report object
     * as its parameter, which contains details about the theme's vulnerability. The actual display logic and UI rendering
     * are handled by the VulnerabilityAlarmView::showThemeAlarm() method, to which this method delegates by passing
     * the theme report object.
     *
     * @param ThemeReport $theme_report An object containing the theme's details, including its vulnerability information.
     * @return string The string result of the VulnerabilityAlarmView::showThemeAlarm() method call
     */
    public static function getThemeAlarmHTML($theme_report)
    {
        return VulnerabilityAlarmView::showThemeAlarm($theme_report);
    }

    /**
     * @param array $themes
     *
     * @return array
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function getSafeThemesViaApi($themes)
    {
        $theme_names = array();
        for ($i = 0; $i < count($themes); $i++) {
            $arr_slug = [$themes[$i]];
            array_push($theme_names, $arr_slug);
        }
        return VulnerabilityAlarmService::getSafeThemesViaAPI($theme_names);
    }
}
