using System.ComponentModel;
using Kompas.Mcp.Core;
using Kompas.Mcp.Core.Modeling;
using ModelContextProtocol.Server;

namespace Kompas.Mcp.Host.Tools;

/// <summary>Инструменты построения эскизов в активной детали.</summary>
[McpServerToolType]
public sealed class SketchTools(KompasSession session, PartModeler modeler)
{
    [McpServerTool(Name = "sketch_create")]
    [Description("Создать эскиз на базовой плоскости (XOY, XOZ или YOZ) активной детали и открыть его на редактирование. Возвращает id эскиза для последующих sketch_add_* и операций.")]
    public async Task<string> Create([Description("Плоскость: XOY, XOZ или YOZ")] string plane)
    {
        await session.ConnectAsync();
        var id = await modeler.OpenSketchAsync(BasePlanes.Parse(plane));
        return $"Эскиз создан, id={id}. Добавьте геометрию (sketch_add_*) и завершите sketch_close.";
    }

    [McpServerTool(Name = "sketch_create_on_face")]
    [Description("Создать эскиз на плоской грани детали, выбранной по точке (мировые координаты модели x,y,z в мм), и открыть его на редактирование. Используйте для построения «на грани» поверх уже созданной геометрии: сделайте model_snapshot, прикиньте координаты точки на нужной грани, затем вызовите этот инструмент. Возвращает id эскиза.")]
    public async Task<string> CreateOnFace(double x, double y, double z)
    {
        await session.ConnectAsync();
        var id = await modeler.OpenSketchOnFaceAsync(x, y, z);
        return $"Эскиз создан на грани в точке ({x}, {y}, {z}), id={id}. Координаты геометрии — в локальной плоскости эскиза.";
    }

    [McpServerTool(Name = "sketch_create_on_offset_plane")]
    [Description("Создать эскиз на плоскости, смещённой от базовой (XOY|XOZ|YOZ) на offset мм, и открыть на редактирование. Позволяет строить геометрию на произвольной высоте/смещении (например сечения для операции loft). direction — направление смещения от базовой плоскости. Возвращает id эскиза.")]
    public async Task<string> CreateOnOffsetPlane(
        [Description("Базовая плоскость: XOY | XOZ | YOZ")] string basePlane,
        [Description("Смещение от базовой плоскости, мм")] double offset,
        [Description("Прямое направление смещения")] bool direction = true)
    {
        await session.ConnectAsync();
        var id = await modeler.OpenSketchOnOffsetPlaneAsync(BasePlanes.Parse(basePlane), offset, direction);
        return $"Эскиз создан на смещённой плоскости {basePlane}+{offset}, id={id}.";
    }

    [McpServerTool(Name = "sketch_add_circle")]
    [Description("Добавить окружность в открытый эскиз. Координаты — в плоскости эскиза, мм.")]
    public async Task<string> AddCircle(int sketchId, double centerX, double centerY, double radius)
    {
        await session.ConnectAsync();
        await modeler.AddCircleAsync(sketchId, centerX, centerY, radius);
        return "Окружность добавлена.";
    }

    [McpServerTool(Name = "sketch_add_line")]
    [Description("Добавить отрезок в открытый эскиз (две точки, мм).")]
    public async Task<string> AddLine(int sketchId, double x1, double y1, double x2, double y2)
    {
        await session.ConnectAsync();
        await modeler.AddLineAsync(sketchId, x1, y1, x2, y2);
        return "Отрезок добавлен.";
    }

    [McpServerTool(Name = "sketch_create_on_face_index")]
    [Description("Создать эскиз на грани детали, заданной индексом из list_faces, и открыть на редактирование. Надёжнее выбора по точке (не нужно угадывать координаты). Возвращает id эскиза.")]
    public async Task<string> CreateOnFaceIndex([Description("Индекс грани из list_faces")] int faceIndex)
    {
        await session.ConnectAsync();
        var id = await modeler.OpenSketchOnFaceIndexAsync(faceIndex);
        return $"Эскиз создан на грани [{faceIndex}], id={id}.";
    }

    [McpServerTool(Name = "sketch_add_axis")]
    [Description("Добавить осевую линию (ось вращения) в открытый эскиз — нужна для revolve_boss/revolve_cut. Две точки в плоскости эскиза, мм.")]
    public async Task<string> AddAxis(int sketchId, double x1, double y1, double x2, double y2)
    {
        await session.ConnectAsync();
        await modeler.AddAxisAsync(sketchId, x1, y1, x2, y2);
        return "Осевая линия добавлена.";
    }

    [McpServerTool(Name = "sketch_add_rectangle")]
    [Description("Добавить прямоугольник по двум противоположным углам в открытый эскиз (мм).")]
    public async Task<string> AddRectangle(int sketchId, double x1, double y1, double x2, double y2)
    {
        await session.ConnectAsync();
        await modeler.AddRectangleAsync(sketchId, x1, y1, x2, y2);
        return "Прямоугольник добавлен.";
    }

