using MBN_STOCK_WEBVIEW.Core.Playout.Scenes;
using MBN_STOCK_WEBVIEW.Infrastructure;
using MBN_STOCK_WEBVIEW.LegacyApplication;

namespace MBN_STOCK_WEBVIEW.LegacyParityApp;

public sealed partial class MainWindow
{
    private const string OperatorDataDirectoryEnvironmentVariable =
        "MBN_STOCK_OPERATOR_DATA_DIRECTORY";

    private S5025TrustedManualFileStore? _manualNetSellStore;
    private ViTrustedManualFileStore? _manualViStore;
    private LegacyManualOperatorDataImporter? _manualDataImporter;

    private LegacyManualListsWorkflow CreateManualListsWorkflow(
        ILegacyStockLookup stockLookup)
    {
        ILegacyManualListStore store;
        try
        {
            if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(
                    OperatorDataDirectoryEnvironmentVariable)))
            {
                store = new UnavailableManualListStore(
                    "UNSAFE_CONFIGURATION",
                    "외부 경로 설정은 사용하지 않습니다. 앱 전용 로컬 저장소만 사용할 수 있습니다.");
                return new LegacyManualListsWorkflow(store, stockLookup);
            }

            var directory = TrustedManualDirectory.PreparePrivateWritableDirectory(
                Path.Combine(
                    Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                    "MBN_STOCK_WEBVIEW",
                    "OperatorData"));
            var inspection = LegacyManualOperatorDataRuntimeGuard.Inspect(directory);
            if (!inspection.IsReady)
            {
                var interrupted = inspection.State ==
                    LegacyManualOperatorDataRuntimeState.InterruptedImport;
                store = new UnavailableManualListStore(
                    interrupted ? "IMPORT_INTERRUPTED" : "IMPORTED_STATE_INVALID",
                    interrupted
                        ? "이전 원본 데이터 가져오기가 중단되어 수동 파일을 열지 않았습니다."
                        : "수동 데이터 가져오기 표식과 파일 상태가 일치하지 않습니다.");
                return new LegacyManualListsWorkflow(store, stockLookup);
            }

