<?php

declare (strict_types=1);
namespace Syde\Vendor\Cawl\Dhii\Versions;

use Syde\Vendor\Cawl\Dhii\Package\Version\VersionInterface;
use DomainException;
use Exception;
use RangeException;
use RuntimeException;
use Stringable;
/**
 * A value object containing information about a SemVer-compliant version.
 */
class Version implements VersionInterface
{
    /**
     * @var int
     */
    protected $major;
    /**
     * @var int
     */
    protected $minor;
    /**
     * @var int
     */
    protected $patch;
    /**
     * @var string[]
     */
    protected $preRelease;
    /**
     * @var string[]
     */
    protected $build;
    /**
     * @param int $major The major version number. See {@see getMajor()}.
     * @param int $minor The minor version number See {@see getMinor()}.
     * @param int $patch The patch version number. See {@see getPatch()}.
     * @param array<string|Stringable> $preRelease A list of pre-release identifiers. See {@see getPreRelease()}.
     * @param array<string|Stringable> $build A list of build identifiers. See {@see getBuild()}.
     *
     * @throws RangeException If an identifier is malformed
     * @throws Exception If problem creating.
     */
    public function __construct(int $major, int $minor, int $patch, array $preRelease, array $build)
    {
        $this->major = $major;
        $this->minor = $minor;
        $this->patch = $patch;
        $this->preRelease = $this->normalizePreRelease($preRelease);
        $this->build = $this->normalizeBuild($build);
    }
    /**
     * @inheritDoc
     */
    public function getMajor() : int
    {
        return $this->major;
    }
    /**
     * @inheritDoc
     */
    public function getMinor() : int
    {
        return $this->minor;
    }
    /**
     * @inheritDoc
     */
    public function getPatch() : int
    {
        return $this->patch;
    }
    /**
     * @inheritDoc
     */
    public function getPreRelease() : array
    {
        return $this->preRelease;
    }
    /**
     * @inheritDoc
     */
    public function getBuild() : array
    {
        return $this->build;
    }
    /**
     * Normalizes an identifier.
     *
     * @param string $identifier The identifier to normalize.
     *                           Must be a non-empty alphanumeric+hyphen string.
     *
     * @return string An identifier with all disallowed characters removed.
     *
     * @throws DomainException If identifier is malformed
     * @throws Exception If problem normalizing.
     */
    protected function normalizeIdentifier(string $identifier) : string
    {
        $origIdentifier = $identifier;
        $identifier = $this->replace('![^\\d\\w-]!', '', $identifier);
        if (!\strlen($identifier)) {
            throw new DomainException(\sprintf('Identifier "%1$s" normalized to "%2$s" is empty', $origIdentifier, $identifier));
        }
        return $identifier;
    }
    /**
     * Normalizes a series of pre-release identifiers.
     *
     * Will remove all illegal characters.
     *
     * @param iterable|array<string|Stringable> $preRelease The series of identifiers to normalize.
     *                             Each is a non-empty alphanumeric+hyphen string.
     *                             If numeric, leading zeroes are not allowed.
     *
     * @return string[] A series of normalized pre-release identifiers.
     *
     * @throws RangeException If could not normalize.
     * @throws Exception If problem normalizing.
     */
    protected function normalizePreRelease(iterable $preRelease) : array
    {
        $normalized = [];
        foreach ($preRelease as $idx => $identifier) {
            $identifier = (string) $identifier;
            try {
                $identifier = $this->normalizeIdentifier($identifier);
            } catch (DomainException $e) {
                throw new RangeException(\sprintf('Pre-release identifier #%1$d "%2$s" cannot be normalized', $idx, $identifier), 0, $e);
            }
            if (\is_numeric($identifier)) {
                $identifier = (string) \intval($identifier);
            }
            $normalized[] = $identifier;
        }
        return $normalized;
    }
    /**
     * Normalizes a series of build identifiers.
     *
     * Will remove all illegal characters.
     *
     * @param iterable|array<string|Stringable> $build The series of identifiers to normalize.
     *                             Each is a non-empty alphanumeric+hyphen string.
     *
     * @return string[] A series of normalized build identifiers.
     *
     * @throws RangeException If could not normalize.
     * @throws Exception If problem normalizing.
     */
    protected function normalizeBuild(iterable $build) : array
    {
        $normalized = [];
        foreach ($build as $idx => $identifier) {
            $identifier = (string) $identifier;
            try {
                $identifier = $this->normalizeIdentifier($identifier);
            } catch (DomainException $e) {
                throw new RangeException(\sprintf('Build identifier #%1$d "%2$s" cannot be normalized', $idx, $identifier), 0, $e);
            }
            $normalized[] = $identifier;
        }
        return $normalized;
    }
    /**
     * Replaces occurrences of $pattern in $subject.
     *
     * @param string $pattern The pattern to use for replacing.
     * @param string $replacement The replacement.
     * @param string $subject The subject.
     *
     * @return string The result of replacement.
     *
     * @throws Exception If problem replacing.
     */
    protected function replace(string $pattern, string $replacement, string $subject) : string
    {
        $result = \preg_replace($pattern, $replacement, $subject);
        if ($result === null) {
            $code = \preg_last_error();
            $code = $code ? $code : 0;
            $message = \preg_last_error_msg();
            $message = !empty($message) ? $message : 'Could not replace';
            throw new RuntimeException($message, $code);
        }
        return $result;
    }
    /**
     * @inheritDoc
     */
    public function __toString()
    {
        $version = "{$this->major}.{$this->minor}.{$this->patch}";
        if (\count($this->preRelease)) {
            $version .= '-' . \implode('.', $this->preRelease);
        }
        if (\count($this->build)) {
            $version .= '+' . \implode('.', $this->build);
        }
        return $version;
    }
}
