using System.ComponentModel;
using System.Text.Json.Serialization;

namespace Kompas.Mcp.Core.Modeling;

/// <summary>
/// Описание одного примитива эскиза во входе инструмента. Один объект вместо отдельного
/// инструмента на каждый тип: контур строится списком за один вызов.
/// </summary>
public sealed class SketchEntityInput
{
    [JsonPropertyName("type")]
    [Description("Тип примитива: line | circle | rectangle | arc | arc3points | ellipse | polyline | polygon | spline | point | axis | text")]
    public string Type { get; set; } = "";

    [JsonPropertyName("points")]
    [Description("Точки в плоскости эскиза (мм). line/axis — 2 точки; rectangle — 2 противоположных угла; " +
                 "arc3points — 3 (начало, промежуточная на дуге, конец); polyline/spline — N точек; " +
                 "point — 1; text — 1 (левый край базовой линии надписи)")]
    public SketchPoint[]? Points { get; set; }

    [JsonPropertyName("centerX")]
    [Description("X центра, мм — для circle | arc | ellipse | polygon")]
    public double? CenterX { get; set; }

    [JsonPropertyName("centerY")]
    [Description("Y центра, мм — для circle | arc | ellipse | polygon")]
    public double? CenterY { get; set; }

    [JsonPropertyName("radius")]
    [Description("Радиус, мм — для circle | arc; для polygon — радиус окружности вершин/сторон")]
    public double? Radius { get; set; }

    [JsonPropertyName("startAngle")]
    [Description("Начальный угол дуги от оси X, градусы — для arc")]
    public double? StartAngle { get; set; }

    [JsonPropertyName("endAngle")]
    [Description("Конечный угол дуги от оси X, градусы — для arc")]
    public double? EndAngle { get; set; }

    [JsonPropertyName("semiMajor")]
    [Description("Большая полуось, мм — для ellipse")]
    public double? SemiMajor { get; set; }

    [JsonPropertyName("semiMinor")]
    [Description("Малая полуось, мм — для ellipse")]
    public double? SemiMinor { get; set; }

    [JsonPropertyName("angle")]
    [Description("Угол поворота, градусы: наклон большой оси (ellipse), поворот первой вершины (polygon) " +
                 "или наклон строки (text). По умолчанию 0")]
    public double? Angle { get; set; }

    [JsonPropertyName("text")]
    [Description("Содержимое надписи — для text. Сразу переводится в кривые, поэтому надпись можно выдавливать")]
    public string? Text { get; set; }

    [JsonPropertyName("fontName")]
    [Description("Имя шрифта, установленного в системе (например «GOST type A», «Arial», «Lobster») — для text. " +
                 "По умолчанию текущий шрифт документа")]
    public string? FontName { get; set; }

    [JsonPropertyName("height")]
    [Description("Высота шрифта, мм — для text (примерно высота прописной с выносным элементом)")]
    public double? Height { get; set; }

    [JsonPropertyName("widthFactor")]
    [Description("Коэффициент сужения шрифта — для text: 1 обычный, <1 уже, >1 шире. По умолчанию 1")]
    public double? WidthFactor { get; set; }

    [JsonPropertyName("bold")]
    [Description("Полужирное начертание — для text. По умолчанию false")]
    public bool? Bold { get; set; }

    [JsonPropertyName("italic")]
    [Description("Наклонное начертание — для text. По умолчанию false")]
    public bool? Italic { get; set; }

    [JsonPropertyName("vertexCount")]
    [Description("Число вершин (>=3) — для polygon")]
    public int? VertexCount { get; set; }

    [JsonPropertyName("closed")]
    [Description("Замкнуть контур — для polyline | spline. По умолчанию false")]
    public bool? Closed { get; set; }

    [JsonPropertyName("counterClockwise")]
    [Description("Дуга против часовой стрелки — для arc. По умолчанию true")]
    public bool? CounterClockwise { get; set; }

    [JsonPropertyName("inscribed")]
    [Description("Вершины на окружности радиуса (true, вписанный) или стороны касаются её (false, описанный) — для polygon. По умолчанию true")]
    public bool? Inscribed { get; set; }
}

/// <summary>
/// Разбор входных примитивов в модель Core. Сообщения об ошибках называют недостающий параметр
/// и позицию в списке: у слитого инструмента схема не может запретить неверную комбинацию полей,
/// поэтому её ловит валидация — и агент исправляется с первой попытки.
/// </summary>
public static class SketchEntityInputs
{
    private const string KnownTypes = "line | circle | rectangle | arc | arc3points | ellipse | polyline | polygon | spline | point | axis | text";

    public static IReadOnlyList<SketchEntity> Parse(IReadOnlyList<SketchEntityInput>? entities)
    {
        if (entities is null || entities.Count == 0) return [];

        var result = new List<SketchEntity>(entities.Count);
        for (int i = 0; i < entities.Count; i++)
        {
            var e = entities[i] ?? throw new ArgumentException($"entities[{i}]: пустой объект.");
            try
            {
                result.Add(ParseOne(e));
            }
            catch (ArgumentException ex)
            {
                throw new ArgumentException($"entities[{i}]: {ex.Message}");
            }
        }
        return result;
    }