    [McpServerTool(Name = "sketch_close")]
    [Description("Завершить редактирование эскиза (перед операцией выдавливания).")]
    public async Task<string> Close(int sketchId)
    {
        await session.ConnectAsync();
        await modeler.CloseSketchAsync(sketchId);
        return "Эскиз закрыт.";
    }

    [McpServerTool(Name = "sketch_add_arc_3points")]
    [Description("Добавить дугу по трём точкам (начало, промежуточная точка на дуге, конец) в открытый эскиз. Координаты — в плоскости эскиза, мм.")]
    public async Task<string> AddArc3Points(int sketchId, double x1, double y1, double x2, double y2, double x3, double y3)
    {
        await session.ConnectAsync();
        await modeler.AddArc3PointsAsync(sketchId, x1, y1, x2, y2, x3, y3);
        return "Дуга по 3 точкам добавлена.";
    }

    [McpServerTool(Name = "sketch_add_arc")]
    [Description("Добавить дугу по центру, радиусу и углам (в градусах, от оси X) в открытый эскиз. counterClockwise=true — против часовой стрелки, false — по часовой. Координаты центра — в плоскости эскиза, мм.")]
    public async Task<string> AddArc(int sketchId, double centerX, double centerY, double radius, double startAngle, double endAngle, bool counterClockwise = true)
    {
        await session.ConnectAsync();
        await modeler.AddArcByAngleAsync(sketchId, centerX, centerY, radius, startAngle, endAngle, counterClockwise);
        return "Дуга добавлена.";
    }

    [McpServerTool(Name = "sketch_add_ellipse")]
    [Description("Добавить эллипс в открытый эскиз: центр, большая полуось semiMajor, малая полуось semiMinor (мм), угол наклона большой оси к X (градусы, по умолчанию 0).")]
    public async Task<string> AddEllipse(int sketchId, double centerX, double centerY, double semiMajor, double semiMinor, double angle = 0)
    {
        await session.ConnectAsync();
        await modeler.AddEllipseAsync(sketchId, centerX, centerY, semiMajor, semiMinor, angle);
        return "Эллипс добавлен.";
    }

    [McpServerTool(Name = "sketch_add_polyline")]
    [Description("Добавить ломаную — цепочку прямых отрезков по списку точек — в открытый эскиз. closed=true замыкает последнюю точку с первой (тогда нужно минимум 3 точки). Координаты — в плоскости эскиза, мм.")]
    public async Task<string> AddPolyline(int sketchId, SketchPoint[] points, bool closed = false)
    {
        await session.ConnectAsync();
        await modeler.AddPolylineAsync(sketchId, Map(points), closed);
        return $"Ломаная из {points.Length} точек добавлена{(closed ? " (замкнута)" : "")}.";
    }

    [McpServerTool(Name = "sketch_add_polygon")]
    [Description("Добавить правильный многоугольник в открытый эскиз: центр, vertexCount вершин (>=3), radius (мм). inscribed=true — вершины лежат на окружности радиуса (вписанный в окружность); false — стороны касаются окружности (описанный). angle — поворот первой вершины (градусы, по умолчанию 0).")]
    public async Task<string> AddPolygon(int sketchId, double centerX, double centerY, int vertexCount, double radius, bool inscribed = true, double angle = 0)
    {
        await session.ConnectAsync();
        await modeler.AddPolygonAsync(sketchId, centerX, centerY, vertexCount, radius, inscribed, angle);
        return $"Правильный многоугольник ({vertexCount} вершин) добавлен.";
    }

    [McpServerTool(Name = "sketch_add_spline")]
    [Description("Добавить сплайн (кубический NURBS) через список точек в открытый эскиз. closed=true замыкает кривую. Координаты — в плоскости эскиза, мм.")]
    public async Task<string> AddSpline(int sketchId, SketchPoint[] points, bool closed = false)
    {
        await session.ConnectAsync();
        await modeler.AddSplineAsync(sketchId, Map(points), closed);
        return $"Сплайн по {points.Length} точкам добавлен{(closed ? " (замкнут)" : "")}.";
    }

    [McpServerTool(Name = "sketch_add_point")]
    [Description("Добавить точку в открытый эскиз (опорная точка для построений). Координаты — в плоскости эскиза, мм.")]
    public async Task<string> AddPoint(int sketchId, double x, double y)
    {
        await session.ConnectAsync();
        await modeler.AddPointAsync(sketchId, x, y);
        return "Точка добавлена.";
    }

    private static IReadOnlyList<(double x, double y)> Map(SketchPoint[] points)
    {
        ArgumentNullException.ThrowIfNull(points);
        return points.Select(p => (p.X, p.Y)).ToList();
    }
}
