using Kompas.Mcp.Core.Modeling;

namespace Kompas.Mcp.Tests;

/// <summary>
/// Разбор пакета примитивов эскиза. Схема слитого инструмента не может запретить неверную
/// комбинацию полей — её ловит этот разбор, и сообщение должно называть недостающий параметр
/// и позицию в списке, иначе агент не исправится с первой попытки.
/// </summary>
[Trait("Category", "Unit")]
public sealed class SketchEntityInputsTests
{
    private static SketchEntityInput Circle() => new()
    {
        Type = "circle", CenterX = 0, CenterY = 0, Radius = 5,
    };

    [Fact]
    public void Empty_input_gives_empty_list()
    {
        Assert.Empty(SketchEntityInputs.Parse(null));
        Assert.Empty(SketchEntityInputs.Parse([]));
    }

    [Theory]
    [InlineData("line", SketchEntityKind.Line)]
    [InlineData("LINE", SketchEntityKind.Line)]
    [InlineData(" rect ", SketchEntityKind.Rectangle)]
    [InlineData("rectangle", SketchEntityKind.Rectangle)]
    [InlineData("arc3", SketchEntityKind.Arc3Points)]
    [InlineData("arc3points", SketchEntityKind.Arc3Points)]
    [InlineData("axis", SketchEntityKind.Axis)]
    public void Type_names_and_aliases_are_recognized(string type, SketchEntityKind expected)
    {
        var e = new SketchEntityInput
        {
            Type = type,
            Points = [new SketchPoint(0, 0), new SketchPoint(10, 10), new SketchPoint(20, 0)],
        };

        Assert.Equal(expected, SketchEntityInputs.Parse([e])[0].Kind);
    }

    [Fact]
    public void Unknown_type_lists_the_allowed_ones()
    {
        var e = new SketchEntityInput { Type = "spiral" };

        var ex = Assert.Throws<ArgumentException>(() => SketchEntityInputs.Parse([e]));
        Assert.Contains("spiral", ex.Message, StringComparison.Ordinal);
        Assert.Contains("polyline", ex.Message, StringComparison.Ordinal);
    }

    [Fact]
    public void Error_names_the_position_in_the_batch()
    {
        var bad = new SketchEntityInput { Type = "circle", CenterX = 0, CenterY = 0 }; // без radius

        var ex = Assert.Throws<ArgumentException>(() => SketchEntityInputs.Parse([Circle(), Circle(), bad]));
        Assert.Contains("entities[2]", ex.Message, StringComparison.Ordinal);
        Assert.Contains("radius", ex.Message, StringComparison.Ordinal);
    }

    [Fact]
    public void Line_requires_two_points()
    {
        var e = new SketchEntityInput { Type = "line", Points = [new SketchPoint(0, 0)] };

        var ex = Assert.Throws<ArgumentException>(() => SketchEntityInputs.Parse([e]));
        Assert.Contains("points", ex.Message, StringComparison.Ordinal);
        Assert.Contains("минимум 2", ex.Message, StringComparison.Ordinal);
    }

    [Fact]
    public void Arc_requires_center_radius_and_angles()
    {
        var e = new SketchEntityInput { Type = "arc", CenterX = 0, CenterY = 0, Radius = 10, StartAngle = 0 };

        var ex = Assert.Throws<ArgumentException>(() => SketchEntityInputs.Parse([e]));
        Assert.Contains("endAngle", ex.Message, StringComparison.Ordinal);
    }

    [Fact]
    public void Polygon_requires_vertex_count()
    {
        var e = new SketchEntityInput { Type = "polygon", CenterX = 0, CenterY = 0, Radius = 10 };

        var ex = Assert.Throws<ArgumentException>(() => SketchEntityInputs.Parse([e]));
        Assert.Contains("vertexCount", ex.Message, StringComparison.Ordinal);
    }

    [Fact]
    public void Defaults_are_applied_for_omitted_flags()
    {
        var e = new SketchEntityInput { Type = "arc", CenterX = 1, CenterY = 2, Radius = 3, StartAngle = 0, EndAngle = 90 };

        var parsed = SketchEntityInputs.Parse([e])[0];

        Assert.True(parsed.CounterClockwise); // умолчание — против часовой
        Assert.True(parsed.Inscribed);
        Assert.False(parsed.Closed);
        Assert.Equal(0, parsed.Angle);
    }

    [Fact]
    public void Text_requires_insertion_point_string_and_height()
    {
        var noPoint = new SketchEntityInput { Type = "text", Text = "OldMan", Height = 10 };
        Assert.Contains("points", Assert.Throws<ArgumentException>(
            () => SketchEntityInputs.Parse([noPoint])).Message, StringComparison.Ordinal);

        var noText = new SketchEntityInput { Type = "text", Points = [new SketchPoint(0, 0)], Height = 10 };
        Assert.Contains("text", Assert.Throws<ArgumentException>(
            () => SketchEntityInputs.Parse([noText])).Message, StringComparison.Ordinal);

        var noHeight = new SketchEntityInput { Type = "text", Points = [new SketchPoint(0, 0)], Text = "OldMan" };
        Assert.Contains("height", Assert.Throws<ArgumentException>(
            () => SketchEntityInputs.Parse([noHeight])).Message, StringComparison.Ordinal);
    }

    [Fact]
    public void Text_defaults_keep_normal_width_and_upright_face()
    {
        var e = new SketchEntityInput
        {
            Type = "text", Points = [new SketchPoint(4, 12)], Text = "EDITION", Height = 6, FontName = "Zilla Slab",
        };

        var parsed = SketchEntityInputs.Parse([e])[0];

        Assert.Equal(SketchEntityKind.Text, parsed.Kind);
        Assert.Equal("EDITION", parsed.Text);
        Assert.Equal("Zilla Slab", parsed.FontName);
        Assert.Equal(6, parsed.Height);
        Assert.Equal(1.0, parsed.WidthFactor);
        Assert.False(parsed.Bold);
        Assert.False(parsed.Italic);
        Assert.Equal((4.0, 12.0), parsed.Points[0]);
    }

    [Fact]
    public void Points_keep_their_order()
    {
        var e = new SketchEntityInput
        {
            Type = "polyline",
            Points = [new SketchPoint(0, 0), new SketchPoint(10, 0), new SketchPoint(10, 5)],
            Closed = true,
        };

        var parsed = SketchEntityInputs.Parse([e])[0];

        Assert.True(parsed.Closed);
        Assert.Equal(3, parsed.Points.Count);
        Assert.Equal((10.0, 5.0), parsed.Points[2]);
    }
}
