using System.Collections.Concurrent;
using Dalamud.Plugin;
using LightlessSync.Interop.Ipc.Framework;
using LightlessSync.Services;
using LightlessSync.Services.Mediator;
using Microsoft.Extensions.Logging;
using Penumbra.Api.Enums;
using Penumbra.Api.IpcSubscribers;

namespace LightlessSync.Interop.Ipc.Penumbra;

public sealed class PenumbraCollections : PenumbraBase
{
    private readonly CreateTemporaryCollection _createNamedTemporaryCollection;
    private readonly AssignTemporaryCollection _assignTemporaryCollection;
    private readonly DeleteTemporaryCollection _removeTemporaryCollection;
    private readonly AddTemporaryMod _addTemporaryMod;
    private readonly RemoveTemporaryMod _removeTemporaryMod;
    private readonly GetCollections _getCollections;
    private readonly ConcurrentDictionary<Guid, string> _activeTemporaryCollections = new();

    private int _cleanupScheduled;

    public PenumbraCollections(
        ILogger logger,
        IDalamudPluginInterface pluginInterface,
        DalamudUtilService dalamudUtil,
        LightlessMediator mediator) : base(logger, pluginInterface, dalamudUtil, mediator)
    {
        _createNamedTemporaryCollection = new CreateTemporaryCollection(pluginInterface);
        _assignTemporaryCollection = new AssignTemporaryCollection(pluginInterface);
        _removeTemporaryCollection = new DeleteTemporaryCollection(pluginInterface);
        _addTemporaryMod = new AddTemporaryMod(pluginInterface);
        _removeTemporaryMod = new RemoveTemporaryMod(pluginInterface);
        _getCollections = new GetCollections(pluginInterface);
    }

    public override string Name => "Penumbra.Collections";

    public async Task AssignTemporaryCollectionAsync(ILogger logger, Guid collectionId, int objectIndex)
    {
        if (!IsAvailable || collectionId == Guid.Empty)
        {
            return;
        }

        await DalamudUtil.RunOnFrameworkThread(() =>
        {
            var result = _assignTemporaryCollection.Invoke(collectionId, objectIndex, forceAssignment: true);
            logger.LogTrace("Assigning Temp Collection {CollectionId} to index {ObjectIndex}, Success: {Result}", collectionId, objectIndex, result);
            return result;
        }).ConfigureAwait(false);
    }

    public async Task<Guid> CreateTemporaryCollectionAsync(ILogger logger, string uid)
    {
        if (!IsAvailable)
        {
            return Guid.Empty;
        }

        var (collectionId, collectionName) = await DalamudUtil.RunOnFrameworkThread(() =>
        {
            var name = $"Lightless_{uid}";
            _createNamedTemporaryCollection.Invoke(name, name, out var tempCollectionId);
            logger.LogTrace("Creating Temp Collection {CollectionName}, GUID: {CollectionId}", name, tempCollectionId);
            return (tempCollectionId, name);
        }).ConfigureAwait(false);

        if (collectionId != Guid.Empty)
        {
            _activeTemporaryCollections[collectionId] = collectionName;
        }

        return collectionId;
    }

    public async Task RemoveTemporaryCollectionAsync(ILogger logger, Guid applicationId, Guid collectionId)
    {
        if (!IsAvailable || collectionId == Guid.Empty)
        {
            return;
        }

        await DalamudUtil.RunOnFrameworkThread(() =>
        {
            logger.LogTrace("[{ApplicationId}] Removing temp collection for {CollectionId}", applicationId, collectionId);
            var result = _removeTemporaryCollection.Invoke(collectionId);
            logger.LogTrace("[{ApplicationId}] RemoveTemporaryCollection: {Result}", applicationId, result);
        }).ConfigureAwait(false);

        _activeTemporaryCollections.TryRemove(collectionId, out _);
    }

