using System.Runtime.InteropServices;
using System.Windows;
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;

namespace AxCopilot.Handlers;

/// <summary>
/// L27-2: 시스템 볼륨 제어 핸들러. "vol" 프리픽스로 사용합니다.
///
/// 예: vol                → 현재 볼륨 표시
///     vol 50             → 볼륨 50% 설정
///     vol up / down      → ±10% 조절
///     vol mute           → 음소거 토글
/// Enter → 해당 명령 실행.
/// Windows Core Audio API (IAudioEndpointVolume) COM 인터페이스 사용.
/// </summary>
public class VolHandler : IActionHandler
{
    public string? Prefix => "vol";

    public PluginMetadata Metadata => new(
        "볼륨 제어",
        "시스템 볼륨 조절 — 설정·증감·음소거",
        "1.0",
        "AX");

    public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        var q     = query.Trim().ToLowerInvariant();
        var items = new List<LauncherItem>();

        // 현재 볼륨 읽기
        float curLevel;
        bool  curMuted;
        try
        {
            using var audio = AudioEndpoint.GetDefault();
            curLevel = audio.GetVolume();
            curMuted = audio.GetMute();
        }
        catch
        {
            items.Add(new LauncherItem(
                "오디오 장치를 찾을 수 없습니다",
                "기본 재생 장치가 연결되어 있는지 확인하세요",
                null, null, Symbol: Symbols.Warning));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        int pct = (int)Math.Round(curLevel * 100);
        var bar = VolumeBar(pct);
        var muteLabel = curMuted ? " 🔇 음소거" : "";
        var symbol = curMuted ? Symbols.VolumeMute
                   : pct == 0 ? Symbols.VolumeMute
                   : pct < 50 ? Symbols.VolumeDown
                   : Symbols.VolumeUp;

        if (string.IsNullOrWhiteSpace(q))
        {
            items.Add(new LauncherItem(
                $"현재 볼륨: {pct}%{muteLabel}",
                $"{bar}  ·  vol 50 / vol up / vol down / vol mute",
                null, null, Symbol: symbol));

            items.Add(new LauncherItem("vol up",    "볼륨 +10%", null, ("set", Math.Min(pct + 10, 100)), Symbol: Symbols.VolumeUp));
            items.Add(new LauncherItem("vol down",  "볼륨 −10%", null, ("set", Math.Max(pct - 10, 0)),  Symbol: Symbols.VolumeDown));
            items.Add(new LauncherItem("vol mute",  curMuted ? "음소거 해제" : "음소거", null, ("mute", !curMuted), Symbol: Symbols.VolumeMute));
            items.Add(new LauncherItem("vol 0",     "볼륨 0% (무음)", null, ("set", 0),   Symbol: Symbols.VolumeMute));
            items.Add(new LauncherItem("vol 50",    "볼륨 50%",       null, ("set", 50),  Symbol: Symbols.VolumeDown));
            items.Add(new LauncherItem("vol 100",   "볼륨 100%",      null, ("set", 100), Symbol: Symbols.VolumeUp));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 명령 파싱
        if (q is "up" or "올려" or "+")
        {
            var target = Math.Min(pct + 10, 100);
            items.Add(new LauncherItem($"볼륨 +10% → {target}%", $"{VolumeBar(target)}", null, ("set", target), Symbol: Symbols.VolumeUp));
        }
        else if (q is "down" or "내려" or "-")
        {
            var target = Math.Max(pct - 10, 0);
            items.Add(new LauncherItem($"볼륨 −10% → {target}%", $"{VolumeBar(target)}", null, ("set", target), Symbol: Symbols.VolumeDown));
        }
        else if (q is "mute" or "음소거" or "m")
        {
            items.Add(new LauncherItem(
                curMuted ? "음소거 해제" : "음소거 설정",
                $"현재: {pct}%{muteLabel}",
                null, ("mute", !curMuted), Symbol: Symbols.VolumeMute));
        }
        else if (int.TryParse(q, out int val) && val is >= 0 and <= 100)
        {
            items.Add(new LauncherItem(
                $"볼륨 {val}% 설정",
                $"{VolumeBar(val)}  (현재 {pct}%)",
                null, ("set", val), Symbol: val > pct ? Symbols.VolumeUp : Symbols.VolumeDown));
        }
        else
        {
            items.Add(new LauncherItem(
                $"'{query}' — 알 수 없는 명령",
                "사용법: vol 50 / vol up / vol down / vol mute",
                null, null, Symbol: Symbols.Warning));
        }

        return Task.FromResult<IEnumerable<LauncherItem>>(items);
    }

    public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        try
        {
            using var audio = AudioEndpoint.GetDefault();

            if (item.Data is ("set", int level))
            {
                audio.SetVolume(level / 100f);
                if (audio.GetMute()) audio.SetMute(false);
                NotificationService.Notify("vol", $"볼륨 {level}%");
            }
            else if (item.Data is ("mute", bool mute))
            {
                audio.SetMute(mute);
                NotificationService.Notify("vol", mute ? "음소거" : "음소거 해제");
            }
        }
        catch (Exception ex)
        {
            NotificationService.Notify("vol", $"오류: {ex.Message}");
        }
        return Task.CompletedTask;
    }

