<?php

namespace ReallySimplePlugins\RSS\Core\Features\Vulnerability\Controllers;

use ReallySimplePlugins\RSS\Core\Features\Vulnerability\Repositories\VulnerabilityStorageRepository;
use ReallySimplePlugins\RSS\Core\Interfaces\ControllerInterface;
use ReallySimplePlugins\RSS\Core\Support\Helpers\Storage;
use ReallySimplePlugins\RSS\Core\Features\Vulnerability\Services\Policies\ConfigurableSeverityPolicy;

/**
 * Integrates vulnerability-related notices into the plugin notice system.
 *
 * This controller is responsible only for UI notice integration:
 * - It hooks into the `rsssl_notices` filter and adds notice definitions.
 * - It reads notification thresholds from plugin options.
 * - It delegates vulnerability lookups to the storage repository.
 *
 * No scanning, persistence, or severity calculation is performed here.
 * The repository is treated as the source of truth for stored vulnerabilities.
 */
final class VulnerabilityNoticeController implements ControllerInterface
{
    private VulnerabilityStorageRepository $vulnerabilityStorageRepository;

    /**
     * Constructor.
     *
     * Injects the vulnerability storage repository dependency.
     * No processing or data fetching occurs during construction.
     */
    public function __construct(
        VulnerabilityStorageRepository $vulnerabilityStorageRepository
    ) {
        $this->vulnerabilityStorageRepository = $vulnerabilityStorageRepository;
    }

    /**
     * Registers the `rsssl_notices` hook to add vulnerability notices.
     *
     * This method is called once during plugin boot.
     */
    public function register(): void
    {
        add_filter('rsssl_notices', [$this, 'showHelpNotices'], 10, 1);
    }

    /**
     * Adds vulnerability notices based on stored vulnerabilities and configured thresholds.
     *
     * WordPress passes the current notice registry as an array. This method returns the
     * modified registry with zero or more vulnerability notices appended.
     *
     * Threshold behavior:
     * - Dashboard threshold controls whether the notice is shown on the plugin dashboard.
     * - Sitewide threshold controls whether the notice is shown as a WordPress admin notice.
     * - A threshold of `*` disables notices for that scope.
     *
     * @param array<string, mixed> $notices Existing notice registry.
     *
     * @return array<string, mixed> Updated notice registry.
     */
    public function showHelpNotices(array $notices): array
    {
        $severityCounts = [
            'low' => 0,
            'medium' => 0,
            'high' => 0,
            'critical' => 0,
        ];
        $dashboardThreshold = rsssl_get_option('vulnerability_notification_dashboard');
        $sitewideThreshold = rsssl_get_option('vulnerability_notification_sitewide');

        $severities = $this->vulnerabilityStorageRepository->getComponentCountPerHighestSeverity();
        $timeStamp = time();
        foreach ($severities as $severity => $count) {
            $normalizedSeverity = strtolower($severity);
            $uniqueCode = md5($severity . $timeStamp);
            $title = $this->getWarningString($normalizedSeverity, $count);
            if ($severity === '') {
                continue;
            }

            $siteWide = false;
            $normalizedSeverity = strtolower($severity);
            if (!isset($severityCounts[$normalizedSeverity])) {
                continue;
            }
            $dashboardNotice = false;

            // Convert severity labels to comparable numeric scores.
            $severityScore = ConfigurableSeverityPolicy::SEVERITY_SCORES[strtolower($severity)] ?? 0;
            $dashboardScore = ConfigurableSeverityPolicy::SEVERITY_SCORES[strtolower((string) $dashboardThreshold)] ?? 0;
            $sitewideScore = ConfigurableSeverityPolicy::SEVERITY_SCORES[strtolower((string) $sitewideThreshold)] ?? 0;

            if ($dashboardThreshold && $dashboardThreshold !== '*' && $severityScore >= $dashboardScore) {
                $dashboardNotice = true;
            }

            if ($sitewideThreshold && $sitewideThreshold !== '*' && $severityScore >= $sitewideScore) {
                $siteWide = true;
            }
            if (!$dashboardNotice && !$siteWide) {
                continue;
            }

            $notices['risk_level_' . $normalizedSeverity . $uniqueCode] = [
                'callback'          => '_true_',
                'score'             => 30,
                'show_with_options' => ['enable_vulnerability_scanner'],
                'output'            => [
                    'true' => [
                        'title'             => $title,
                        'msg'               => $title . ' ' . __(
                                'Please take appropriate action.',
                                'really-simple-ssl'
                            ),
                        'icon'              => ($normalizedSeverity === 'critical' || $normalizedSeverity === 'high') ? 'warning' : 'open',
                        'type'              => 'warning',
                        'dismissible'       => true,
                        'admin_notice'      => $siteWide,
                        'plusone'           => true,
                        'highlight_field_id' => 'vulnerabilities-overview',
                    ],
                ],
            ];
        }

        return $notices;
    }

    /**
     * Builds the translated notice title for a given severity level and count.
     *
     * Uses WordPress pluralization (`_n`) to return a human-friendly message.
     *
     * @param non-empty-string $severity Normalized severity key (low|medium|high|critical).
     * @param positive-int $count Number of components with that severity as highest.
     */
    private function getWarningString(string $severity, int $count): string
    {
        switch ($severity) {
            case 'critical':
                $warning = sprintf(_n(
                    'You have %s critical vulnerability',
                    'You have %s critical vulnerabilities',
                    $count,
                    'really-simple-ssl'
                ), $count);
                break;
            case 'high':
                $warning = sprintf(_n(
                    'You have %s high-risk vulnerability',
                    'You have %s high-risk vulnerabilities',
                    $count,
                    'really-simple-ssl'
                ), $count);
                break;
            case 'medium':
                $warning = sprintf(_n(
                    'You have %s medium-risk vulnerability',
                    'You have %s medium-risk vulnerabilities',
                    $count,
                    'really-simple-ssl'
                ), $count);
                break;
            default:
                $warning = sprintf(_n(
                    'You have %s low-risk vulnerability',
                    'You have %s low-risk vulnerabilities',
                    $count,
                    'really-simple-ssl'
                ), $count);
                break;
        }
        return $warning;
    }
}
