namespace Kompas.Mcp.Core.Assemblies;

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

    /// <summary>
    /// Нормализовать путь к файлу компонента: обрезать пробелы, проверить непустоту и расширение
    /// (.m3d/.a3d, без учёта регистра), вернуть абсолютный путь. Существование файла НЕ проверяет
    /// (это делает сервис на реальной ФС).
    /// </summary>
    public static string NormalizeComponentPath(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>Проверить, что координаты конечны (отсекает NaN/Infinity).</summary>
    public static void RequireFinitePosition(double x, double y, double z)
    {
        if (!double.IsFinite(x) || !double.IsFinite(y) || !double.IsFinite(z))
            throw new ArgumentException("Координаты должны быть конечными числами (не NaN/Infinity).");
    }

    /// <summary>
    /// Проверить числовое значение сопряжения. Если значение требуется (<paramref name="requiresValue"/>,
    /// напр. расстояние) — оно должно быть конечным и строго положительным (зазор 0 вырождается в
    /// совпадение — для него есть отдельный тип).
    /// </summary>
    public static void RequireMateValue(bool requiresValue, double value)
    {
        if (!requiresValue) return; // для типов без параметра значение игнорируется
        if (!double.IsFinite(value) || value <= 0)
            throw new ArgumentOutOfRangeException(nameof(value),
                "Значение сопряжения (расстояние) должно быть конечным и > 0 " +
                "(для нулевого зазора используйте тип coincidence).");
    }
}
