using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Plugin.Ipc;
using Microsoft.Extensions.Logging;

namespace HellionChat;

internal sealed class IpcManager : IDisposable
{
    private readonly ILogger<IpcManager> _logger;

    private ICallGateProvider<string> RegisterGate { get; }
    private ICallGateProvider<string, object?> UnregisterGate { get; }
    private ICallGateProvider<object?> AvailableGate { get; }
    private ICallGateProvider<
        string,
        PlayerPayload?,
        ulong,
        Payload?,
        SeString?,
        SeString?,
        object?
    > InvokeGate { get; }

    // v1.4.9 R4: ChatTwo IPC compatibility mirror. Third-party plugins with
    // a no-fork policy (e.g. Artisan, AllaganTools) only subscribe to the
    // ChatTwo.*-prefixed context-menu integration gates. Mirroring all four
    // provider slots under the ChatTwo namespace lets those plugins keep
    // working without code changes on their side. Conflict detection
    // prevents ChatTwo and HellionChat from loading in parallel, so no slot
    // collision risk.
    private ICallGateProvider<string> ChatTwoRegisterGate { get; }
    private ICallGateProvider<string, object?> ChatTwoUnregisterGate { get; }
    private ICallGateProvider<object?> ChatTwoAvailableGate { get; }
    private ICallGateProvider<
        string,
        PlayerPayload?,
        ulong,
        Payload?,
        SeString?,
        SeString?,
        object?
    > ChatTwoInvokeGate { get; }

    internal List<string> Registered { get; } = [];

    public IpcManager(ILogger<IpcManager> logger)
    {
        _logger = logger;
        RegisterGate = Plugin.Interface.GetIpcProvider<string>("HellionChat.Register");
        RegisterGate.RegisterFunc(Register);

        AvailableGate = Plugin.Interface.GetIpcProvider<object?>("HellionChat.Available");

        UnregisterGate = Plugin.Interface.GetIpcProvider<string, object?>("HellionChat.Unregister");
        UnregisterGate.RegisterAction(Unregister);

        InvokeGate = Plugin.Interface.GetIpcProvider<
            string,
            PlayerPayload?,
            ulong,
            Payload?,
            SeString?,
            SeString?,
            object?
        >("HellionChat.Invoke");

        // v1.4.9 R4: ChatTwo-prefixed mirrors of the four context-menu slots
        // above. Share the same Register/Unregister backing methods so a
        // plugin that subscribes via either namespace lands in the same
        // Registered list. SendMessage on Invoke fans out to both gates.
        ChatTwoRegisterGate = Plugin.Interface.GetIpcProvider<string>("ChatTwo.Register");
        ChatTwoRegisterGate.RegisterFunc(Register);

        ChatTwoAvailableGate = Plugin.Interface.GetIpcProvider<object?>("ChatTwo.Available");

        ChatTwoUnregisterGate = Plugin.Interface.GetIpcProvider<string, object?>(
            "ChatTwo.Unregister"
        );
        ChatTwoUnregisterGate.RegisterAction(Unregister);

        ChatTwoInvokeGate = Plugin.Interface.GetIpcProvider<
            string,
            PlayerPayload?,
            ulong,
            Payload?,
            SeString?,
            SeString?,
            object?
        >("ChatTwo.Invoke");

        AvailableGate.SendMessage();
        ChatTwoAvailableGate.SendMessage();
    }

    internal void Invoke(
        string id,
        PlayerPayload? sender,
        ulong contentId,
        Payload? payload,
        SeString? senderString,
        SeString? content
    )
    {
        InvokeGate.SendMessage(id, sender, contentId, payload, senderString, content);
        // v1.4.9 R4: fan out the same event to plugins listening on ChatTwo.Invoke.
        ChatTwoInvokeGate.SendMessage(id, sender, contentId, payload, senderString, content);
    }

    private string Register()
    {
        var id = Guid.NewGuid().ToString();
        Registered.Add(id);
        return id;
    }

    private void Unregister(string id)
    {
        Registered.Remove(id);
    }

    public void Dispose()
    {
        UnregisterGate.UnregisterAction();
        RegisterGate.UnregisterFunc();
        ChatTwoUnregisterGate.UnregisterAction();
        ChatTwoRegisterGate.UnregisterFunc();
        Registered.Clear();
    }
}
