using Dalamud.Plugin;
using LightlessSync.Interop.Ipc.Framework;
using LightlessSync.PlayerData.Handlers;
using LightlessSync.Services;
using LightlessSync.Services.Mediator;
using Microsoft.Extensions.Logging;
using Penumbra.Api.Enums;
using Penumbra.Api.Helpers;
using Penumbra.Api.IpcSubscribers;

namespace LightlessSync.Interop.Ipc.Penumbra;

public sealed class PenumbraRedraw : PenumbraBase
{
    private readonly RedrawManager _redrawManager;
    private readonly RedrawObject _penumbraRedraw;
    private readonly EventSubscriber<nint, int> _penumbraObjectIsRedrawn;

    public PenumbraRedraw(
        ILogger logger,
        IDalamudPluginInterface pluginInterface,
        DalamudUtilService dalamudUtil,
        LightlessMediator mediator,
        RedrawManager redrawManager) : base(logger, pluginInterface, dalamudUtil, mediator)
    {
        _redrawManager = redrawManager;

        _penumbraRedraw = new RedrawObject(pluginInterface);
        _penumbraObjectIsRedrawn = GameObjectRedrawn.Subscriber(pluginInterface, HandlePenumbraRedrawEvent);
    }

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

    public void CancelPendingRedraws()
        => _redrawManager.Cancel();

    public void RequestImmediateRedraw(int objectIndex, RedrawType redrawType)
    {
        if (!IsAvailable)
        {
            return;
        }

        _penumbraRedraw.Invoke(objectIndex, redrawType);
    }

    public async Task RedrawAsync(ILogger logger, GameObjectHandler handler, Guid applicationId, CancellationToken token)
    {
        if (!IsAvailable || DalamudUtil.IsZoning)
        {
            return;
        }

        var redrawSemaphore = _redrawManager.RedrawSemaphore;
        var semaphoreAcquired = false;

        try
        {
            await redrawSemaphore.WaitAsync(token).ConfigureAwait(false);
            semaphoreAcquired = true;

            await _redrawManager.PenumbraRedrawInternalAsync(logger, handler, applicationId, chara =>
            {
                logger.LogDebug("[{ApplicationId}] Calling on IPC: PenumbraRedraw", applicationId);
                _penumbraRedraw.Invoke(chara.ObjectIndex, RedrawType.Redraw);
            }, token).ConfigureAwait(false);
        }
        finally
        {
            if (semaphoreAcquired)
            {
                redrawSemaphore.Release();
            }
        }
    }

    private void HandlePenumbraRedrawEvent(IntPtr objectAddress, int objectTableIndex)
        => Mediator.Publish(new PenumbraRedrawMessage(objectAddress, objectTableIndex, false));

    protected override void HandleStateChange(IpcConnectionState previous, IpcConnectionState current)
    {
    }

    public override void Dispose()
    {
        base.Dispose();
        _penumbraObjectIsRedrawn.Dispose();
    }
}
