using Kompas.Mcp.Core.Assemblies;

namespace Kompas.Mcp.Tests;

[Trait("Category", "Unit")]
public sealed class AssemblyValidationTests
{
    [Theory]
    [InlineData(null)]
    [InlineData("")]
    [InlineData("   ")]
    public void NormalizeComponentPath_throws_on_empty(string? path)
        => Assert.Throws<ArgumentException>(() => AssemblyValidation.NormalizeComponentPath(path));

    [Theory]
    [InlineData(@"C:\parts\bolt.step")]
    [InlineData(@"C:\parts\bolt.txt")]
    [InlineData(@"C:\parts\bolt")]
    public void NormalizeComponentPath_throws_on_wrong_extension(string path)
        => Assert.Throws<ArgumentException>(() => AssemblyValidation.NormalizeComponentPath(path));

    [Theory]
    [InlineData(@"C:\parts\bolt.m3d", ".m3d")]
    [InlineData(@"C:\parts\frame.a3d", ".a3d")]
    [InlineData(@"C:\parts\bolt.M3D", ".m3d")]      // регистр не важен
    [InlineData("  C:\\parts\\bolt.a3d  ", ".a3d")] // пробелы обрезаются
    public void NormalizeComponentPath_accepts_m3d_and_a3d(string path, string expectedExt)
    {
        var result = AssemblyValidation.NormalizeComponentPath(path);
        Assert.True(Path.IsPathFullyQualified(result), "Должен вернуться абсолютный путь.");
        Assert.EndsWith(expectedExt, result, StringComparison.OrdinalIgnoreCase);
    }

    [Theory]
    [InlineData(double.NaN, 0, 0)]
    [InlineData(0, double.PositiveInfinity, 0)]
    [InlineData(0, 0, double.NegativeInfinity)]
    public void RequireFinitePosition_throws_on_non_finite(double x, double y, double z)
        => Assert.Throws<ArgumentException>(() => AssemblyValidation.RequireFinitePosition(x, y, z));

    [Fact]
    public void RequireFinitePosition_accepts_finite()
        => Assert.Null(Record.Exception(() => AssemblyValidation.RequireFinitePosition(50, -10, 0)));

    // ── Сопряжения (Mates) ───────────────────────────────────────────────

    [Theory]
    [InlineData("coincidence", MateType.Coincidence)]
    [InlineData("совпадение", MateType.Coincidence)]
    [InlineData("DISTANCE", MateType.Distance)]
    [InlineData("  расстояние  ", MateType.Distance)]
    public void Mates_Parse_maps_known(string text, MateType expected)
        => Assert.Equal(expected, Mates.Parse(text));

    [Theory]
    [InlineData("parallel")]   // пока не поддержан
    [InlineData("сопряжение")]
    [InlineData("")]
    public void Mates_Parse_throws_on_unknown(string text)
        => Assert.Throws<ArgumentException>(() => Mates.Parse(text));

    // Корректность ToConstraintType проверяется интеграционно (coincidence даёт прилегание,
    // distance — зазор), а не unit-ассертом: КОМПАС-enum встроен (EmbedInteropTypes) и не виден
    // тестовому проекту через границу сборки.

    [Theory]
    [InlineData(MateType.Distance, true)]
    [InlineData(MateType.Coincidence, false)]
    public void Mates_RequiresValue(MateType type, bool expected)
        => Assert.Equal(expected, Mates.RequiresValue(type));

    [Fact]
    public void RequireMateValue_ignores_value_when_not_required()
        => Assert.Null(Record.Exception(() => AssemblyValidation.RequireMateValue(false, double.NaN)));

    [Theory]
    [InlineData(double.NaN)]
    [InlineData(double.PositiveInfinity)]
    [InlineData(-1)]
    [InlineData(0)]   // нулевой зазор вырождается в coincidence → ошибка
    public void RequireMateValue_throws_on_bad_required_value(double value)
        => Assert.Throws<ArgumentOutOfRangeException>(() => AssemblyValidation.RequireMateValue(true, value));

    [Fact]
    public void RequireMateValue_accepts_positive_required_value()
        => Assert.Null(Record.Exception(() => AssemblyValidation.RequireMateValue(true, 30)));
}
