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 FeatureTools(KompasSession session, PartModeler modeler, HoleService holes)
{
    [McpServerTool(Name = "extrude_boss")]
    [Description("Выдавить эскиз, добавив материал (на глубину depth, мм). Возвращает id операции.")]
    public async Task<string> ExtrudeBoss(int sketchId, double depth, [Description("Прямое направление")] bool forward = true)
    {
        await session.ConnectAsync();
        var id = await modeler.ExtrudeAsync(sketchId, depth, cut: false, forward: forward);
        return $"Выдавливание (boss) создано, id={id}.";
    }

    [McpServerTool(Name = "extrude_cut")]
    [Description("Вырезать выдавливанием по эскизу. Для сквозного отверстия передайте throughAll=true (depth не нужен). Иначе вырез на глубину depth (мм).")]
    public async Task<string> ExtrudeCut(
        int sketchId,
        [Description("Глубина, мм (нужна, если throughAll=false)")] double depth = 0,
        [Description("Вырезать насквозь через всю деталь")] bool throughAll = false,
        [Description("Прямое направление")] bool forward = true)
    {
        await session.ConnectAsync();
        var id = await modeler.ExtrudeAsync(sketchId, depth, cut: true, forward: forward, throughAll: throughAll);
        return $"Вырез (cut) создан, id={id}.";
    }

    [McpServerTool(Name = "revolve_boss")]
    [Description("Вращать эскиз вокруг его осевой линии, добавив материал. Эскиз должен содержать профиль и осевую линию (sketch_add_axis). angle — угол в градусах (по умолчанию 360). Возвращает id операции.")]
    public async Task<string> RevolveBoss(
        int sketchId,
        [Description("Угол вращения, градусы (0..360]")] double angle = 360,
        [Description("Прямое направление")] bool forward = true)
    {
        await session.ConnectAsync();
        var id = await modeler.RevolveAsync(sketchId, angle, cut: false, forward: forward);
        return $"Вращение (boss) создано, id={id}.";
    }

    [McpServerTool(Name = "revolve_cut")]
    [Description("Вырезать вращением эскиза вокруг его осевой линии. Эскиз должен содержать профиль и осевую линию (sketch_add_axis). angle — угол в градусах (по умолчанию 360). Возвращает id операции.")]
    public async Task<string> RevolveCut(
        int sketchId,
        [Description("Угол вращения, градусы (0..360]")] double angle = 360,
        [Description("Прямое направление")] bool forward = true)
    {
        await session.ConnectAsync();
        var id = await modeler.RevolveAsync(sketchId, angle, cut: true, forward: forward);
        return $"Вырез вращением (cut) создан, id={id}.";
    }

    [McpServerTool(Name = "fillet_edge")]
    [Description("Скруглить ребро детали, выбранное по точке (мировые координаты x,y,z в мм, точка должна лежать на ребре), радиусом radius (мм). Сделайте model_snapshot, прикиньте точку на ребре. Возвращает id операции.")]
    public async Task<string> FilletEdge(double x, double y, double z, double radius)
    {
        await session.ConnectAsync();
        var id = await modeler.FilletEdgeAsync(x, y, z, radius);
        return $"Скругление R{radius} создано на ребре в точке ({x}, {y}, {z}), id={id}.";
    }

    [McpServerTool(Name = "chamfer_edge")]
    [Description("Снять фаску с ребра детали, выбранного по точке (мировые координаты x,y,z в мм, точка должна лежать на ребре), катетом distance (мм). Сделайте model_snapshot, прикиньте точку на ребре. Возвращает id операции.")]
    public async Task<string> ChamferEdge(double x, double y, double z, double distance)
    {
        await session.ConnectAsync();
        var id = await modeler.ChamferEdgeAsync(x, y, z, distance);
        return $"Фаска {distance}×{distance} создана на ребре в точке ({x}, {y}, {z}), id={id}.";
    }

    [McpServerTool(Name = "fillet_edge_index")]
    [Description("Скруглить ребро детали по индексу из list_edges радиусом radius (мм). Надёжнее выбора по точке. Возвращает id операции.")]
    public async Task<string> FilletEdgeIndex([Description("Индекс ребра из list_edges")] int edgeIndex, double radius)
    {
        await session.ConnectAsync();
        var id = await modeler.FilletEdgeIndexAsync(edgeIndex, radius);
        return $"Скругление R{radius} создано на ребре [{edgeIndex}], id={id}.";
    }

    [McpServerTool(Name = "chamfer_edge_index")]
    [Description("Снять фаску с ребра детали по индексу из list_edges катетом distance (мм). Надёжнее выбора по точке. Возвращает id операции.")]
    public async Task<string> ChamferEdgeIndex([Description("Индекс ребра из list_edges")] int edgeIndex, double distance)
    {
        await session.ConnectAsync();
        var id = await modeler.ChamferEdgeIndexAsync(edgeIndex, distance);
        return $"Фаска {distance}×{distance} создана на ребре [{edgeIndex}], id={id}.";
    }

    [McpServerTool(Name = "shell")]
    [Description("Превратить тело активной детали в оболочку (придать стенкам толщину): удалить (открыть) грани с индексами faceIndices из list_faces и задать толщину стенки thickness (мм). outward=false — толщина внутрь (габарит сохраняется), true — наружу. Возвращает id операции.")]
    public async Task<string> Shell(
        [Description("Индексы удаляемых (открываемых) граней из list_faces")] int[] faceIndices,
        [Description("Толщина стенки, мм")] double thickness,
        [Description("Толщина наружу (true) или внутрь (false)")] bool outward = false)
    {
        await session.ConnectAsync();
        var id = await modeler.ShellAsync(faceIndices, thickness, outward);
        var removed = faceIndices?.Distinct().Count() ?? 0;
        return $"Оболочка толщиной {thickness} мм создана (удалено граней: {removed}), id={id}.";
    }

    [McpServerTool(Name = "rib")]
    [Description("Построить ребро жёсткости от разомкнутого контура эскиза до тела. sketchId — эскиз с разомкнутым контуром (отрезок/ломаная) на плоскости, пересекающей тело. thickness — толщина стенки ребра (мм). side — направление достройки полотна до тела (ksRibSideEnum): left|right|up|down (если ребро не строится — перебрать значения). symmetric=true — толщина симметрична плоскости эскиза, false — в одну сторону. angle — уклон стенок (градусы). Возвращает id операции.")]
    public async Task<string> Rib(int sketchId, double thickness, string side = "up", bool symmetric = true, double angle = 0)
    {
        await session.ConnectAsync();
        var id = await modeler.RibAsync(sketchId, thickness, side, symmetric, angle);
        return $"Ребро жёсткости толщиной {thickness} мм создано (side={side}), id={id}.";
    }

    [McpServerTool(Name = "sweep")]
    [Description("Кинематическая операция: переместить замкнутый профиль (эскиз profileSketchId) вдоль траектории (эскиз pathSketchId), образуя сплошное тело. Профиль и траектория должны быть совместимы: на разных плоскостях (обычно перпендикулярных), начало траектории — на плоскости профиля; иначе операция вернёт ошибку построения. Возвращает id операции.")]
    public async Task<string> Sweep(int profileSketchId, int pathSketchId)
    {
        await session.ConnectAsync();
        var id = await modeler.SweepAsync(profileSketchId, pathSketchId);
        return $"Кинематическая операция создана (профиль {profileSketchId} по траектории {pathSketchId}), id={id}.";
    }

    [McpServerTool(Name = "loft")]
    [Description("Операция по сечениям (loft): построить сплошное тело по нескольким закрытым эскизам-сечениям (sketchIds, минимум 2, в порядке списка). Сечения обычно на параллельных плоскостях — используйте sketch_create_on_offset_plane для сечений на разной высоте. closed=true — замкнуть сечения в кольцо. Возвращает id операции.")]
    public async Task<string> Loft(
        [Description("Id эскизов-сечений (минимум 2, в порядке построения)")] int[] sketchIds,
        [Description("Замкнуть сечения в кольцо")] bool closed = false)
    {
        await session.ConnectAsync();
        var id = await modeler.LoftAsync(sketchIds, closed);
        var sections = sketchIds?.Distinct().Count() ?? 0;
        return $"Операция по сечениям создана ({sections} сечений), id={id}.";
    }

    [McpServerTool(Name = "linear_pattern")]
    [Description("Линейный массив: размножить ранее созданные операции featureIds (их id из extrude/revolve/fillet/...) вдоль координатной оси axis (X|Y|Z). count — количество экземпляров ВСЕГО, включая исходный (>=2). step — шаг между соседними экземплярами (мм). geometric=true — быстрое геометрическое копирование (может не строиться на скруглениях/фасках кривых поверхностей). Возвращает id операции.")]
    public async Task<string> LinearPattern(
        [Description("Id операций-источников (из ранее созданных операций)")] int[] featureIds,
        [Description("Координатная ось направления: X|Y|Z")] string axis,
        [Description("Число экземпляров всего, включая исходный (>=2)")] int count,
        [Description("Шаг между соседними экземплярами, мм")] double step,
        [Description("Геометрическое копирование (быстрее)")] bool geometric = false)
    {
        await session.ConnectAsync();
        var id = await modeler.LinearPatternAsync(featureIds, CoordinateAxes.Parse(axis), count, step, geometric);
        return $"Линейный массив создан ({count} экз. вдоль {axis}, шаг {step} мм), id={id}.";
    }

    [McpServerTool(Name = "circular_pattern")]
    [Description("Круговой массив: размножить ранее созданные операции featureIds вокруг координатной оси axis (X|Y|Z). count — количество экземпляров ВСЕГО, включая исходный (>=2). step — угловой шаг между соседними экземплярами, градусы (например 90 для 4 шт через 90°, или 120 для 3 шт). reverse=true — обратное направление вращения. geometric=true — быстрое геометрическое копирование. Возвращает id операции.")]
    public async Task<string> CircularPattern(
        [Description("Id операций-источников")] int[] featureIds,
        [Description("Координатная ось вращения: X|Y|Z")] string axis,
        [Description("Число экземпляров всего, включая исходный (>=2)")] int count,
        [Description("Угловой шаг между соседними, градусы")] double step,
        [Description("Обратное направление вращения")] bool reverse = false,
        [Description("Геометрическое копирование (быстрее)")] bool geometric = false)
    {
        await session.ConnectAsync();
        var id = await modeler.CircularPatternAsync(featureIds, CoordinateAxes.Parse(axis), count, step, reverse, geometric);
        return $"Круговой массив создан ({count} экз. вокруг {axis}, шаг {step}°), id={id}.";
    }

    [McpServerTool(Name = "mirror_operation")]
    [Description("Зеркальная копия операций: отразить ранее созданные операции featureIds относительно координатной плоскости plane (XOY|XOZ|YOZ). Добавляет зеркальные копии (оригинал сохраняется). Возвращает id операции.")]
    public async Task<string> MirrorOperation(
        [Description("Id операций-источников")] int[] featureIds,
        [Description("Плоскость симметрии: XOY|XOZ|YOZ")] string plane)
    {
        await session.ConnectAsync();
        var id = await modeler.MirrorOperationAsync(featureIds, BasePlanes.Parse(plane));
        return $"Зеркальная копия операций создана (плоскость {plane}), id={id}.";
    }

    [McpServerTool(Name = "mirror_body")]
    [Description("Зеркально отразить всё тело детали относительно координатной плоскости plane (XOY|XOZ|YOZ) — получить симметричную деталь (оригинал + зеркало). Удобно для моделирования половины с последующим отражением. Возвращает id операции.")]
    public async Task<string> MirrorBody(
        [Description("Плоскость симметрии: XOY|XOZ|YOZ")] string plane)
    {
        await session.ConnectAsync();
        var id = await modeler.MirrorBodyAsync(BasePlanes.Parse(plane));
        return $"Тело зеркально отражено (плоскость {plane}), id={id}.";
    }

    [McpServerTool(Name = "hole")]
    [Description("Просверлить простое цилиндрическое отверстие (API7) на грани, выбранной по мировой точке (x,y,z в мм — точка должна лежать на грани и задаёт ЦЕНТР отверстия; сделайте model_snapshot/describe_face и прикиньте точку). diameter — диаметр (мм). throughAll=true — сквозное (depth не нужен); иначе глухое на глубину depth (мм). Направление в тело подбирается автоматически. Отличие от extrude_cut: параметрическое отверстие без эскиза.")]
    public async Task<string> Hole(
        double x, double y, double z,
        [Description("Диаметр отверстия, мм")] double diameter,
        [Description("Глубина, мм (нужна, если throughAll=false)")] double depth = 0,
        [Description("Сквозное отверстие через всё тело")] bool throughAll = false)
    {
        await session.ConnectAsync();
        await holes.HoleAsync(x, y, z, diameter, depth, throughAll);
        var kind = throughAll ? "сквозное" : $"глухое на {depth} мм";
        return $"Отверстие Ø{diameter} ({kind}) создано на грани в точке ({x}, {y}, {z}).";
    }

    [McpServerTool(Name = "draft")]
    [Description("Уклон граней: наклонить грани faceIndices (из list_faces) на угол angle (градусы, строго 0 < angle < 90) относительно нейтральной (опорной) координатной плоскости neutralPlane (XOY|XOZ|YOZ) — сечение тела в этой плоскости остаётся неизменным, грани уклоняются вокруг линии пересечения с ней. outward=true — расширение (материал добавляется), false — сужение. Грани должны граничить/пересекаться с нейтральной плоскостью. Возвращает id операции.")]
    public async Task<string> Draft(
        [Description("Индексы уклоняемых граней из list_faces")] int[] faceIndices,
        [Description("Нейтральная (опорная) плоскость: XOY|XOZ|YOZ")] string neutralPlane,
        [Description("Угол уклона, градусы (0 < angle < 90)")] double angle,
        [Description("Расширение (true) или сужение (false)")] bool outward = false)
    {
        await session.ConnectAsync();
        var id = await modeler.DraftAsync(faceIndices, BasePlanes.Parse(neutralPlane), angle, outward);
        var count = faceIndices?.Distinct().Count() ?? 0;
        return $"Уклон {angle}° создан на {count} гранях (нейтральная плоскость {neutralPlane}), id={id}.";
    }

    [McpServerTool(Name = "hole_counterbore")]
    [Description("Отверстие с ЦЕКОВКОЙ (цилиндрическое уширение сверху — под винт с цилиндрической головкой) на грани в точке (x,y,z — центр). diameter — диаметр отверстия (мм). depth/throughAll — как у hole (для глухого depth должен быть больше spotfaceDepth). spotfaceDiameter — диаметр цековки (мм, больше diameter), spotfaceDepth — глубина цековки (мм). Направление в тело — авто.")]
    public async Task<string> HoleCounterbore(
        double x, double y, double z,
        [Description("Диаметр отверстия, мм")] double diameter,
        [Description("Диаметр цековки, мм (> diameter)")] double spotfaceDiameter,
        [Description("Глубина цековки, мм")] double spotfaceDepth,
        [Description("Глубина отверстия, мм (если throughAll=false)")] double depth = 0,
        [Description("Сквозное отверстие")] bool throughAll = false)
    {
        await session.ConnectAsync();
        await holes.CounterboreHoleAsync(x, y, z, diameter, depth, throughAll, spotfaceDiameter, spotfaceDepth);
        var kind = throughAll ? "сквозное" : $"глубина {depth} мм";
        return $"Отверстие с цековкой Ø{diameter} (цековка Ø{spotfaceDiameter}×{spotfaceDepth}, {kind}) создано в точке ({x}, {y}, {z}).";
    }

    [McpServerTool(Name = "hole_countersink")]
    [Description("Отверстие с ЗЕНКОВКОЙ (коническое уширение сверху — под винт с потайной головкой) на грани в точке (x,y,z — центр). diameter — диаметр отверстия (мм). depth/throughAll — как у hole. sinkDiameter — диаметр зенковки (мм, больше diameter), sinkAngle — угол конуса зенковки (градусы, строго 0 < sinkAngle < 180; типично 90). Направление в тело — авто.")]
    public async Task<string> HoleCountersink(
        double x, double y, double z,
        [Description("Диаметр отверстия, мм")] double diameter,
        [Description("Диаметр зенковки, мм (> diameter)")] double sinkDiameter,
        [Description("Угол конуса зенковки, градусы (0..180, типично 90)")] double sinkAngle,
        [Description("Глубина отверстия, мм (если throughAll=false)")] double depth = 0,
        [Description("Сквозное отверстие")] bool throughAll = false)
    {
        await session.ConnectAsync();
        await holes.CountersinkHoleAsync(x, y, z, diameter, depth, throughAll, sinkDiameter, sinkAngle);
        var kind = throughAll ? "сквозное" : $"глубина {depth} мм";
        return $"Отверстие с зенковкой Ø{diameter} (зенковка Ø{sinkDiameter}, угол {sinkAngle}°, {kind}) создано в точке ({x}, {y}, {z}).";
    }

    [McpServerTool(Name = "hole_conic")]
    [Description("Коническое (конусное) отверстие на грани в точке (x,y,z — центр). diameter — диаметр у дна (мм). conicAngle — угол конуса (градусы, строго 0 < conicAngle < 180); отверстие расширяется к поверхности. depth/throughAll — как у hole. Направление в тело — авто.")]
    public async Task<string> HoleConic(
        double x, double y, double z,
        [Description("Диаметр у дна, мм")] double diameter,
        [Description("Угол конуса, градусы (0 < conicAngle < 180)")] double conicAngle,
        [Description("Глубина отверстия, мм (если throughAll=false)")] double depth = 0,
        [Description("Сквозное отверстие")] bool throughAll = false)
    {
        await session.ConnectAsync();
        await holes.ConicHoleAsync(x, y, z, diameter, depth, throughAll, conicAngle);
        var kind = throughAll ? "сквозное" : $"глубина {depth} мм";
        return $"Коническое отверстие Ø{diameter} (угол {conicAngle}°, {kind}) создано в точке ({x}, {y}, {z}).";
    }

    [McpServerTool(Name = "rebuild")]
    [Description("Перестроить активный документ.")]
    public async Task<string> Rebuild()
    {
        await session.ConnectAsync();
        await modeler.RebuildAsync();
        return "Документ перестроен.";
    }
}
