using Kompas.Mcp.Core.Query;

namespace Kompas.Mcp.Tests;

/// <summary>Юнит-тесты чистого рендера/группировки структурного «паспорта». COM не требуется.</summary>
[Trait("Category", "Unit")]
public sealed class InspectionTextTests
{
    // ── Детект пустой модели по габариту ──────────────────────────────────
    [Fact]
    public void IsEmptyGabarit_true_for_sentinel()
        => Assert.True(InspectionText.IsEmptyGabarit(double.MaxValue, double.MaxValue, double.MaxValue));

    [Fact]
    public void IsEmptyGabarit_true_for_all_zero()
        => Assert.True(InspectionText.IsEmptyGabarit(0, 0, 0));

    [Fact]
    public void IsEmptyGabarit_false_for_real_part()
        => Assert.False(InspectionText.IsEmptyGabarit(40, 20, 15));

    // ── Группировка типов ────────────────────────────────────────────────
    [Fact]
    public void GroupTypes_orders_by_count_desc()
    {
        var s = InspectionText.GroupTypes(new[] { "plane", "cylinder", "plane", "plane", "cylinder" });
        Assert.Equal("3 plane, 2 cylinder", s);
    }

    [Fact]
    public void GroupTypes_empty_returns_dash()
        => Assert.Equal("—", InspectionText.GroupTypes(Array.Empty<string>()));

    // ── Наименование/обозначение детали в заголовке ──────────────────────
    private static ModelDescription Desc(string partName, string partMarking) => new()
    {
        Name = "part.m3d", PartName = partName, PartMarking = partMarking,
        IsEmpty = true, DocumentTypeName = "деталь",
        Bodies = [], FaceTypes = [], EdgeTypes = [], Features = [], Variables = [], Imported = false,
    };

    [Fact]
    public void Part_without_a_name_is_flagged_with_the_way_to_set_it()
    {
        var s = InspectionText.PartIdentityText(Desc("", ""));

        Assert.Contains("без наименования", s, StringComparison.Ordinal);
        Assert.Contains("set_part_info", s, StringComparison.Ordinal);
    }

    [Fact]
    public void Part_identity_shows_both_name_and_marking()
    {
        var s = InspectionText.PartIdentityText(Desc("Значок декоративный", "ОМЕ.741124.001"));

        Assert.Contains("«Значок декоративный»", s, StringComparison.Ordinal);
        Assert.Contains("«ОМЕ.741124.001»", s, StringComparison.Ordinal);
    }

    [Fact]
    public void Part_identity_omits_the_empty_marking()
    {
        var s = InspectionText.PartIdentityText(Desc("Значок декоративный", "   "));

        Assert.Contains("наименование", s, StringComparison.Ordinal);
        Assert.DoesNotContain("обозначение", s, StringComparison.Ordinal);
    }

    // ── Детект импорта без истории ───────────────────────────────────────
    private static FeatureInfo Feat(int i, string name, string kind) => new() { Index = i, Name = name, Kind = kind };

    [Fact]
    public void IsImported_true_for_bare_origin_and_body()
    {
        var fs = new[] { Feat(0, "Начало координат", "origin"), Feat(1, "Body1", "other") };
        Assert.True(InspectionText.IsImported(fs, bodyCount: 1));
    }

    [Fact]
    public void IsImported_false_when_tree_has_edit_history()
    {
        // Импорт, отредактированный move_face: origin + тело + 5 узлов-правок (>2) — это уже история.
        var fs = new[]
        {
            Feat(0, "Начало координат", "origin"), Feat(1, "Body1", "other"),
            Feat(2, "Смещенная плоскость:1", "other"), Feat(3, "Разрезать:1", "other"),
            Feat(4, "Переместить грани:1", "other"), Feat(5, "Булева операция:1", "other"),
        };
        Assert.False(InspectionText.IsImported(fs, bodyCount: 1));
    }

    [Fact]
    public void IsImported_false_for_native_part_with_operations()
    {
        var fs = new[]
        {
            Feat(0, "Начало координат", "origin"), Feat(1, "Эскиз:1", "sketch"),
            Feat(2, "Элемент выдавливания:1", "extrusion"), Feat(3, "Скругление:1", "fillet"),
        };
        Assert.False(InspectionText.IsImported(fs, bodyCount: 1));
    }

    [Fact]
    public void IsImported_false_without_body()
        => Assert.False(InspectionText.IsImported(new[] { Feat(0, "Начало координат", "origin") }, bodyCount: 0));

