using System.Collections.Immutable;
using MongoDB.Bson;
using StellaOps.Feedser.Source.Common;
using StellaOps.Feedser.Models;
using StellaOps.Feedser.Source.Ru.Bdu.Internal;
using StellaOps.Feedser.Storage.Mongo.Documents;
using Xunit;

namespace StellaOps.Feedser.Source.Ru.Bdu.Tests;

public sealed class RuBduMapperTests
{
    [Fact]
    public void Map_ConstructsCanonicalAdvisory()
    {
        var dto = new RuBduVulnerabilityDto(
            Identifier: "BDU:2025-12345",
            Name: "Уязвимость тестового продукта",
            Description: "Описание",
            Solution: "Обновить",
            IdentifyDate: new DateTimeOffset(2025, 10, 10, 0, 0, 0, TimeSpan.Zero),
            SeverityText: "Высокий уровень опасности",
            CvssVector: "AV:N/AC:L/Au:N/C:P/I:P/A:P",
            CvssScore: 7.5,
            Cvss3Vector: "AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
            Cvss3Score: 9.8,
            ExploitStatus: "Существует",
            IncidentCount: 2,
            FixStatus: "Уязвимость устранена",
            VulStatus: "Подтверждена производителем",
            VulClass: null,
            VulState: null,
            Other: null,
            Software: new[]
            {
                new RuBduSoftwareDto(
                    "ООО Вендор",
                    "Продукт",
                    "1.2.3;1.2.4",
                    "Windows",
                    new[] { "ПО программно-аппаратного средства АСУ ТП" }.ToImmutableArray())
            }.ToImmutableArray(),
            Environment: ImmutableArray<RuBduEnvironmentDto>.Empty,
            Cwes: new[] { new RuBduCweDto("CWE-79", "XSS"), new RuBduCweDto("CWE-89", "SQL Injection") }.ToImmutableArray(),
            Sources: new[]
            {
                "https://advisories.example/BDU-2025-12345",
                "www.example.com/ru-bdu/BDU-2025-12345"
            }.ToImmutableArray(),
            Identifiers: new[]
            {
                new RuBduExternalIdentifierDto("CVE", "CVE-2025-12345", "https://nvd.nist.gov/vuln/detail/CVE-2025-12345"),
                new RuBduExternalIdentifierDto("Positive Technologies Advisory", "PT-2025-001", "https://ptsecurity.com/PT-2025-001")
            }.ToImmutableArray());

        var document = new DocumentRecord(
            Guid.NewGuid(),
            RuBduConnectorPlugin.SourceName,
            "https://bdu.fstec.ru/vul/2025-12345",
            DateTimeOffset.UtcNow,
            "abc",
            DocumentStatuses.PendingMap,
            "application/json",
            null,
            null,
            null,
            dto.IdentifyDate,
            ObjectId.GenerateNewId());

        var advisory = RuBduMapper.Map(dto, document, dto.IdentifyDate!.Value);

        Assert.Equal("BDU:2025-12345", advisory.AdvisoryKey);
        Assert.Contains("BDU:2025-12345", advisory.Aliases);
        Assert.Contains("CVE-2025-12345", advisory.Aliases);
        Assert.Equal("critical", advisory.Severity);
        Assert.True(advisory.ExploitKnown);

        var package = Assert.Single(advisory.AffectedPackages);
        Assert.Equal(AffectedPackageTypes.IcsVendor, package.Type);
        Assert.Equal(2, package.VersionRanges.Length);
        Assert.Equal(2, package.NormalizedVersions.Length);
        Assert.All(package.NormalizedVersions, rule => Assert.Equal("ru-bdu.raw", rule.Scheme));
        Assert.Contains(package.NormalizedVersions, rule => rule.Value == "1.2.3");
        Assert.Contains(package.NormalizedVersions, rule => rule.Value == "1.2.4");
        Assert.Contains(package.Statuses, status => status.Status == AffectedPackageStatusCatalog.Affected);
        Assert.Contains(package.Statuses, status => status.Status == AffectedPackageStatusCatalog.Fixed);

        Assert.Equal(2, advisory.CvssMetrics.Length);
        Assert.Contains(advisory.References, reference => reference.Url == "https://bdu.fstec.ru/vul/2025-12345" && reference.Kind == "details");
        Assert.Contains(advisory.References, reference => reference.Url == "https://nvd.nist.gov/vuln/detail/CVE-2025-12345" && reference.Kind == "cve");
        Assert.Contains(advisory.References, reference => reference.Url == "https://advisories.example/BDU-2025-12345" && reference.Kind == "source");
        Assert.Contains(advisory.References, reference => reference.Url == "https://www.example.com/ru-bdu/BDU-2025-12345" && reference.Kind == "source");
        Assert.Contains(advisory.References, reference => reference.SourceTag == "positivetechnologiesadvisory");
    }
}
