using System.Reflection;
using System.Runtime.Loader;

namespace Kompas.Mcp.Core.Interop;

/// <summary>
/// Подключает интероп-сборки КОМПАС из установленного экземпляра CAD, а не из поставки сервера.
/// <para>
/// Вызывается первой строкой <c>Program.Main</c>: обработчик
/// <see cref="AssemblyLoadContext.Resolving"/> должен быть на месте до того, как JIT впервые
/// коснётся COM-типов (это происходит лениво — обычно на <c>kompas_connect</c>). Сам поиск
/// ленивый: регистрация обработчика ничего не ищет и не распаковывает.
/// </para>
/// </summary>
public static class KompasInteropLoader
{
    private static readonly object Gate = new();
    private static KompasInteropProvider? _provider;

    /// <summary>Зарегистрировать резолвер. Повторные вызовы игнорируются.</summary>
    public static void Install(KompasInteropProvider? provider = null)
    {
        lock (Gate)
        {
            if (_provider is not null)
                return;

            _provider = provider ?? KompasInteropProvider.CreateDefault();
            AssemblyLoadContext.Default.Resolving += Resolve;
        }
    }

    /// <summary>
    /// Каталог, из которого будут загружены сборки. Нужен для ранней диагностики при старте:
    /// иначе первый же вызов инструмента платил бы за распаковку архива SDK.
    /// </summary>
    /// <exception cref="KompasInteropException">Сборки не найдены.</exception>
    public static string ResolveDirectory()
    {
        var provider = _provider
            ?? throw new InvalidOperationException($"Сначала вызовите {nameof(KompasInteropLoader)}.{nameof(Install)}().");

        return provider.ResolveDirectory();
    }

    private static Assembly? Resolve(AssemblyLoadContext context, AssemblyName name)
    {
        if (!KompasInteropProvider.IsKompasAssembly(name.Name))
            return null;

        var path = Path.Combine(ResolveDirectory(), name.Name + ".dll");
        return File.Exists(path) ? context.LoadFromAssemblyPath(path) : null;
    }
}
