using System;
using System.Text.RegularExpressions;

namespace StellaOps.Excititor.Connectors.OCI.OpenVEX.Attest.Discovery;

internal static class OciImageReferenceParser
{
    private static readonly Regex DigestRegex = new(@"^(?<algorithm>[A-Za-z0-9+._-]+):(?<hash>[A-Fa-f0-9]{32,})$", RegexOptions.Compiled | RegexOptions.CultureInvariant);
    private static readonly Regex RepositoryRegex = new(@"^[a-z0-9]+(?:(?:[._]|__|[-]*)[a-z0-9]+)*(?:/[a-z0-9]+(?:(?:[._]|__|[-]*)[a-z0-9]+)*)*$", RegexOptions.Compiled | RegexOptions.CultureInvariant);

    public static OciImageReference Parse(string reference)
    {
        if (string.IsNullOrWhiteSpace(reference))
        {
            throw new InvalidOperationException("OCI reference cannot be empty.");
        }

        var trimmed = reference.Trim();
        string original = trimmed;

        var scheme = "https";
        if (trimmed.StartsWith("oci://", StringComparison.OrdinalIgnoreCase))
        {
            trimmed = trimmed.Substring("oci://".Length);
        }

        if (trimmed.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
        {
            trimmed = trimmed.Substring("https://".Length);
            scheme = "https";
        }
        else if (trimmed.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
        {
            trimmed = trimmed.Substring("http://".Length);
            scheme = "http";
        }

        var firstSlash = trimmed.IndexOf('/');
        if (firstSlash <= 0)
        {
            throw new InvalidOperationException($"OCI reference '{reference}' must include a registry and repository component.");
        }

        var registry = trimmed[..firstSlash];
        var remainder = trimmed[(firstSlash + 1)..];

        if (!LooksLikeRegistry(registry))
        {
            throw new InvalidOperationException($"OCI reference '{reference}' is missing an explicit registry component.");
        }

        string? digest = null;
        string? tag = null;

        var digestIndex = remainder.IndexOf('@');
        if (digestIndex >= 0)
        {
            digest = remainder[(digestIndex + 1)..];
            remainder = remainder[..digestIndex];

            if (!DigestRegex.IsMatch(digest))
            {
                throw new InvalidOperationException($"Digest segment '{digest}' is not a valid OCI digest.");
            }
        }

        var tagIndex = remainder.LastIndexOf(':');
        if (tagIndex >= 0)
        {
            tag = remainder[(tagIndex + 1)..];
            remainder = remainder[..tagIndex];

            if (string.IsNullOrWhiteSpace(tag))
            {
                throw new InvalidOperationException("OCI tag segment cannot be empty.");
            }

            if (tag.Contains('/', StringComparison.Ordinal))
            {
                throw new InvalidOperationException("OCI tag segment cannot contain '/'.");
            }
        }

        var repository = remainder;
        if (string.IsNullOrWhiteSpace(repository))
        {
            throw new InvalidOperationException("OCI repository segment cannot be empty.");
        }

        if (!RepositoryRegex.IsMatch(repository))
        {
            throw new InvalidOperationException($"Repository segment '{repository}' is not valid per OCI distribution rules.");
        }

        return new OciImageReference(
            Registry: registry,
            Repository: repository,
            Tag: tag,
            Digest: digest,
            OriginalReference: original,
            Scheme: scheme);
    }

    private static bool LooksLikeRegistry(string value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return false;
        }

        if (value.Equals("localhost", StringComparison.OrdinalIgnoreCase))
        {
            return true;
        }

        if (value.Contains('.', StringComparison.Ordinal) || value.Contains(':', StringComparison.Ordinal))
        {
            return true;
        }

        // IPv4/IPv6 simplified check
        if (value.Length >= 3 && char.IsDigit(value[0]))
        {
            return true;
        }

        return false;
    }
}
