using System;
using Dalamud.Hooking;
using Dalamud.Interface.ImGuiNotification;
using FFXIVClientStructs.FFXIV.Client.System.String;
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
using HellionChat._Helpers;
using HellionChat.Resources;
using HellionChat.Util;
using Microsoft.Extensions.Logging;

namespace HellionChat.Integrations;

// A minimal, failed-tell-specific game hook. A locale-robust "tell failed"
// signal is not reachable over the processed message stream (Message carries
// no LogMessage row id, ChatCode 60 is too broad). This hooks the one
// ShowLogMessageString overload and toasts on a pinned id set. It is NOT the
// broad ad-block hook layer.
internal sealed class FailedTellNotifier : IDisposable
{
    private readonly ILogger<FailedTellNotifier> _logger;
    private readonly Hook<RaptureLogModule.Delegates.ShowLogMessageString>? _hook;

    public unsafe FailedTellNotifier(ILogger<FailedTellNotifier> logger)
    {
        _logger = logger;

        // Creating/enabling a hook is safe off the framework thread (the
        // ctor runs during host startup on the framework thread,
        // eager-resolved via FailedTellNotifierInitHostedService).
        _hook =
            Plugin.GameInteropProvider.HookFromAddress<RaptureLogModule.Delegates.ShowLogMessageString>(
                RaptureLogModule.MemberFunctionPointers.ShowLogMessageString,
                ShowLogMessageStringDetour
            );
        _hook.Enable();
    }

    private unsafe void ShowLogMessageStringDetour(
        RaptureLogModule* module,
        uint logMessageId,
        Utf8String* value
    )
    {
        try
        {
            if (
                FailedTellMatcher.ShouldNotify(
                    logMessageId,
                    Plugin.Config.NotifyFailedTell,
                    FailedTellMatcher.FailedTellLogMessageIds
                )
            )
            {
                var recipient = value is null ? string.Empty : value->ToString();
                var content = string.IsNullOrEmpty(recipient)
                    ? HellionStrings.FailedTell_Notification_Generic
                    : string.Format(HellionStrings.FailedTell_Notification_Named, recipient);
                WrapperUtil.AddNotification(content, NotificationType.Warning);
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "FailedTellNotifier detour threw");
        }

        _hook!.Original(module, logMessageId, value);
    }

    public void Dispose()
    {
        _hook?.Disable();
        _hook?.Dispose();
    }
}
