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

namespace Kompas.Mcp.Tests.Integration;

/// <summary>
/// Интеграция: скругление пакетом по ТОЧКАМ. На теле с сотнями рёбер (надпись!) список из
/// list_edges неподъёмен по объёму ответа, а выбор по точке раньше брал ровно одно ребро —
/// четыре угла давали четыре операции в дереве. Теперь это одна операция.
/// </summary>
[Trait("Category", "Integration")]
[Collection(KompasCollection.Name)]
public sealed class FilletByPointsTests : IntegrationTestBase
{
    private readonly DocumentService _docs;
    private readonly PartModeler _modeler;
    private readonly ModelInspectionService _inspection;
    private readonly QueryService _query;

    public FilletByPointsTests(KompasFixture fx) : base(fx)
    {
        _docs = new DocumentService(fx.Session, fx.Dispatcher);
        _modeler = new PartModeler(fx.Session, fx.Dispatcher);
        _inspection = new ModelInspectionService(fx.Session, fx.Dispatcher);
        _query = new QueryService(fx.Session, fx.Dispatcher);
    }

    /// <summary>Плита 20×10×3 от начала координат: вертикальные рёбра — в её четырёх углах.</summary>
    private async Task PlateAsync()
    {
        await _docs.CreateAsync(KompasDocumentType.Part);
        var s = await _modeler.OpenSketchAsync(BasePlane.XOY, name: "Контур плиты");
        await _modeler.AddRectangleAsync(s, 0, 0, 20, 10);
        await _modeler.CloseSketchAsync(s);
        await _modeler.ExtrudeAsync(s, depth: 3, name: "Плита");
        await _modeler.RebuildAsync();
    }

    [Fact]
    public async Task Four_corners_become_one_fillet_in_the_tree()
    {
        await PlateAsync();
        try
        {
            var before = (await _query.GetPartInfoAsync()).Volume;

            await _modeler.FilletEdgesByPointsAsync(
            [
                new Point3d(0, 0, 1.5), new Point3d(20, 0, 1.5),
                new Point3d(20, 10, 1.5), new Point3d(0, 10, 1.5),
            ], radius: 2, name: "Скругления углов плиты");
            await _modeler.RebuildAsync();

            var tree = (await _inspection.DescribeModelAsync(InspectionText.ModelSections.Tree)).Features;
            Assert.Single(tree, f => f.Name == "Скругления углов плиты");

            // Каждый угол теряет (4 − π)·r²/4 · h материала — проверяем, что скруглены все четыре.
            var expected = before - 4 * (4 - Math.PI) / 4 * 2 * 2 * 3;
            Assert.InRange((await _query.GetPartInfoAsync()).Volume, expected * 0.99, expected * 1.01);
        }
        finally { await _docs.CloseAsync(save: false); }
    }

    [Fact]
    public async Task Points_without_an_edge_are_all_listed_at_once()
    {
        await PlateAsync();
        try
        {
            var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => _modeler.FilletEdgesByPointsAsync(
            [
                new Point3d(0, 0, 1.5),        // ребро есть
                new Point3d(100, 100, 100),    // пусто
                new Point3d(-50, -50, -50),    // пусто
            ], radius: 1));

            // Обе промахнувшиеся точки названы в ОДНОМ сообщении: иначе агент выясняет их по одной.
            Assert.Contains("[1]", ex.Message, StringComparison.Ordinal);
            Assert.Contains("[2]", ex.Message, StringComparison.Ordinal);

            // Модель не тронута — операция не создавалась.
            var tree = (await _inspection.DescribeModelAsync(InspectionText.ModelSections.Tree)).Features;
            Assert.DoesNotContain(tree, f => f.Name.Contains("Скругление", StringComparison.OrdinalIgnoreCase));
        }
        finally { await _docs.CloseAsync(save: false); }
    }
}
