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

namespace Kompas.Mcp.Host.Tools;

/// <summary>Инструменты 2D-чертежа (виды, оформление).</summary>
[McpServerToolType]
public sealed class DrawingTools(KompasSession session, DrawingService drawing)
{
    [McpServerTool(Name = "drawing_create_standard_views")]
    [Description("На активном чертеже создать стандартные ассоциативные виды (спереди, сверху, слева) " +
        "сохранённой 3D-модели (.m3d деталь / .a3d сборка). Активный документ должен быть чертежом " +
        "(document_create drawing); модель должна быть сохранена в файл (document_save_as). " +
        "scale — масштаб (1.0 = 1:1); x,y — точка привязки главного вида на листе (мм). " +
        "Повторный вызов добавит ещё один набор видов — задайте другие x,y, чтобы не накладывались. " +
        "Возвращает число созданных видов.")]
    public async Task<string> CreateStandardViews(
        [Description("Путь к файлу 3D-модели (.m3d/.a3d)")] string partFilePath,
        [Description("Масштаб видов (1.0 = 1:1)")] double scale = 1.0,
        [Description("X привязки главного вида на листе, мм")] double x = 100,
        [Description("Y привязки главного вида на листе, мм")] double y = 150)
    {
        await session.ConnectAsync();
        var r = await drawing.CreateStandardViewsAsync(partFilePath, scale, x, y);
        var numbers = string.Join(", ", r.ViewNumbers);
        return $"Создано {r.Created} стандартных вида на чертеже (всего видов: {r.Total}, масштаб {scale}). " +
               $"Номера созданных видов: {numbers} (для инструментов простановки размеров — параметр viewNumber).";
    }

    [McpServerTool(Name = "drawing_fill_title_block")]
    [Description("Заполнить графы основной надписи (штампа) активного чертежа. Передавайте только нужные поля: " +
        "designation — обозначение документа (графа 2); name — наименование изделия (графа 1); " +
        "material — материал (графа 3). Незаданные/пустые поля пропускаются (нужно хотя бы одно). " +
        "Существующий текст графы перезаписывается. Возвращает число заполненных граф.")]
    public async Task<string> FillTitleBlock(
        [Description("Обозначение документа (графа 2)")] string? designation = null,
        [Description("Наименование изделия (графа 1)")] string? name = null,
        [Description("Материал (графа 3)")] string? material = null)
    {
        await session.ConnectAsync();
        var filled = await drawing.FillTitleBlockAsync(designation, name, material);
        return $"Заполнено граф основной надписи: {filled}.";
    }

    [McpServerTool(Name = "drawing_add_linear_dimension")]
    [Description("Поставить линейный размер на виде активного чертежа между точками (x1,y1)-(x2,y2) " +
        "в ЛОКАЛЬНОЙ СК вида (мм; при масштабе 1:1 = координаты модели на плоскости вида). Размерная " +
        "линия проходит через (x3,y3). orientation: horizontal (|X2-X1|) | vertical (|Y2-Y1|) | parallel " +
        "(длина отрезка). viewNumber — номер вида из drawing_create_standard_views (0 = первый/главный вид). " +
        "Значение измеряется автоматически. Возвращает измеренное значение и номер вида.")]
    public async Task<string> AddLinearDimension(
        [Description("X первой выносной точки (СК вида, мм)")] double x1,
        [Description("Y первой выносной точки")] double y1,
        [Description("X второй выносной точки")] double x2,
        [Description("Y второй выносной точки")] double y2,
        [Description("X положения размерной линии")] double x3,
        [Description("Y положения размерной линии")] double y3,
        [Description("Ориентация: horizontal | vertical | parallel")] string orientation = "horizontal",
        [Description("Номер вида (0 = первый/главный)")] int viewNumber = 0)
    {
        await session.ConnectAsync();
        var o = DimensionOrientations.Parse(orientation);
        var r = await drawing.AddLinearDimensionAsync(viewNumber, x1, y1, x2, y2, x3, y3, o);
        return $"Линейный размер ({orientation}) поставлен на виде {r.ViewNumber}, значение {r.Value:0.###} мм.";
    }

