<?php

declare(strict_types=1);

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

use ReallySimplePlugins\RSS\Core\Features\Vulnerability\Repositories\VulnerabilitySnapshotRepository;
use ReallySimplePlugins\RSS\Core\Features\Vulnerability\Repositories\VulnerabilityStorageRepository;
use ReallySimplePlugins\RSS\Core\Features\Vulnerability\Services\Policies\ConfigurableSeverityPolicy;

/**
 * Executes post-sync vulnerability notification logic.
 *
 * This service is scheduled and executed by the core scheduler after a
 * vulnerability sync has completed, and may also be triggered by a
 * daily cron safety net.
 *
 * Responsibilities:
 * - Build a snapshot of current vulnerabilities from storage.
 * - Compare it with the previously stored snapshot.
 * - Decide whether a notification should be sent using the configured policy.
 * - Send the vulnerability notification email when required.
 * - Persist the current snapshot for future comparisons (always).
 *
 * This service contains no scheduling logic and no sync logic.
 * It is safe to run multiple times and is designed to be idempotent.
 */
final class VulnerabilityAfterSyncService
{
    /**
     * Source of persisted vulnerability data
     */
    private VulnerabilityStorageRepository $storage;

    /**
     * Manages snapshot persistence and retrieval
     */
    private VulnerabilitySnapshotRepository $snapshots;

    /**
     * Decides if/when notifications may be sent
     */
    private ConfigurableSeverityPolicy $policy;

    /**
     * Responsible for composing and sending emails
     */
    private VulnerabilityEmailService $email;

    /**
     * VulnerabilityAfterSyncService constructor.
     *
     * Dependencies are injected here; no work is performed during construction.
     *
     * @param VulnerabilityStorageRepository $storage Source of persisted vulnerability data
     * @param VulnerabilitySnapshotRepository $snapshots Manages snapshot persistence and retrieval
     * @param ConfigurableSeverityPolicy $policy Decides if/when notifications may be sent
     * @param VulnerabilityEmailService $email Responsible for composing and sending emails
     */
    public function __construct(
        VulnerabilityStorageRepository $storage,
        VulnerabilitySnapshotRepository $snapshots,
        ConfigurableSeverityPolicy $policy,
        VulnerabilityEmailService $email
    ) {
        $this->storage = $storage;
        $this->snapshots = $snapshots;
        $this->policy = $policy;
        $this->email = $email;
    }

    /**
     * Runs the post-sync notification decision flow.
     *
     * This method is invoked by the scheduler once the vulnerability
     * sync process has completed (or via a scheduled safety-net run).
     *
     * The current vulnerability snapshot is always persisted, even if
     * notification sending fails, to ensure idempotent behavior on
     * subsequent runs.
     *
     * Any exceptions during notification sending are caught and logged
     * to prevent breaking admin or cron execution flows.
     */
    public function run(): void
    {
        $previous = $this->snapshots->getLatest();
        $current = $this->snapshots->buildFromStorage($this->storage);
        try {
            if ($this->policy->canSend() && $this->policy->shouldSend($current, $previous)) {
                $this->email->sendVulnerabilityNotification($current);
            }
        } catch (\Throwable $e) {
            if (function_exists('error_log')) {
                error_log('RSS Vulnerability notification failed: ' . $e->getMessage());
            }
        } finally {
            $this->snapshots->save($current);
        }

        /**
         * Log the reason for the decision in debug mode.
         */
        if (defined('WP_DEBUG') && WP_DEBUG) {
            error_log($this->policy->getReason());
        }
    }
}