    private static string VolumeBar(int pct)
    {
        int filled = pct / 5; // 0~20
        return "[" + new string('█', filled) + new string('░', 20 - filled) + "]";
    }

    // ─── Core Audio API COM 래퍼 ─────────────────────────────────────────────

    private sealed class AudioEndpoint : IDisposable
    {
        private readonly IAudioEndpointVolume _vol;

        private AudioEndpoint(IAudioEndpointVolume vol) => _vol = vol;

        public static AudioEndpoint GetDefault()
        {
            var enumerator = (IMMDeviceEnumerator)new MMDeviceEnumeratorClass();
            enumerator.GetDefaultAudioEndpoint(0 /*eRender*/, 1 /*eMultimedia*/, out var device);
            var iid = typeof(IAudioEndpointVolume).GUID;
            device.Activate(ref iid, 1 /*CLSCTX_ALL*/, IntPtr.Zero, out var obj);
            return new AudioEndpoint((IAudioEndpointVolume)obj);
        }

        public float GetVolume() { _vol.GetMasterVolumeLevelScalar(out float l); return l; }
        public bool  GetMute()   { _vol.GetMute(out bool m); return m; }
        public void  SetVolume(float level) { var g = Guid.Empty; _vol.SetMasterVolumeLevelScalar(Math.Clamp(level, 0f, 1f), ref g); }
        public void  SetMute(bool mute)     { var g = Guid.Empty; _vol.SetMute(mute, ref g); }

        public void Dispose()
        {
            if (_vol is IDisposable d) d.Dispose();
            if (_vol != null) Marshal.ReleaseComObject(_vol);
        }
    }

    // ─── COM 인터페이스 정의 (Windows Core Audio API) ──────────────────────

    [ComImport, Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
    private class MMDeviceEnumeratorClass { }

    [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IMMDeviceEnumerator
    {
        int EnumAudioEndpoints(int dataFlow, uint stateMask, out IntPtr devices);
        int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice device);
    }

    [Guid("D666063F-1587-4E43-81F1-B948E807363F")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IMMDevice
    {
        int Activate(ref Guid iid, uint clsCtx, IntPtr activationParams,
            [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface);
    }

    [Guid("5CDF2C82-841E-4546-9722-0CF74078229A")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IAudioEndpointVolume
    {
        int RegisterControlChangeNotify(IntPtr pNotify);
        int UnregisterControlChangeNotify(IntPtr pNotify);
        int GetChannelCount(out uint pnChannelCount);
        int SetMasterVolumeLevel(float fLevelDB, ref Guid pguidEventContext);
        int SetMasterVolumeLevelScalar(float fLevel, ref Guid pguidEventContext);
        int GetMasterVolumeLevel(out float pfLevelDB);
        int GetMasterVolumeLevelScalar(out float pfLevel);
        int SetChannelVolumeLevel(uint nChannel, float fLevelDB, ref Guid pguidEventContext);
        int SetChannelVolumeLevelScalar(uint nChannel, float fLevel, ref Guid pguidEventContext);
        int GetChannelVolumeLevel(uint nChannel, out float pfLevelDB);
        int GetChannelVolumeLevelScalar(uint nChannel, out float pfLevel);
        int SetMute([MarshalAs(UnmanagedType.Bool)] bool bMute, ref Guid pguidEventContext);
        int GetMute([MarshalAs(UnmanagedType.Bool)] out bool pbMute);
    }
}