    [McpServerTool(Name = "drawing_add_diametral_dimension")]
    [Description("Поставить диаметральный размер (Ø) окружности на виде активного чертежа. Окружность " +
        "задаётся центром (xc,yc) и радиусом radius в ЛОКАЛЬНОЙ СК вида (мм; при масштабе 1:1 = координаты " +
        "модели на плоскости вида) — задайте параметры реальной окружности вида. angle — направление выноски " +
        "в градусах. viewNumber — номер вида из drawing_create_standard_views (0 = первый/главный). " +
        "associate=false: свободный размер (значение = 2·radius). associate=true: АССОЦИАТИВНО — (xc,yc,radius) " +
        "используются как ключ для поиска спроецированной окружности на виде; размер привязывается к ней " +
        "(BaseObject) и значение Ø читается из геометрии и обновляется с моделью. Значение измеряется " +
        "автоматически. Возвращает диаметр и номер вида.")]
    public async Task<string> AddDiametralDimension(
        [Description("X центра окружности (СК вида, мм)")] double xc,
        [Description("Y центра окружности")] double yc,
        [Description("Радиус окружности, мм")] double radius,
        [Description("Направление выноски, градусы")] double angle = 0,
        [Description("Номер вида (0 = первый/главный)")] int viewNumber = 0,
        [Description("true = привязать к окружности вида (значение из геометрии); false = свободный размер")] bool associate = false)
    {
        await session.ConnectAsync();
        var r = await drawing.AddDiametralDimensionAsync(viewNumber, xc, yc, radius, angle, associate);
        var mode = associate ? " (ассоциативно)" : "";
        return $"Диаметральный размер поставлен на виде {r.ViewNumber}, Ø{r.Value:0.###} мм{mode}.";
    }

    [McpServerTool(Name = "drawing_add_radial_dimension")]
    [Description("Поставить радиальный размер (R) окружности на виде активного чертежа. Окружность " +
        "задаётся центром (xc,yc) и радиусом radius в ЛОКАЛЬНОЙ СК вида (мм; при масштабе 1:1 = координаты " +
        "модели на плоскости вида) — задайте параметры реальной окружности вида. angle — направление выноски " +
        "в градусах. viewNumber — номер вида из drawing_create_standard_views (0 = первый/главный). " +
        "associate=false: свободный размер (значение = radius). associate=true: АССОЦИАТИВНО — (xc,yc,radius) " +
        "используются как ключ для поиска спроецированной окружности на виде; размер привязывается к ней " +
        "(BaseObject) и значение R читается из геометрии и обновляется с моделью. Значение измеряется " +
        "автоматически. Возвращает радиус и номер вида.")]
    public async Task<string> AddRadialDimension(
        [Description("X центра окружности (СК вида, мм)")] double xc,
        [Description("Y центра окружности")] double yc,
        [Description("Радиус окружности, мм")] double radius,
        [Description("Направление выноски, градусы")] double angle = 0,
        [Description("Номер вида (0 = первый/главный)")] int viewNumber = 0,
        [Description("true = привязать к окружности вида (значение из геометрии); false = свободный размер")] bool associate = false)
    {
        await session.ConnectAsync();
        var r = await drawing.AddRadialDimensionAsync(viewNumber, xc, yc, radius, angle, associate);
        var mode = associate ? " (ассоциативно)" : "";
        return $"Радиальный размер поставлен на виде {r.ViewNumber}, R{r.Value:0.###} мм{mode}.";
    }