    // ── Рендер: родная параметрическая деталь ────────────────────────────
    [Fact]
    public void Render_native_part_shows_tree_with_params()
    {
        var d = new ModelDescription
        {
            Name = "demo",
            IsEmpty = false,
            Box = new BoundingBox { MinX = -20, MinY = -10, MinZ = 0, MaxX = 20, MaxY = 10, MaxZ = 15 },
            Mass = new PartInfo { Volume = 11961, Mass = 0.094, Area = 3370, CenterX = 0, CenterY = 0, CenterZ = 7.5 },
            Bodies = new[] { new BodyInfo { Index = 0, IsSolid = true, FaceCount = 7 } },
            FaceTypes = new[] { "plane", "plane", "cylinder" },
            EdgeTypes = new[] { "line", "line" },
            Imported = false,
            Features = new[]
            {
                new FeatureInfo { Index = 0, Name = "Начало координат", Kind = "origin" },
                new FeatureInfo { Index = 1, Name = "Эскиз:1", Kind = "sketch" },
                new FeatureInfo { Index = 2, Name = "Элемент выдавливания:1", Kind = "extrusion", Detail = "глубина 15.00 мм" },
                new FeatureInfo { Index = 3, Name = "Скругление:1", Kind = "fillet", Detail = "R3.00 мм" },
            },
            Variables = Array.Empty<VariableInfo>(),
        };

        var text = InspectionText.Render(d);

        Assert.Contains("Документ: «demo»", text);
        Assert.Contains("Габарит (Д×Ш×В): 40.00 × 20.00 × 15.00 мм", text);
        Assert.Contains("Тела: 1", text);
        Assert.Contains("граней 3 (2 plane, 1 cylinder)", text);
        Assert.Contains("Дерево построения (4", text);
        Assert.Contains("«Элемент выдавливания:1» — глубина 15.00 мм", text);
        Assert.Contains("«Скругление:1» — R3.00 мм", text);
        Assert.Contains("Переменные: нет", text);
    }

    // ── Рендер: импортированная модель (флаг Imported) ───────────────────
    [Fact]
    public void Render_imported_model_notes_no_history()
    {
        var d = new ModelDescription
        {
            Name = "top_spacer",
            IsEmpty = false,
            Box = new BoundingBox { MinX = 0, MinY = 0, MinZ = 0, MaxX = 18, MaxY = 8, MaxZ = 39 },
            Mass = new PartInfo { Volume = 2463, Mass = 0.019, Area = 2053, CenterX = 0, CenterY = 0, CenterZ = 21 },
            Bodies = new[] { new BodyInfo { Index = 0, IsSolid = true, FaceCount = 61 } },
            FaceTypes = new[] { "plane", "cylinder" },
            EdgeTypes = new[] { "line" },
            Imported = true,
            Features = new[]
            {
                new FeatureInfo { Index = 0, Name = "Начало координат", Kind = "origin" },
                new FeatureInfo { Index = 1, Name = "Body1", Kind = "body" },
            },
            Variables = Array.Empty<VariableInfo>(),
        };

        var text = InspectionText.Render(d);

        Assert.Contains("импортирован", text.ToLowerInvariant());
        Assert.DoesNotContain("Дерево построения (", text); // не показываем как параметрическое дерево
    }

    // ── Рендер: пустая модель ────────────────────────────────────────────
    [Fact]
    public void Render_empty_model()
    {
        var d = new ModelDescription
        {
            Name = "empty", IsEmpty = true,
            Bodies = Array.Empty<BodyInfo>(), FaceTypes = Array.Empty<string>(), EdgeTypes = Array.Empty<string>(),
            Imported = false, Features = Array.Empty<FeatureInfo>(), Variables = Array.Empty<VariableInfo>(),
        };

        var text = InspectionText.Render(d);

        Assert.Contains("Модель пуста", text);
    }

    // ── Рендер переменных ────────────────────────────────────────────────
    [Fact]
    public void Render_variables_listed_with_flags()
    {
        var d = new ModelDescription
        {
            Name = "p", IsEmpty = false,
            Box = new BoundingBox { MinX = 0, MinY = 0, MinZ = 0, MaxX = 1, MaxY = 1, MaxZ = 1 },
            Mass = new PartInfo { Volume = 1, Mass = 1, Area = 6, CenterX = 0, CenterY = 0, CenterZ = 0 },
            Bodies = new[] { new BodyInfo { Index = 0, IsSolid = true, FaceCount = 6 } },
            FaceTypes = new[] { "plane" }, EdgeTypes = new[] { "line" },
            Imported = false, Features = Array.Empty<FeatureInfo>(),
            Variables = new[]
            {
                new VariableInfo { Name = "L", Expression = "80", Value = 80, External = true, Information = false },
                new VariableInfo { Name = "v", Expression = "L/2", Value = 40, External = false, Information = true },
            },
        };

        var text = InspectionText.Render(d);

        Assert.Contains("Переменные (2):", text);
        Assert.Contains("L = 80", text);
        Assert.Contains("[внешняя]", text);
        Assert.Contains("[инфо]", text);
    }
}
