<?php

declare(strict_types=1);

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

use ReallySimplePlugins\RSS\Core\Support\Helpers\Storage;

/**
 * Immutable-style data transfer object representing all vulnerabilities
 * for a single installed component.
 *
 * A component can be a plugin, theme, or WordPress core. This DTO groups:
 * - Component identity (name, slug, type).
 * - Known vulnerability ranges affecting this component.
 * - Runtime-enriched data such as installed version, update availability,
 *   and latest known version.
 *
 * It is primarily used to:
 * - Transport vulnerability data between API, storage, and domain layers.
 * - Enrich vulnerability data with local installation context.
 *
 * This object holds state only:
 * - No persistence logic
 * - No policy or decision logic
 * - No scheduling or WordPress hook concerns
 */
final class ComponentVulnerabilitiesDto
{
    /**
     * Human-readable component name.
     */
    private string $name;

    /**
     * Unique component identifier used for lookups.
     */
    private string $slug;

    /**
     * Component type (`plugin`, `theme`, or `core`).
     */
    private string $type;

    /**
     * Latest known available version (null if unknown).
     */
    private ?string $latestVersion = null;

    /**
     * List of vulnerability ranges affecting this component.
     *
     * @var list<VulnerabilityRangeDto>
     */
    public array $vulnerabilities = [];

    /**
     * Currently installed version on the site.
     */
    public string $installedVersion = '';

    /**
     * Whether an update is available for the installed version.
     */
    private ?bool $updateAvailable = null;

    /**
     * Constructor.
     *
     * @param string $name Component name from the API.
     * @param string $slug Component slug from the API.
     * @param string $type Component type from the API.
     * @param list<VulnerabilityRangeDto> $vulnerabilities Vulnerability ranges from the API.
     *
     * Runtime fields like installed version and update availability
     * are populated later via setters.
     */
    public function __construct(
        string $name,
        string $slug,
        string $type,
        array $vulnerabilities
    ) {
        $this->vulnerabilities = $vulnerabilities;
        $this->type = $type;
        $this->slug = $slug;
        $this->name = $name;
    }

    /**
     * Normalize a raw API component payload into a strongly typed DTO.
     *
     * Invalid or unexpected vulnerability entries are skipped defensively.
     *
     * @param array<string, mixed> $payload Raw API component data.
     *
     * @return self
     */
    public static function fromApiComponentArray(array $payload): self
    {
        $storage = new Storage($payload);
        $name = $storage->getString('name');
        $slug = $storage->getString('slug');
        $type = strtolower($storage->getString('type'));

        $vulnerabilities = [];

        $rawVulnerabilities = $payload['vulnerabilities'] ?? [];

        if (is_array($rawVulnerabilities)) {
            foreach ($rawVulnerabilities as $rawVulnerability) {
                if (!is_array($rawVulnerability)) {
                    continue;
                }

                $vulnerabilities[] = VulnerabilityRangeDto::fromApiArray($rawVulnerability);
            }
        }

        return new self(
            $name,
            $slug,
            $type,
            $vulnerabilities,
        );
    }

    /**
     * Return a storage/serialization-friendly representation of this component and its vulnerabilities.
     *
     * Nested vulnerabilities are converted using their own DTOs.
     *
     * @return array{
     *     name: string,
     *     slug: string,
     *     type: string,
     *     latestVersion: string|null,
     *     vulnerabilities: list<array<string, mixed>>
     * }
     */
    public function toArray(): array
    {
        $list = [];

        foreach ($this->vulnerabilities as $vulnerability) {
            $list[] = $vulnerability->toArray();
        }

        return [
            'name' => $this->name,
            'slug' => $this->slug,
            'type' => $this->type,
            'latestVersion' => $this->latestVersion,
            'vulnerabilities' => $list,
        ];
    }

    /**
     * Set the latest known available version for this component.
     *
     * Used to enrich the DTO with runtime update information.
     */
    public function setLatestVersion(?string $latestVersion): void
    {
        $this->latestVersion = $latestVersion;
    }

    /**
     * Get a unique storage key for this component.
     *
     * Combines type and slug for indexing.
     */
    public function getStorageKey(): string
    {
        return $this->type . ':' . $this->slug;
    }

    /**
     * Update the component type.
     *
     * Used to enrich the DTO with runtime data.
     */
    public function setType(string $type): void
    {
        $this->type = $type;
    }

    /**
     * Update the component slug.
     *
     * Used to enrich the DTO with runtime data.
     */
    public function setSlug(string $installedSlug): void
    {
        $this->slug = $installedSlug;
    }

    /**
     * Update the component name.
     *
     * Used to enrich the DTO with runtime data.
     */
    public function setName(string $installedName): void
    {
        $this->name = $installedName;
    }

    /**
     * Set the currently installed version on the site.
     *
     * Used to enrich the DTO with runtime installation data.
     */
    public function setInstalledVersion(string $installedVersion): void
    {
        $this->installedVersion = $installedVersion;
    }

    /**
     * Set whether an update is available for the installed version.
     *
     * Used to enrich the DTO with runtime update status.
     */
    public function setUpdateAvailable(?bool $updateAvailable): void
    {
        $this->updateAvailable = $updateAvailable;
    }
}
