using Kompas6Constants3D;

namespace Kompas.Mcp.Core.Modeling;

/// <summary>Элементарное тело («примитив»): строится без эскиза, по размерам.</summary>
public enum PrimitiveKind
{
    Block,
    Cylinder,
    Sphere,
    Cone,
}

/// <summary>Как результат примитива сочетается с уже построенными телами.</summary>
public enum PrimitiveResult
{
    /// <summary>Отдельное новое тело (объединять потом вручную).</summary>
    NewBody,

    /// <summary>Объединить с телом, которое примитив пересекает.</summary>
    Union,

    /// <summary>Вычесть из тела — карман/паз без эскиза и выреза.</summary>
    Subtract,

    /// <summary>Оставить только пересечение.</summary>
    Intersect,
}

public static class Primitives
{
    public static PrimitiveKind ParseKind(string? value) => (value ?? "").Trim().ToLowerInvariant() switch
    {
        "block" or "box" => PrimitiveKind.Block,
        "cylinder" => PrimitiveKind.Cylinder,
        "sphere" => PrimitiveKind.Sphere,
        "cone" => PrimitiveKind.Cone,
        _ => throw new ArgumentException(
            $"Неизвестный примитив '{value}'. Допустимо: block | cylinder | sphere | cone.", nameof(value)),
    };

    public static PrimitiveResult ParseResult(string? value) => (value ?? "").Trim().ToLowerInvariant() switch
    {
        "" or "new" or "newbody" or "new_body" => PrimitiveResult.NewBody,
        "union" or "add" => PrimitiveResult.Union,
        "subtract" or "cut" => PrimitiveResult.Subtract,
        "intersect" => PrimitiveResult.Intersect,
        _ => throw new ArgumentException(
            $"Неизвестный режим '{value}'. Допустимо: new | union | subtract | intersect.", nameof(value)),
    };

    public static ksOperationResultEnum ToOperationResult(PrimitiveResult result) => result switch
    {
        PrimitiveResult.Union => ksOperationResultEnum.ksOperationUnion,
        PrimitiveResult.Subtract => ksOperationResultEnum.ksOperationCut,
        PrimitiveResult.Intersect => ksOperationResultEnum.ksOperationIntersect,
        _ => ksOperationResultEnum.ksOperationNewBody,
    };

    /// <summary>Тип объекта для <c>IElementaryBodies.Add</c> — именно ksObj3dTypeEnum, не Obj3dType
    /// (значения совпадают, но API7 принимает только этот enum).</summary>
    public static ksObj3dTypeEnum ToObj3dType(PrimitiveKind kind) => kind switch
    {
        PrimitiveKind.Block => ksObj3dTypeEnum.o3d_BlockBySizes,
        PrimitiveKind.Cylinder => ksObj3dTypeEnum.o3d_Cylinder,
        PrimitiveKind.Sphere => ksObj3dTypeEnum.o3d_Sphere,
        PrimitiveKind.Cone => ksObj3dTypeEnum.o3d_ConeByHeight,
        _ => throw new ArgumentOutOfRangeException(nameof(kind), kind, "Неизвестный примитив."),
    };

    /// <summary>
    /// Проверить набор размеров под конкретный примитив. Схема инструмента не может запретить
    /// неверную комбинацию полей, поэтому ошибка обязана называть kind и недостающий параметр.
    /// </summary>
    public static void Validate(PrimitiveKind kind, double? length, double? width, double? height, double? diameter)
    {
        switch (kind)
        {
            case PrimitiveKind.Block:
                Positive(length, "length", kind);
                Positive(width, "width", kind);
                Positive(height, "height", kind);
                break;
            case PrimitiveKind.Cylinder:
            case PrimitiveKind.Cone:
                Positive(diameter, "diameter", kind);
                Positive(height, "height", kind);
                break;
            case PrimitiveKind.Sphere:
                Positive(diameter, "diameter", kind);
                break;
        }
    }

    private static void Positive(double? value, string name, PrimitiveKind kind)
    {
        if (value is null)
            throw new ArgumentException($"kind={kind.ToString().ToLowerInvariant()} требует параметр {name}.");
        if (!double.IsFinite(value.Value) || value.Value <= 0)
            throw new ArgumentException(
                $"kind={kind.ToString().ToLowerInvariant()}: {name} должен быть конечным числом > 0 (передано {value}).");
    }
}
