using System.Runtime.Versioning;
using Kompas.Mcp.Core.Documents;
using Kompas.Mcp.Core.Interop;
using Kompas.Mcp.Core.Threading;
using Kompas6Constants;
using KompasAPI7;

namespace Kompas.Mcp.Core.Conversion;

/// <summary>
/// Импорт/экспорт обменных форматов (STEP) через КОМПАС-конвертер.
/// <para>
/// Встроенный конвертер получается через <c>IApplication.get_Converter(код_формата)</c>
/// (целочисленный код <c>ksKOMPASConverterEnum</c>, НЕ путь к DLL), параметры — через
/// <c>IConverter.ConverterParameters</c>, сама конвертация — через
/// <c>IKompasDocument3D1.ConvertTo/FromAdditionFormat</c>.
/// </para>
/// </summary>
[SupportedOSPlatform("windows")]
public sealed class ConversionService
{
    private readonly KompasSession _session;
    private readonly KompasDispatcher _dispatcher;

    public ConversionService(KompasSession session, KompasDispatcher dispatcher)
    {
        _session = session ?? throw new ArgumentNullException(nameof(session));
        _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
    }

    /// <summary>
    /// Импортировать STEP-файл в новый документ заданного типа (по умолчанию — сборка) и сделать его активным.
    /// <paramref name="createComponentFiles"/> — писать ли компоненты сборки отдельными .m3d рядом с STEP.
    /// </summary>
    public Task ImportStepAsync(
        string path,
        KompasDocumentType docType = KompasDocumentType.Assembly,
        bool createComponentFiles = false,
        CancellationToken ct = default)
        => _dispatcher.InvokeAsync(() => ImportCore(path, docType, createComponentFiles), ct);

    private void ImportCore(string path, KompasDocumentType docType, bool createComponentFiles)
    {
        if (!File.Exists(path))
            throw new FileNotFoundException($"STEP-файл не найден: {path}", path);

        var app = _session.Application;
        var typeEnum = DocumentTypes.ToEnum(docType);
        IConverter? conv = null;
        IAdditionConvertParameters? prm = null;
        object? doc = null;
        try
        {
            conv = GetConverter(app, StepFormats.ImportConverterCode); // -3
            conv.GetFilter((int)typeEnum, false /* импорт */, out int cmd);

            doc = app.Documents.Add(typeEnum, true);
            if (doc is not IKompasDocument3D1 doc3d1)
                throw new InvalidOperationException("Созданный документ не является 3D-документом (нет IKompasDocument3D1).");

            prm = conv.ConverterParameters(cmd) as IAdditionConvertParameters
                ?? throw new InvalidOperationException("ConverterParameters вернул не IAdditionConvertParameters.");
            prm.Format = (ksKOMPASConverterEnum)StepFormats.ImportConverterCode;
            if (createComponentFiles)
            {
                // NeedCreateComponentsFiles может отсутствовать в Home-редакции КОМПАС —
                // ловим COM-исключения; не критично, если свойство недоступно.
                try { prm.NeedCreateComponentsFiles = true; }
                catch (System.Runtime.InteropServices.COMException) { }
                catch (System.Runtime.InteropServices.InvalidComObjectException) { }
                catch (NotImplementedException) { }
            }

            if (!doc3d1.ConvertFromAdditionFormat(path, (AdditionConvertParameters)prm))
                throw new InvalidOperationException("ConvertFromAdditionFormat вернул FALSE (импорт STEP не удался).");
        }
        finally
        {
            ComHelper.Release(prm);
            ComHelper.Release(doc);
            ComHelper.Release(conv);
        }
    }

    /// <summary>Экспортировать активный 3D-документ в STEP по указанному пути.</summary>
    public Task ExportStepAsync(string path, StepFormat format = StepFormat.Auto, CancellationToken ct = default)
        => _dispatcher.InvokeAsync(() => ExportCore(path, format), ct);

    private void ExportCore(string path, StepFormat format)
    {
        // Каталог создаём сами: конвертер в несуществующую папку возвращает FALSE без причины.
        var full = Documents.OutputPath.Prepare(path);

        var app = _session.Application;
        object? active = null;
        IConverter? conv = null;
        IAdditionConvertParameters? prm = null;
        try
        {
            active = app.ActiveDocument
                ?? throw new InvalidOperationException("Нет активного документа для экспорта.");
            if (active is not IKompasDocument3D1 doc3d1)
                throw new InvalidOperationException("Активный документ не является 3D-документом (нет IKompasDocument3D1).");

            int formatCode = StepFormats.ExportConverterCode(format);
            conv = GetConverter(app, formatCode);
            conv.GetFilter((int)DocumentTypeEnum.ksDocumentPart, true /* saveAs = экспорт */, out int cmd);

            prm = conv.ConverterParameters(cmd) as IAdditionConvertParameters
                ?? throw new InvalidOperationException("ConverterParameters вернул не IAdditionConvertParameters.");
            prm.Format = (ksKOMPASConverterEnum)formatCode;

            if (!doc3d1.ConvertToAdditionFormat(full, (AdditionConvertParameters)prm))
                throw new InvalidOperationException($"ConvertToAdditionFormat вернул FALSE (экспорт STEP по пути «{full}» не удался).");
        }
        finally
        {
            ComHelper.Release(prm);
            ComHelper.Release(active);
            ComHelper.Release(conv);
        }
    }

    /// <summary>
    /// Получить встроенный конвертер. Для STEP get_Converter принимает целочисленный код формата
    /// (boxed int), НЕ путь к DLL. Пробуем код формата, затем базовый код экспорта STEP (3).
    /// </summary>
    private static IConverter GetConverter(IApplication app, int formatCode)
    {
        if (app.Converter[(object)formatCode] is IConverter c) return c;
        // AP-вариант мог не быть отдельным конвертером — берём базовый STEP-экспорт (ksConverterToSTEP = 3).
        if (app.Converter[(object)3] is IConverter cBase) return cBase;
        throw new InvalidOperationException(
            $"get_Converter({formatCode}) вернул null — STEP-конвертер недоступен в этой редакции КОМПАС.");
    }
}