    private static SketchEntity ParseOne(SketchEntityInput e)
    {
        var kind = ParseKind(e.Type);
        var points = (e.Points ?? []).Select(p => (p.X, p.Y)).ToList();

        switch (kind)
        {
            case SketchEntityKind.Line:
            case SketchEntityKind.Axis:
            case SketchEntityKind.Rectangle:
                RequirePoints(points, 2, kind, kind == SketchEntityKind.Rectangle
                    ? "два противоположных угла"
                    : "начало и конец");
                break;
            case SketchEntityKind.Arc3Points:
                RequirePoints(points, 3, kind, "начало, промежуточную точку на дуге и конец");
                break;
            case SketchEntityKind.Point:
                RequirePoints(points, 1, kind, "одну точку");
                break;
            case SketchEntityKind.Polyline:
            case SketchEntityKind.Spline:
                RequirePoints(points, 2, kind, "список точек");
                break;
            case SketchEntityKind.Circle:
                RequireCenter(e, kind);
                Require(e.Radius, "radius", kind);
                break;
            case SketchEntityKind.Arc:
                RequireCenter(e, kind);
                Require(e.Radius, "radius", kind);
                Require(e.StartAngle, "startAngle", kind);
                Require(e.EndAngle, "endAngle", kind);
                break;
            case SketchEntityKind.Ellipse:
                RequireCenter(e, kind);
                Require(e.SemiMajor, "semiMajor", kind);
                Require(e.SemiMinor, "semiMinor", kind);
                break;
            case SketchEntityKind.Polygon:
                RequireCenter(e, kind);
                Require(e.Radius, "radius", kind);
                Require(e.VertexCount, "vertexCount", kind);
                break;
            case SketchEntityKind.Text:
                RequirePoints(points, 1, kind, "точку вставки (левый край базовой линии)");
                if (string.IsNullOrEmpty(e.Text))
                    throw new ArgumentException("type=text требует параметр text (непустую строку).");
                Require(e.Height, "height", kind);
                break;
        }

        return new SketchEntity
        {
            Kind = kind,
            Points = points,
            CenterX = e.CenterX ?? 0,
            CenterY = e.CenterY ?? 0,
            Radius = e.Radius ?? 0,
            StartAngle = e.StartAngle ?? 0,
            EndAngle = e.EndAngle ?? 0,
            SemiMajor = e.SemiMajor ?? 0,
            SemiMinor = e.SemiMinor ?? 0,
            Angle = e.Angle ?? 0,
            VertexCount = e.VertexCount ?? 0,
            Closed = e.Closed ?? false,
            CounterClockwise = e.CounterClockwise ?? true,
            Inscribed = e.Inscribed ?? true,
            Text = e.Text,
            FontName = e.FontName,
            Height = e.Height ?? 0,
            WidthFactor = e.WidthFactor ?? 1.0,
            Bold = e.Bold ?? false,
            Italic = e.Italic ?? false,
        };
    }

    private static SketchEntityKind ParseKind(string? type) => (type ?? "").Trim().ToLowerInvariant() switch
    {
        "line" => SketchEntityKind.Line,
        "circle" => SketchEntityKind.Circle,
        "rectangle" or "rect" => SketchEntityKind.Rectangle,
        "arc" => SketchEntityKind.Arc,
        "arc3points" or "arc3" => SketchEntityKind.Arc3Points,
        "ellipse" => SketchEntityKind.Ellipse,
        "polyline" => SketchEntityKind.Polyline,
        "polygon" => SketchEntityKind.Polygon,
        "spline" => SketchEntityKind.Spline,
        "point" => SketchEntityKind.Point,
        "axis" => SketchEntityKind.Axis,
        "text" => SketchEntityKind.Text,
        _ => throw new ArgumentException($"неизвестный тип примитива '{type}'. Допустимо: {KnownTypes}."),
    };

    private static void RequirePoints(IReadOnlyList<(double, double)> points, int min, SketchEntityKind kind, string what)
    {
        if (points.Count < min)
            throw new ArgumentException(
                $"type={Name(kind)} требует points — {what} (минимум {min}), передано {points.Count}.");
    }

    private static void RequireCenter(SketchEntityInput e, SketchEntityKind kind)
    {
        Require(e.CenterX, "centerX", kind);
        Require(e.CenterY, "centerY", kind);
    }

    private static void Require<T>(T? value, string name, SketchEntityKind kind) where T : struct
    {
        if (value is null)
            throw new ArgumentException($"type={Name(kind)} требует параметр {name}.");
    }

    private static string Name(SketchEntityKind kind) => kind switch
    {
        SketchEntityKind.Arc3Points => "arc3points",
        _ => kind.ToString().ToLowerInvariant(),
    };
}
