namespace Kompas.Mcp.Core.Drawings;

/// <summary>Чистые валидаторы операций чертежа (без COM) — выделены для unit-тестов.</summary>
public static class DrawingValidation
{
    /// <summary>Допустимые расширения файла-источника модели: деталь (.m3d) и сборка (.a3d).</summary>
    private static readonly HashSet<string> AllowedExtensions =
        new(StringComparer.OrdinalIgnoreCase) { ".m3d", ".a3d" };

    /// <summary>
    /// Нормализовать путь к файлу 3D-модели: обрезать пробелы, проверить непустоту и расширение
    /// (.m3d/.a3d), вернуть абсолютный путь. Существование файла НЕ проверяет (это делает сервис).
    /// </summary>
    public static string NormalizeModelPath(string? path)
    {
        if (string.IsNullOrWhiteSpace(path))
            throw new ArgumentException("Путь к файлу модели не задан.", nameof(path));

        var trimmed = path.Trim();
        var ext = Path.GetExtension(trimmed);
        if (!AllowedExtensions.Contains(ext))
            throw new ArgumentException(
                $"Недопустимое расширение файла модели '{ext}'. Ожидается .m3d (деталь) или .a3d (сборка).",
                nameof(path));

        return Path.GetFullPath(trimmed);
    }

    /// <summary>Проверить масштаб чертежа: конечный и строго положительный.</summary>
    public static void RequireScale(double scale)
    {
        if (!double.IsFinite(scale) || scale <= 0)
            throw new ArgumentOutOfRangeException(nameof(scale), "Масштаб должен быть конечным и > 0.");
    }

    /// <summary>Проверить, что все координаты конечны (отсекает NaN/Infinity).</summary>
    public static void RequireFiniteCoords(params double[] coords)
    {
        foreach (var c in coords)
            if (!double.IsFinite(c))
                throw new ArgumentException("Координаты должны быть конечными числами (не NaN/Infinity).");
    }

    /// <summary>Проверить радиус: конечный и строго положительный.</summary>
    public static void RequirePositiveRadius(double radius)
    {
        if (!double.IsFinite(radius) || radius <= 0)
            throw new ArgumentOutOfRangeException(nameof(radius), "Радиус должен быть конечным и > 0.");
    }

    /// <summary>Проверить, что текст не пустой и не только из пробелов.</summary>
    public static void RequireNonEmptyText(string? text, string paramName)
    {
        if (string.IsNullOrWhiteSpace(text))
            throw new ArgumentException("Текст не должен быть пустым.", paramName);
    }

    /// <summary>
    /// Проверить размеры формата листа: для <see cref="PaperFormat.User"/> — ширина и высота оба
    /// конечны и &gt; 0; для стандартного формата — оба должны быть 0 (размеры авто, задавать нельзя).
    /// </summary>
    public static void ValidateFormatDimensions(PaperFormat format, double width, double height)
    {
        if (format == PaperFormat.User)
        {
            if (!double.IsFinite(width) || width <= 0)
                throw new ArgumentOutOfRangeException(nameof(width),
                    "Для format=user ширина должна быть конечной и > 0.");
            if (!double.IsFinite(height) || height <= 0)
                throw new ArgumentOutOfRangeException(nameof(height),
                    "Для format=user высота должна быть конечной и > 0.");
        }
        else if (width != 0 || height != 0)
        {
            // Точное имя нарушившего параметра (а не всегда width).
            var offender = width != 0 ? nameof(width) : nameof(height);
            throw new ArgumentException(
                "Ширина/высота задаются только для format=user; для стандартного формата размеры авто (передайте 0).",
                offender);
        }
    }

    /// <summary>Минимальное расстояние между выносными точками линейного размера (мм).</summary>
    private const double MinPointDistance = 1e-7;

    /// <summary>
    /// Проверить, что выносные точки размера различны (иначе размер вырожден — нулевая длина).
    /// </summary>
    public static void RequireDistinctPoints(double x1, double y1, double x2, double y2)
    {
        var dx = x2 - x1;
        var dy = y2 - y1;
        if (Math.Sqrt(dx * dx + dy * dy) < MinPointDistance)
            throw new ArgumentException("Выносные точки размера совпадают — нельзя поставить размер нулевой длины.");
    }
}