            _manualNetSellStore = new S5025TrustedManualFileStore(directory);
            _manualViStore = new ViTrustedManualFileStore(directory);
            _manualDataImporter = new LegacyManualOperatorDataImporter(
                directory,
                new FixedLegacyManualOperatorDataSource());
            store = new TrustedManualListStore(
                _manualNetSellStore,
                _manualViStore,
                _manualDataImporter);
        }
        catch
        {
            _manualDataImporter?.Dispose();
            _manualViStore?.Dispose();
            _manualNetSellStore?.Dispose();
            _manualDataImporter = null;
            _manualViStore = null;
            _manualNetSellStore = null;
            store = new UnavailableManualListStore(
                "INITIALIZATION_FAILED",
                "수동 순매도/VI 로컬 저장소를 초기화하지 못했습니다.");
        }

        return new LegacyManualListsWorkflow(store, stockLookup);
    }

    private void ShutdownManualListsRuntime()
    {
        var importer = Interlocked.Exchange(ref _manualDataImporter, null);
        var viStore = Interlocked.Exchange(ref _manualViStore, null);
        var netStore = Interlocked.Exchange(ref _manualNetSellStore, null);
        if (importer is null && viStore is null && netStore is null)
        {
            return;
        }

        _ = Task.Run(async () =>
        {
            await _intentGate.WaitAsync().ConfigureAwait(false);
            try
            {
                importer?.Dispose();
                viStore?.Dispose();
                netStore?.Dispose();
            }
            finally
            {
                _intentGate.Release();
            }
        });
    }

    private sealed class TrustedManualListStore : ILegacyManualListStore
    {
        private readonly S5025TrustedManualFileStore _netStore;
        private readonly ViTrustedManualFileStore _viStore;
        private readonly LegacyManualOperatorDataImporter _importer;

        public TrustedManualListStore(
            S5025TrustedManualFileStore netStore,
            ViTrustedManualFileStore viStore,
            LegacyManualOperatorDataImporter importer)
        {
            _netStore = netStore;
            _viStore = viStore;
            _importer = importer;
        }

        public LegacyManualListStoreAvailability Availability { get; } =
            new(true, "READY", null);

        public Task<IReadOnlyList<S5025TrustedManualRow>> ReadNetSellAsync(
            S5025ManualAudience audience,
            CancellationToken cancellationToken = default) =>
            _netStore.ReadAsync(audience, cancellationToken);

        public Task WriteNetSellAsync(
            S5025ManualAudience audience,
            IReadOnlyList<S5025TrustedManualRow> rows,
            CancellationToken cancellationToken = default) =>
            _netStore.WriteAsync(audience, rows, cancellationToken);

        public async Task<LegacyManualViStoreSnapshot> ReadViAsync(
            CancellationToken cancellationToken = default)
        {
            var snapshot = await _viStore.ReadSnapshotAsync(cancellationToken)
                .ConfigureAwait(false);
            return Map(snapshot);
        }

        public async Task<LegacyManualViWriteReceipt> WriteViAsync(
            IReadOnlyList<LegacyManualViStoredItem> items,
            CancellationToken cancellationToken = default)
        {
            var result = await _viStore.WriteAsync(
                items.Select(item => new ViTrustedManualItem(item.Code, item.Name)).ToArray(),
                cancellationToken).ConfigureAwait(false);
            return new LegacyManualViWriteReceipt(Map(result.Snapshot), result.Persisted);
        }

        public async Task<LegacyManualImportReceipt> ImportAsync(
            CancellationToken cancellationToken = default)
        {
            try
            {
                var result = await _importer.ImportAsync(cancellationToken)
                    .ConfigureAwait(false);
                return new LegacyManualImportReceipt(
                    result.MarkerVersion,
                    result.SourceSha256,
                    result.ImportedAt,
                    result.NetSellRowCount,
                    result.ViItemCount);
            }
            catch (LegacyManualOperatorImportException exception)
            {
                throw new LegacyManualImportStoreException(
                    ImportFailureCode(exception.Failure),
                    exception.OutcomeUnknown);
            }
        }

        private static LegacyManualViStoreSnapshot Map(ViTrustedManualSnapshot snapshot) =>
            new(
                Array.AsReadOnly(snapshot.Items.Select(item =>
                    new LegacyManualViStoredItem(item.Code, item.Name)).ToArray()),
                snapshot.RowVersion);

        private static string ImportFailureCode(LegacyManualOperatorImportFailure failure) =>
            failure switch
            {
                LegacyManualOperatorImportFailure.SourceUnavailable => "SOURCE_UNAVAILABLE",
                LegacyManualOperatorImportFailure.SourceInvalid => "SOURCE_INVALID",
                LegacyManualOperatorImportFailure.AlreadyImported => "ALREADY_IMPORTED",
                LegacyManualOperatorImportFailure.DestinationNotEmpty =>
                    "DESTINATION_NOT_EMPTY",
                LegacyManualOperatorImportFailure.InterruptedImport => "INTERRUPTED_IMPORT",
                LegacyManualOperatorImportFailure.ImportFailed => "IMPORT_FAILED",
                LegacyManualOperatorImportFailure.RollbackFailed => "ROLLBACK_FAILED",
                _ => "IMPORT_FAILED"
            };
    }

    private sealed class UnavailableManualListStore : ILegacyManualListStore
    {
        private readonly string _message;

        public UnavailableManualListStore(string code, string message)
        {
            _message = message;
            Availability = new LegacyManualListStoreAvailability(false, code, message);
        }

        public LegacyManualListStoreAvailability Availability { get; }

        public Task<IReadOnlyList<S5025TrustedManualRow>> ReadNetSellAsync(
            S5025ManualAudience audience,
            CancellationToken cancellationToken = default) =>
            Task.FromException<IReadOnlyList<S5025TrustedManualRow>>(
                new InvalidOperationException(_message));

        public Task WriteNetSellAsync(
            S5025ManualAudience audience,
            IReadOnlyList<S5025TrustedManualRow> rows,
            CancellationToken cancellationToken = default) =>
            Task.FromException(new InvalidOperationException(_message));

        public Task<LegacyManualViStoreSnapshot> ReadViAsync(
            CancellationToken cancellationToken = default) =>
            Task.FromException<LegacyManualViStoreSnapshot>(
                new InvalidOperationException(_message));

        public Task<LegacyManualViWriteReceipt> WriteViAsync(
            IReadOnlyList<LegacyManualViStoredItem> items,
            CancellationToken cancellationToken = default) =>
            Task.FromException<LegacyManualViWriteReceipt>(
                new InvalidOperationException(_message));

        public Task<LegacyManualImportReceipt> ImportAsync(
            CancellationToken cancellationToken = default) =>
            Task.FromException<LegacyManualImportReceipt>(
                new LegacyManualImportStoreException("UNAVAILABLE", false));
    }
}