    [McpServerTool(Name = "drawing_add_angular_dimension")]
    [Description("Поставить угловой размер между двумя сторонами на виде активного чертежа. Угол задаётся " +
        "лучами из вершины (xc,yc): сторона 1 проходит через точку (x1,y1), сторона 2 — через (x2,y2) " +
        "(всё в ЛОКАЛЬНОЙ СК вида, мм). Точка (x3,y3) — положение размерной дуги (задаёт её радиус = " +
        "расстояние от вершины); поместите её в стороне измеряемого угла. КАКОЙ из двух углов измерять " +
        "(например 90° или 270° для перпендикуляра) — выбирает angleType: min (меньший/острый) | max " +
        "(больший/тупой) | more (рефлексный, >180°). viewNumber — номер вида (0 = первый/главный). " +
        "Значение (градусы) измеряется автоматически. Возвращает угол (°) и номер вида.")]
    public async Task<string> AddAngularDimension(
        [Description("X вершины угла (СК вида, мм)")] double xc,
        [Description("Y вершины угла")] double yc,
        [Description("X точки на стороне 1")] double x1,
        [Description("Y точки на стороне 1")] double y1,
        [Description("X точки на стороне 2")] double x2,
        [Description("Y точки на стороне 2")] double y2,
        [Description("X положения размерной дуги (отстоит от вершины; угол выбирает angleType, не эта точка)")] double x3,
        [Description("Y положения размерной дуги")] double y3,
        [Description("Тип угла: min | max | more")] string angleType = "min",
        [Description("Номер вида (0 = первый/главный)")] int viewNumber = 0)
    {
        await session.ConnectAsync();
        var t = AngleDimensionTypes.Parse(angleType);
        var r = await drawing.AddAngularDimensionAsync(viewNumber, xc, yc, x1, y1, x2, y2, x3, y3, t);
        return $"Угловой размер поставлен на виде {r.ViewNumber}, {r.Value:0.###}°.";
    }

    [McpServerTool(Name = "drawing_add_rough")]
    [Description("Поставить знак шероховатости на виде активного чертежа в точке (x,y) в ЛОКАЛЬНОЙ СК вида " +
        "(мм). value — текст параметра (например \"Ra 1.6\" или \"Rz 40\"; пусто = знак без значения). " +
        "signType: delete (с удалением слоя материала) | without (без удаления) | none (без указания вида " +
        "обработки). angle — угол наклона оси знака в градусах. viewNumber — номер вида из " +
        "drawing_create_standard_views (0 = первый/главный). Возвращает значение и номер вида.")]
    public async Task<string> AddRough(
        [Description("X положения знака (СК вида, мм)")] double x,
        [Description("Y положения знака")] double y,
        [Description("Текст параметра, напр. \"Ra 1.6\" (пусто = без значения)")] string value = "",
        [Description("Тип знака: delete | without | none")] string signType = "delete",
        [Description("Угол наклона оси знака, градусы")] double angle = 0,
        [Description("Номер вида (0 = первый/главный)")] int viewNumber = 0)
    {
        await session.ConnectAsync();
        var st = RoughSignTypes.Parse(signType);
        var r = await drawing.AddRoughAsync(viewNumber, x, y, value, st, angle);
        var shown = string.IsNullOrEmpty(r.Value) ? "(без значения)" : r.Value;
        return $"Знак шероховатости поставлен на виде {r.ViewNumber}: {shown}.";
    }

    [McpServerTool(Name = "drawing_add_text")]
    [Description("Поставить свободную текстовую надпись на виде активного чертежа в точке (x,y) в ЛОКАЛЬНОЙ " +
        "СК вида (мм) стилем по умолчанию. text — содержимое надписи (\\n — несколько строк). angle — угол " +
        "наклона текста в градусах. viewNumber — номер вида из drawing_create_standard_views (0 = первый/" +
        "главный). Возвращает поставленный текст и номер вида.")]
    public async Task<string> AddText(
        [Description("X точки привязки (СК вида, мм)")] double x,
        [Description("Y точки привязки")] double y,
        [Description("Текст надписи (\\n — многострочно)")] string text = "",
        [Description("Угол наклона текста, градусы")] double angle = 0,
        [Description("Номер вида (0 = первый/главный)")] int viewNumber = 0)
    {
        await session.ConnectAsync();
        var r = await drawing.AddTextAsync(viewNumber, x, y, text, angle);
        return $"Надпись поставлена на виде {r.ViewNumber}.";
    }

