using System;
using System.Collections.Generic;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using StellaOps.Vexer.Core;
using StellaOps.Vexer.Policy;
using Xunit;

namespace StellaOps.Vexer.Policy.Tests;

public sealed class VexPolicyProviderTests
{
    [Fact]
    public void GetSnapshot_UsesDefaultsWhenOptionsMissing()
    {
        var provider = new VexPolicyProvider(
            new OptionsMonitorStub(new VexPolicyOptions()),
            NullLogger<VexPolicyProvider>.Instance);

        var snapshot = provider.GetSnapshot();

        Assert.Equal(VexConsensusPolicyOptions.BaselineVersion, snapshot.Version);
        Assert.Empty(snapshot.Issues);

        var evaluator = new VexPolicyEvaluator(provider);
        var consensusProvider = new VexProvider("vendor", "Vendor", VexProviderKind.Vendor);
        var claim = new VexClaim(
            "CVE-2025-0001",
            "vendor",
            new VexProduct("pkg:vendor/app", "app"),
            VexClaimStatus.NotAffected,
            new VexClaimDocument(VexDocumentFormat.Csaf, "sha256:test", new Uri("https://example.com")),
            DateTimeOffset.UtcNow,
            DateTimeOffset.UtcNow);

        Assert.Equal(1.0, evaluator.GetProviderWeight(consensusProvider));
        Assert.False(evaluator.IsClaimEligible(claim, consensusProvider, out var reason));
        Assert.Equal("missing_justification", reason);
    }

    [Fact]
    public void GetSnapshot_AppliesOverridesAndClampsInvalidValues()
    {
        var options = new VexPolicyOptions
        {
            Version = "custom/v1",
            Weights = new VexPolicyWeightOptions
            {
                Vendor = 1.2,
                Distro = 0.8,
            },
            ProviderOverrides = new Dictionary<string, double>
            {
                ["vendor"] = 0.95,
                [" "] = 0.5,
            },
        };

        var provider = new VexPolicyProvider(new OptionsMonitorStub(options), NullLogger<VexPolicyProvider>.Instance);

        var snapshot = provider.GetSnapshot();

        Assert.Equal("custom/v1", snapshot.Version);
        Assert.NotEmpty(snapshot.Issues);
        Assert.Equal(0.95, snapshot.ConsensusOptions.ProviderOverrides["vendor"]);

        var evaluator = new VexPolicyEvaluator(provider);
        var vendor = new VexProvider("vendor", "Vendor", VexProviderKind.Vendor);
        Assert.Equal(0.95, evaluator.GetProviderWeight(vendor));
    }

    private sealed class OptionsMonitorStub : IOptionsMonitor<VexPolicyOptions>
    {
        private readonly VexPolicyOptions _value;

        public OptionsMonitorStub(VexPolicyOptions value)
        {
            _value = value;
        }

        public VexPolicyOptions CurrentValue => _value;

        public VexPolicyOptions Get(string? name) => _value;

        public IDisposable OnChange(Action<VexPolicyOptions, string> listener) => DisposableAction.Instance;

        private sealed class DisposableAction : IDisposable
        {
            public static readonly DisposableAction Instance = new();

            public void Dispose()
            {
            }
        }
    }
}