    public async Task SetTemporaryModsAsync(ILogger logger, Guid applicationId, Guid collectionId, Dictionary<string, string> modPaths)
    {
        if (!IsAvailable || collectionId == Guid.Empty)
        {
            return;
        }

        await DalamudUtil.RunOnFrameworkThread(() =>
        {
            foreach (var mod in modPaths)
            {
                logger.LogTrace("[{ApplicationId}] Change: {From} => {To}", applicationId, mod.Key, mod.Value);
            }

            var removeResult = _removeTemporaryMod.Invoke("LightlessChara_Files", collectionId, 0);
            logger.LogTrace("[{ApplicationId}] Removing temp files mod for {CollectionId}, Success: {Result}", applicationId, collectionId, removeResult);

            var addResult = _addTemporaryMod.Invoke("LightlessChara_Files", collectionId, modPaths, string.Empty, 0);
            logger.LogTrace("[{ApplicationId}] Setting temp files mod for {CollectionId}, Success: {Result}", applicationId, collectionId, addResult);
        }).ConfigureAwait(false);
    }

    public async Task SetManipulationDataAsync(ILogger logger, Guid applicationId, Guid collectionId, string manipulationData)
    {
        if (!IsAvailable || collectionId == Guid.Empty)
        {
            return;
        }

        await DalamudUtil.RunOnFrameworkThread(() =>
        {
            logger.LogTrace("[{ApplicationId}] Manip: {Data}", applicationId, manipulationData);
            var result = _addTemporaryMod.Invoke("LightlessChara_Meta", collectionId, [], manipulationData, 0);
            logger.LogTrace("[{ApplicationId}] Setting temp meta mod for {CollectionId}, Success: {Result}", applicationId, collectionId, result);
        }).ConfigureAwait(false);
    }

    protected override void HandleStateChange(IpcConnectionState previous, IpcConnectionState current)
    {
        if (current == IpcConnectionState.Available)
        {
            ScheduleCleanup();
        }
        else if (previous == IpcConnectionState.Available && current != IpcConnectionState.Available)
        {
            Interlocked.Exchange(ref _cleanupScheduled, 0);
        }
    }

    private void ScheduleCleanup()
    {
        if (Interlocked.Exchange(ref _cleanupScheduled, 1) != 0)
        {
            return;
        }

        _ = Task.Run(CleanupTemporaryCollectionsAsync);
    }

    private async Task CleanupTemporaryCollectionsAsync()
    {
        if (!IsAvailable)
        {
            return;
        }

        try
        {
            var collections = await DalamudUtil.RunOnFrameworkThread(() => _getCollections.Invoke()).ConfigureAwait(false);
            foreach (var (collectionId, name) in collections)
            {
                if (!IsLightlessCollectionName(name) || _activeTemporaryCollections.ContainsKey(collectionId))
                {
                    continue;
                }

                Logger.LogDebug("Cleaning up stale temporary collection {CollectionName} ({CollectionId})", name, collectionId);
                var deleteResult = await DalamudUtil.RunOnFrameworkThread(() =>
                {
                    var result = (PenumbraApiEc)_removeTemporaryCollection.Invoke(collectionId);
                    Logger.LogTrace("Cleanup RemoveTemporaryCollection result for {CollectionName} ({CollectionId}): {Result}", name, collectionId, result);
                    return result;
                }).ConfigureAwait(false);

                if (deleteResult == PenumbraApiEc.Success)
                {
                    _activeTemporaryCollections.TryRemove(collectionId, out _);
                }
                else
                {
                    Logger.LogDebug("Skipped removing temporary collection {CollectionName} ({CollectionId}). Result: {Result}", name, collectionId, deleteResult);
                }
            }
        }
        catch (Exception ex)
        {
            Logger.LogWarning(ex, "Failed to clean up Penumbra temporary collections");
        }
    }

    private static bool IsLightlessCollectionName(string? name)
        => !string.IsNullOrEmpty(name) && name.StartsWith("Lightless_", StringComparison.Ordinal);
}