    [McpServerTool(Name = "drawing_set_technical_requirements")]
    [Description("Задать технические требования активного чертежа — единый текстовый блок над основной " +
        "надписью (ГОСТ). text — содержимое (\\n разделяет пункты/строки, например \"1. ...\\n2. ...\"). " +
        "Перезаписывает прежние технические требования (не добавляет). Возвращает число строк.")]
    public async Task<string> SetTechnicalRequirements(
        [Description("Текст требований (\\n — строки)")] string text)
    {
        await session.ConnectAsync();
        var lines = await drawing.SetTechnicalRequirementsAsync(text);
        return $"Технические требования заданы ({lines} стр.).";
    }

    [McpServerTool(Name = "drawing_set_sheet_format")]
    [Description("Задать формат и ориентацию листа активного чертежа. format: A0|A1|A2|A3|A4|A5|user. " +
        "landscape: false = книжная (portrait), true = альбомная (landscape). Для стандартного формата " +
        "размеры вычисляются автоматически — width/height передавать НЕ нужно (должны быть 0). Для format=user " +
        "задайте width и height в мм (>0); при user флаг landscape игнорируется (ориентация — из соотношения " +
        "сторон). sheetNumber — номер листа (1 = первый). Возвращает итоговый формат, размеры и ориентацию.")]
    public async Task<string> SetSheetFormat(
        [Description("Формат: A0|A1|A2|A3|A4|A5|user")] string format = "A4",
        [Description("Альбомная ориентация (true) или книжная (false)")] bool landscape = false,
        [Description("Ширина листа, мм (только для format=user)")] double width = 0,
        [Description("Высота листа, мм (только для format=user)")] double height = 0,
        [Description("Номер листа (1 = первый)")] int sheetNumber = 1)
    {
        await session.ConnectAsync();
        var f = PaperFormats.Parse(format);
        var r = await drawing.SetSheetFormatAsync(f, landscape, width, height, sheetNumber);
        var orient = r.Landscape ? "альбомная" : "книжная";
        return $"Формат листа {sheetNumber}: {r.Format}, {r.Width:0.#}×{r.Height:0.#} мм, {orient}.";
    }

    [McpServerTool(Name = "drawing_add_leader")]
    [Description("Поставить линию-выноску с текстом на виде активного чертежа. Стрелка выноски указывает " +
        "в точку (x,y); текст text размещается на полке у точки (textX,textY) — всё в ЛОКАЛЬНОЙ СК вида (мм). " +
        "shelfDirection: auto/авто (по умолчанию — КОМПАС сам) | right/вправо | left/влево | up/вверх | " +
        "down/вниз — направление полки. " +
        "viewNumber — номер вида из drawing_create_standard_views (0 = первый/главный). Точка острия (x,y) " +
        "должна отличаться от точки полки (textX,textY). Возвращает поставленный текст и номер вида.")]
    public async Task<string> AddLeader(
        [Description("X острия — куда указывает стрелка (СК вида, мм)")] double x,
        [Description("Y острия")] double y,
        [Description("X полки/якоря текста")] double textX,
        [Description("Y полки/якоря текста")] double textY,
        [Description("Текст надписи на полке")] string text,
        [Description("Направление полки: auto | right | left | up | down")] string shelfDirection = "auto",
        [Description("Номер вида (0 = первый/главный)")] int viewNumber = 0)
    {
        await session.ConnectAsync();
        var dir = ShelfDirections.Parse(shelfDirection);
        var r = await drawing.AddLeaderAsync(viewNumber, x, y, textX, textY, text, dir);
        return $"Выноска поставлена на виде {r.ViewNumber}: «{r.Value}».";
    }
}
