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

namespace Kompas.Mcp.Tests.Integration;

/// <summary>Интеграция: переменные модели (параметрика, API5 ksVariable).</summary>
[Trait("Category", "Integration")]
[Collection(KompasCollection.Name)]
public sealed class VariableTests : IntegrationTestBase
{
    private readonly DocumentService _docs;
    private readonly PartModeler _modeler;
    private readonly VariableService _vars;
    private readonly ModelInspectionService _inspect;

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

    [Fact]
    public async Task Variable_crud_roundtrip()
    {
        await _docs.CreateAsync(KompasDocumentType.Part);
        try
        {
            // Небольшая геометрия, чтобы было дерево построения.
            var s = await _modeler.OpenSketchAsync(BasePlane.XOY);
            await _modeler.AddRectangleAsync(s, 0, 0, 20, 20);
            await _modeler.CloseSketchAsync(s);
            await _modeler.ExtrudeAsync(s, depth: 10);

            // create
            var created = await _vars.CreateVariableAsync("width", 40, "ширина");
            Assert.Equal(40, created, 3);
            Assert.Equal(40, await _vars.GetVariableValueAsync("width"), 3);

            // видна в list_variables (пользовательское чтение)
            var list = await _inspect.ListVariablesAsync();
            Assert.Contains(list, v => v.Name == "width");

            // set expression (формула → вычисленное значение)
            var computed = await _vars.SetVariableAsync("width", "12+8");
            Assert.Equal(20, computed, 3);
            Assert.Equal(20, await _vars.GetVariableValueAsync("width"), 3);

            // дубликат имени отвергается
            await Assert.ThrowsAsync<InvalidOperationException>(() => _vars.CreateVariableAsync("width", 1));

            // вторая переменная, ссылающаяся на первую (формула с именем переменной)
            await _vars.CreateVariableAsync("height", 5);
            var doubled = await _vars.SetVariableAsync("height", "width * 2");
            Assert.Equal(40, doubled, 3); // width=20 → 40

            // десятичный разделитель — точка; зависимая переменная пересчитывается
            Assert.Equal(3.5, await _vars.SetVariableAsync("width", "3.5"), 3);
            Assert.Equal(7, await _vars.GetVariableValueAsync("height"), 3); // 3.5*2

            // нельзя удалить переменную, на которую ссылаются (height = width*2)
            await Assert.ThrowsAsync<InvalidOperationException>(() => _vars.DeleteVariableAsync("width"));

            // delete (в порядке зависимостей: сначала зависимая height, затем width)
            await _vars.DeleteVariableAsync("height");
            await _vars.DeleteVariableAsync("width");
            await Assert.ThrowsAsync<KeyNotFoundException>(() => _vars.GetVariableValueAsync("width"));
            await Assert.ThrowsAsync<KeyNotFoundException>(() => _vars.GetVariableValueAsync("height"));
        }
        finally { await _docs.CloseAsync(save: false); }
    }

    [Fact]
    public async Task Variable_set_note_and_list_shows_note()
    {
        await _docs.CreateAsync(KompasDocumentType.Part);
        try
        {
            var s = await _modeler.OpenSketchAsync(BasePlane.XOY);
            await _modeler.AddRectangleAsync(s, 0, 0, 20, 20);
            await _modeler.CloseSketchAsync(s);
            await _modeler.ExtrudeAsync(s, depth: 10);

            // Create variable without note
            await _vars.CreateVariableAsync("t", 5);
            var list0 = await _inspect.ListVariablesAsync();
            var v0 = list0.Single(v => v.Name == "t");
            Assert.Equal("", v0.Note);

            // Set note
            await _vars.SetVariableNoteAsync("t", "wall thickness, mm");
            var list1 = await _inspect.ListVariablesAsync();
            var v1 = list1.Single(v => v.Name == "t");
            Assert.Equal("wall thickness, mm", v1.Note);

            // Overwrite note
            await _vars.SetVariableNoteAsync("t", "thickness of base plate, mm");
            var list2 = await _inspect.ListVariablesAsync();
            Assert.Equal("thickness of base plate, mm", list2.Single(v => v.Name == "t").Note);
        }
        finally { await _docs.CloseAsync(save: false); }
    }
}
