<?php

declare(strict_types=1);

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

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

/**
 * Immutable data transfer object representing a vulnerable version range.
 *
 * A vulnerability range describes *which versions* of a component are affected
 * by a specific vulnerability and how that range should be interpreted.
 *
 * This DTO is used to:
 * - Represent vulnerability range data received from external APIs.
 * - Transport normalized range metadata through the application.
 * - Serialize range data for storage or further processing.
 *
 * This object contains data only:
 * - No business logic
 * - No persistence logic
 * - No version comparison logic
 */
final class VulnerabilityRangeDto
{
    /**
     * Identifier used to match this range to a specific component/version set
     */
    private string $lookup;

    /**
     * Severity label associated with this range
     */
    private string $severity;

    /**
     * Publication date of the vulnerability (raw API value)
     */
    private string $publishedAt;

    /**
     * Whether the vulnerability is fixed within this range
     */
    private bool $fixedIn;

    /**
     * Lower bound of the affected version range
     */
    private string $versionFrom;

    /**
     * Upper bound of the affected version range
     */
    private string $versionTo;

    /**
     * Whether the lower bound is inclusive
     */
    private bool $fromInclusive;

    /**
     * Whether the upper bound is inclusive
     */
    private bool $toInclusive;

    public function __construct(
        string $lookup,
        string $severity,
        string $publishedAt,
        bool $fixedIn,
        string $versionFrom,
        string $versionTo,
        bool $fromInclusive,
        bool $toInclusive
    ) {
        $this->toInclusive = $toInclusive;
        $this->fromInclusive = $fromInclusive;
        $this->versionTo = $versionTo;
        $this->versionFrom = $versionFrom;
        $this->fixedIn = $fixedIn;
        $this->publishedAt = $publishedAt;
        $this->severity = $severity;
        $this->lookup = $lookup;
    }

    /**
     * Builds a range DTO from a raw API payload.
     *
     * This method normalizes raw API payloads into a strongly typed DTO.
     * Missing or malformed values are handled defensively via the Storage helper.
     *
     * @param array<string, mixed> $payload
     */
    public static function fromApiArray(array $payload): self
    {
        $storage = new Storage($payload);

        return new self(
            $storage->getString('lookup'),
            $storage->getString('severity'),
            $storage->getString('published_at'),
            $storage->getBoolean('fixed_in'),
            $storage->getString('version_from'),
            $storage->getString('version_to'),
            $storage->getBoolean('from_inclusive'),
            $storage->getBoolean('to_inclusive'),
        );
    }

    /**
     * @return array{
     *     lookup: string,
     *     severity: string,
     *     published_at: string,
     *     fixed_in: bool,
     *     version_from: string,
     *     version_to: string,
     *     from_inclusive: bool,
     *     to_inclusive: bool
     * }
     */
    public function toArray(): array
    {
        return [
            'lookup' => $this->lookup,
            'severity' => $this->severity,
            'published_at' => $this->publishedAt,
            'fixed_in' => $this->fixedIn,
            'version_from' => $this->versionFrom,
            'version_to' => $this->versionTo,
            'from_inclusive' => $this->fromInclusive,
            'to_inclusive' => $this->toInclusive,
        ];
    }

    /**
     * Returns the severity label associated with this range.
     */
    public function getSeverity(): string
    {
        return $this->severity;
    }

    /**
     * Returns the identifier used to match this range to a specific component/version set.
     */
    public function getLookup(): string
    {
        return $this->lookup;
    }
}
