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

namespace Kompas.Mcp.Host.Tools;

/// <summary>
/// Структурное «зрение» агента — чтение модели как данных, а не картинки.
/// Предпочтительнее снимка (<c>model_snapshot</c>): точнее и дешевле по контексту.
/// </summary>
[McpServerToolType]
public sealed class InspectionTools(KompasSession session, ModelInspectionService inspection)
{
    [McpServerTool(Name = "describe_model")]
    [Description("ПРЕДПОЧТИТЕЛЬНЫЙ способ «увидеть» активную деталь — структурный паспорт одним вызовом: " +
                 "габарит (размеры и координаты), МЦХ (объём, масса, площадь, центр масс), тела, сводка " +
                 "топологии (грани/рёбра по типам), дерево построения с параметрами и переменные. " +
                 "sections сужает ответ до нужных разделов: box | mass | bodies | topology | tree | variables " +
                 "(через запятую; пусто = всё). Незапрошенные разделы не читаются вовсе — на тяжёлой модели " +
                 "«box,mass» заметно быстрее полного паспорта. Вызывайте ПЕРЕД model_snapshot: точнее снимка " +
                 "и дешевле по контексту; снимок — только для визуально-пространственных вопросов.")]
    public async Task<string> DescribeModel(
        [Description("Разделы через запятую: box | mass | bodies | topology | tree | variables; пусто — все")] string? sections = null)
    {
        await session.ConnectAsync();
        var wanted = InspectionText.ParseSections(sections);
        var d = await inspection.DescribeModelAsync(wanted);
        return InspectionText.Render(d, wanted);
    }

    [McpServerTool(Name = "list_features")]
    [Description("Дерево построения активной детали по порядку: индекс, имя операции и ключевые параметры " +
                 "(глубина/радиус/катеты). Для импортированных моделей (STEP) истории нет — только начало координат и тело.")]
    public async Task<string> ListFeatures()
    {
        await session.ConnectAsync();
        var fs = await inspection.ListFeaturesAsync();
        if (fs.Count == 0) return "Дерево построения пусто.";
        var lines = fs.Select(f => $"  [{f.Index}] «{f.Name}»" + (string.IsNullOrEmpty(f.Detail) ? "" : $" — {f.Detail}"));
        return $"Операций: {fs.Count}\n" + string.Join("\n", lines);
    }

    [McpServerTool(Name = "list_bodies")]
    [Description("Тела активной детали: индекс, признак твёрдого тела и число граней. Полезно для многотельных деталей.")]
    public async Task<string> ListBodies()
    {
        await session.ConnectAsync();
        var bs = await inspection.ListBodiesAsync();
        if (bs.Count == 0) return "Тел нет (модель пуста?).";
        var lines = bs.Select(b => $"  [{b.Index}] {(b.IsSolid ? "твёрдое" : "поверхность")}, граней {b.FaceCount}");
        return $"Тел: {bs.Count}\n" + string.Join("\n", lines);
    }

    [McpServerTool(Name = "list_variables")]
    [Description("Переменные активной детали: имя, выражение, значение и признаки (внешняя/информационная). " +
                 "Это «ручки управления» параметрической модели.")]
    public async Task<string> ListVariables()
    {
        await session.ConnectAsync();
        var vs = await inspection.ListVariablesAsync();
        if (vs.Count == 0) return "Переменных нет.";
        var lines = vs.Select(v =>
        {
            var flags = (v.External ? " [внешняя]" : "") + (v.Information ? " [инфо]" : "");
            var notePart = string.IsNullOrWhiteSpace(v.Note) ? "" : $" — {v.Note}";
            return Inv($"  {v.Name} = {v.Expression} (= {v.Value:G}){flags}{notePart}");
        });
        return $"Переменных: {vs.Count}\n" + string.Join("\n", lines);
    }

    [McpServerTool(Name = "measure")]
    [Description("Измерить расстояние и угол между двумя объектами активной детали. kind1/kind2: face|edge|vertex; " +
                 "индексы — из list_faces/list_edges. Возвращает минимальное расстояние (мм) и угол (°, если применим).")]
    public async Task<string> Measure(
        [Description("Тип первого объекта: face|edge|vertex")] string kind1,
        [Description("Индекс первого объекта")] int index1,
        [Description("Тип второго объекта: face|edge|vertex")] string kind2,
        [Description("Индекс второго объекта")] int index2)
    {
        await session.ConnectAsync();
        var m = await inspection.MeasureAsync(kind1, index1, kind2, index2);
        var angle = m.AngleValid ? Inv($", угол {m.Angle:F2}°") : " (угол неприменим)";
        return Inv($"{kind1}[{index1}] ↔ {kind2}[{index2}]: расстояние {m.MinDistance:F2} мм{angle}");
    }

    private static string Inv(FormattableString fs) => FormattableString.Invariant(fs);
}
