using Kompas.Mcp.Core.Documents;
using Kompas.Mcp.Core.Modeling;
using Kompas.Mcp.Core.Query;

namespace Kompas.Mcp.Tests.Integration;

/// <summary>Интеграция: структурное «зрение» — дерево, топология, drill-down, измерения.</summary>
[Trait("Category", "Integration")]
[Collection(KompasCollection.Name)]
public sealed class InspectionTests : IntegrationTestBase
{
    private readonly DocumentService _docs;
    private readonly PartModeler _modeler;
    private readonly ModelInspectionService _inspect;

    public InspectionTests(KompasFixture fx) : base(fx)
    {
        _docs = new DocumentService(fx.Session, fx.Dispatcher);
        _modeler = new PartModeler(fx.Session, fx.Dispatcher);
        _inspect = new ModelInspectionService(fx.Session, fx.Dispatcher);
    }

    /// <summary>Построить эталон: прямоугольник 40×20 → выдавить 15 мм → скругление R3 первого ребра.</summary>
    private async Task BuildDemoPartAsync()
    {
        var s = await _modeler.OpenSketchAsync(BasePlane.XOY);
        await _modeler.AddRectangleAsync(s, -20, -10, 20, 10);
        await _modeler.CloseSketchAsync(s);
        await _modeler.ExtrudeAsync(s, depth: 15);
        await _modeler.FilletEdgeIndexAsync(0, 3);
        await _modeler.RebuildAsync();
    }

    [Fact]
    public async Task Describe_model_shows_tree_topology_and_bodies()
    {
        await _docs.CreateAsync(KompasDocumentType.Part);
        try
        {
            await BuildDemoPartAsync();

            var d = await _inspect.DescribeModelAsync();

            Assert.False(d.IsEmpty);
            Assert.False(d.Imported);                       // родная параметрическая деталь
            Assert.Single(d.Bodies);                        // одно тело
            Assert.True(d.Bodies[0].IsSolid);

            // Габарит исходного бруска 40×20×15 (скругление R3 не меняет общий габарит).
            Assert.NotNull(d.Box);
            Assert.InRange(d.Box!.SizeX, 39.9, 40.1);
            Assert.InRange(d.Box!.SizeZ, 14.9, 15.1);

            // Топология: есть плоскости и цилиндр от скругления.
            Assert.Contains("plane", d.FaceTypes);
            Assert.Contains("cylinder", d.FaceTypes);

            // Дерево: выдавливание с глубиной и скругление.
            Assert.Contains(d.Features, f => f.Kind == "extrusion" && (f.Detail ?? "").Contains("15"));
            Assert.Contains(d.Features, f => f.Kind == "fillet");
        }
        finally { await _docs.CloseAsync(save: false); }
    }

    [Fact]
    public async Task List_features_reports_extrusion_depth()
    {
        await _docs.CreateAsync(KompasDocumentType.Part);
        try
        {
            await BuildDemoPartAsync();

            var fs = await _inspect.ListFeaturesAsync();

            var extrusion = Assert.Single(fs, f => f.Kind == "extrusion");
            Assert.Contains("15", extrusion.Detail ?? "");
            Assert.Contains(fs, f => f.Kind == "fillet");
        }
        finally { await _docs.CloseAsync(save: false); }
    }

    [Fact]
    public async Task Describe_face_returns_area_and_edges()
    {
        await _docs.CreateAsync(KompasDocumentType.Part);
        try
        {
            await BuildDemoPartAsync();

            var face = await _inspect.DescribeFaceAsync(0);

            Assert.True(face.Area > 0);
            Assert.True(face.EdgeCount > 0);
            Assert.NotNull(face.Normal);
        }
        finally { await _docs.CloseAsync(save: false); }
    }

    [Fact]
    public async Task Measure_between_two_faces_succeeds()
    {
        await _docs.CreateAsync(KompasDocumentType.Part);
        try
        {
            await BuildDemoPartAsync();

            var m = await _inspect.MeasureAsync("face", 0, "face", 1);

            Assert.True(m.MinDistance >= 0);               // путь измерения работает
            Assert.True(double.IsFinite(m.Distance));
        }
        finally { await _docs.CloseAsync(save: false); }
    }
}
