using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Threading;

namespace AxCopilot.Services;

/// <summary>
/// Windows 열기/저장 대화상자(#32770)를 감지하여 이벤트를 발생시킵니다.
/// SetWinEventHook으로 HWND 생성/소멸을 모니터링합니다.
/// </summary>
public class FileDialogWatcher : IDisposable
{
    // ─── P/Invoke ────────────────────────────────────────────────────────────

    private delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType,
        IntPtr hwnd, int idObject, int idChild, uint idEventThread, uint dwmsEventTime);

    [DllImport("user32.dll")]
    private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax,
        IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc,
        uint idProcess, uint idThread, uint dwFlags);

    [DllImport("user32.dll")]
    private static extern bool UnhookWinEvent(IntPtr hWinEventHook);

    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

    [DllImport("user32.dll")]
    private static extern bool IsWindowVisible(IntPtr hWnd);

    private const uint EVENT_OBJECT_SHOW = 0x8002;
    private const uint EVENT_OBJECT_CREATE = 0x8000;
    private const uint WINEVENT_OUTOFCONTEXT = 0x0000;
    private const uint WINEVENT_SKIPOWNPROCESS = 0x0002;

    // ─── 상태 ────────────────────────────────────────────────────────────────

    private IntPtr _hook;
    private WinEventDelegate? _delegate; // prevent GC
    private bool _disposed;
    private readonly Dispatcher _dispatcher;

    /// <summary>열기/저장 대화상자가 감지되면 발생합니다. IntPtr = 대화상자 HWND.</summary>
    public event EventHandler<IntPtr>? FileDialogOpened;

    public FileDialogWatcher()
    {
        _dispatcher = Dispatcher.CurrentDispatcher;
    }

    public void Start()
    {
        if (_hook != IntPtr.Zero) return;
        _delegate = OnWinEvent;
        _hook = SetWinEventHook(
            EVENT_OBJECT_SHOW, EVENT_OBJECT_SHOW,
            IntPtr.Zero, _delegate,
            0, 0,
            WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
    }

    public void Stop()
    {
        if (_hook != IntPtr.Zero)
        {
            UnhookWinEvent(_hook);
            _hook = IntPtr.Zero;
        }
    }

    private void OnWinEvent(IntPtr hWinEventHook, uint eventType,
        IntPtr hwnd, int idObject, int idChild, uint idEventThread, uint dwmsEventTime)
    {
        if (hwnd == IntPtr.Zero || idObject != 0) return;
        if (!IsWindowVisible(hwnd)) return;

        // 클래스명 #32770 = 공통 대화상자
        var sb = new StringBuilder(256);
        GetClassName(hwnd, sb, 256);
        var className = sb.ToString();

        if (className != "#32770") return;

        // 창 제목으로 열기/저장 대화상자인지 확인
        var titleSb = new StringBuilder(256);
        GetWindowText(hwnd, titleSb, 256);
        var title = titleSb.ToString();

        if (title.Contains("열기") || title.Contains("Open") ||
            title.Contains("저장") || title.Contains("Save") ||
            title.Contains("다른 이름") || title.Contains("Browse") ||
            title.Contains("폴더") || title.Contains("Folder"))
        {
            _dispatcher.BeginInvoke(() => FileDialogOpened?.Invoke(this, hwnd));
        }
    }

    public void Dispose()
    {
        if (_disposed) return;
        _disposed = true;
        Stop();
    }
}
