<?php

declare(strict_types=1);

final class MyronVersionComparator
{
    /**
     * @return array{major:int, minor:int, patch:int, suffix:string, valid:bool}
     */
    public function parse(string $version): array
    {
        $version = trim($version);
        if (!preg_match('/^v?(\\d+)\\.(\\d+)\\.(\\d+)(?:-([A-Za-z][A-Za-z0-9.-]*))?$/', $version, $matches)) {
            return [
                'major' => 0,
                'minor' => 0,
                'patch' => 0,
                'suffix' => '',
                'valid' => false,
            ];
        }

        return [
            'major' => (int) $matches[1],
            'minor' => (int) $matches[2],
            'patch' => (int) $matches[3],
            'suffix' => strtolower((string) ($matches[4] ?? '')),
            'valid' => true,
        ];
    }

    public function compare(string $left, string $right): int
    {
        $a = $this->parse($left);
        $b = $this->parse($right);

        if (!$a['valid'] || !$b['valid']) {
            throw new InvalidArgumentException('Version comparison requires vMAJOR.MINOR.PATCH values with optional suffixes.');
        }

        foreach (['major', 'minor', 'patch'] as $key) {
            $comparison = $a[$key] <=> $b[$key];
            if ($comparison !== 0) {
                return $comparison;
            }
        }

        if ($a['suffix'] === $b['suffix']) {
            return 0;
        }

        if ($a['suffix'] === '') {
            return 1;
        }

        if ($b['suffix'] === '') {
            return -1;
        }

        return strcmp($a['suffix'], $b['suffix']) <=> 0;
    }

    public function isNewer(string $candidate, string $installed): bool
    {
        return $this->compare($candidate, $installed) > 0;
    }

    public function isAtLeast(string $candidate, string $minimum): bool
    {
        return $this->compare($candidate, $minimum) >= 0;
    }

    public function normalize(string $version): string
    {
        $parsed = $this->parse($version);
        if (!$parsed['valid']) {
            throw new InvalidArgumentException('Invalid version format: ' . $version);
        }

        $normalized = 'v' . $parsed['major'] . '.' . $parsed['minor'] . '.' . $parsed['patch'];
        if ($parsed['suffix'] !== '') {
            $normalized .= '-' . $parsed['suffix'];
        }

        return $normalized;
    }
}
