"""Thư viện dùng chung quản lý tiến trình Win32 cho Path of Exile 2.

Cung cấp các hàm kiểm tra trạng thái hoạt động, trích xuất Process ID (PID)
và tìm kiếm tiến trình theo tên thực thi. Hỗ trợ cả thư viện psutil và Win32 Toolhelp32
Snapshot API với cơ chế giải phóng tài nguyên triệt để trong khối finally.
Tuân thủ nghiêm ngặt chuẩn tài liệu Google Docstrings theo Rule 16.
"""

import ctypes
import ctypes.wintypes
import os
from typing import List, Optional

try:
    import psutil
    _HAS_PSUTIL = True
except ImportError:
    psutil = None  # type: ignore
    _HAS_PSUTIL = False

POE2_PROCESS_NAMES = (
    "pathofexile.exe",
    "pathofexilesteam.exe",
    "pathofexile_x64.exe",
    "pathofexile_x64steam.exe",
    "pathofexile2.exe",
)

TH32CS_SNAPPROCESS = 0x00000002


class _PROCESSENTRY32W(ctypes.Structure):
    _fields_ = [
        ("dwSize", ctypes.wintypes.DWORD),
        ("cntUsage", ctypes.wintypes.DWORD),
        ("th32ProcessID", ctypes.wintypes.DWORD),
        ("th32DefaultHeapID", ctypes.POINTER(ctypes.c_ulong)),
        ("th32ModuleID", ctypes.wintypes.DWORD),
        ("cntThreads", ctypes.wintypes.DWORD),
        ("th32ParentProcessID", ctypes.wintypes.DWORD),
        ("pcPriClassBase", ctypes.c_long),
        ("dwFlags", ctypes.wintypes.DWORD),
        ("szExeFile", ctypes.c_wchar * 260),
    ]


def find_process_by_name(name: str) -> List[int]:
    """Tìm danh sách Process ID (PID) của tất cả tiến trình có tên tệp thực thi khớp với tên chỉ định.

    So sánh không phân biệt hoa thường. Ưu tiên kiểm tra nhanh qua psutil nếu có sẵn,
    và tự động chuyển sang Windows Toolhelp32 API nếu psutil chưa cài đặt hoặc gặp sự cố.
    Đảm bảo giải phóng handle snapshot trong khối finally.

    Args:
        name: Tên tệp thực thi cần tìm (ví dụ: 'PathOfExile.exe').

    Returns:
        List[int]: Danh sách các PID đang chạy khớp với tên chỉ định. Trả về rỗng nếu không tìm thấy.

    Thread-safety:
        Thread-safe.

    Example:
        >>> pids = find_process_by_name("PathOfExile.exe")
        >>> print(f"Tìm thấy {len(pids)} tiến trình.")
    """
    target_name = name.lower()
    found_pids: List[int] = []

    # 1. Thử qua psutil
    if psutil is not None:
        try:
            for proc in psutil.process_iter(["pid", "name"]):
                try:
                    pname = proc.info.get("name")
                    if pname and pname.lower() == target_name:
                        pid = proc.info.get("pid")
                        if pid is not None:
                            found_pids.append(int(pid))
                except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
                    continue
            return found_pids
        except Exception:
            pass

    # 2. Fallback qua Win32 Toolhelp32 Snapshot API
    if os.name == "nt":
        kernel32 = ctypes.windll.kernel32
        h_snapshot = kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
        invalid_handle = ctypes.wintypes.HANDLE(-1).value
        if h_snapshot and h_snapshot != invalid_handle:
            try:
                pe32 = _PROCESSENTRY32W()
                pe32.dwSize = ctypes.sizeof(_PROCESSENTRY32W)
                if kernel32.Process32FirstW(h_snapshot, ctypes.byref(pe32)):
                    while True:
                        if pe32.szExeFile.lower() == target_name:
                            found_pids.append(int(pe32.th32ProcessID))
                        if not kernel32.Process32NextW(h_snapshot, ctypes.byref(pe32)):
                            break
            finally:
                kernel32.CloseHandle(h_snapshot)

    return found_pids


def is_poe2_running() -> bool:
    """Kiểm tra xem tiến trình game Path of Exile 2 có đang hoạt động trên hệ thống hay không.

    Quét đối chiếu toàn bộ các tên tệp thực thi phổ biến của POE2 (Standalone, Steam, x64).

    Returns:
        bool: True nếu phát hiện ít nhất một tiến trình POE2 đang chạy, ngược lại False.

    Thread-safety:
        Thread-safe.

    Example:
        >>> if is_poe2_running():
        ...     print("Client POE2 đang hoạt động.")
    """
    return get_poe2_pid() is not None


def get_poe2_pid() -> Optional[int]:
    """Lấy Process ID (PID) của tiến trình Path of Exile 2 đang chạy.

    Tìm kiếm tuần tự qua danh sách các tên tệp thực thi hợp lệ và trả về PID đầu tiên
    được tìm thấy.

    Returns:
        Optional[int]: PID của tiến trình POE2 nếu đang chạy, hoặc None nếu không tìm thấy.

    Thread-safety:
        Thread-safe.

    Example:
        >>> pid = get_poe2_pid()
        >>> if pid is not None:
        ...     print(f"POE2 PID: {pid}")
    """
    for proc_name in POE2_PROCESS_NAMES:
        pids = find_process_by_name(proc_name)
        if pids:
            return pids[0]
    return None


def get_process_name_by_pid(pid: int) -> str:
    """Lấy tên tệp thực thi (lowercase) của tiến trình từ Process ID (PID).

    Ưu tiên tra cứu qua psutil nếu có sẵn, và tự động fallback về Win32
    QueryFullProcessImageNameW (PROCESS_QUERY_LIMITED_INFORMATION) không cần quyền Admin.

    Args:
        pid: Process ID cần kiểm tra.

    Returns:
        str: Tên tệp thực thi dạng chữ thường (ví dụ: 'pathofexile.exe'), hoặc chuỗi rỗng.

    Thread-safety:
        Thread-safe.
    """
    if not pid or pid <= 0:
        return ""

    # 1. Thử qua psutil
    if psutil is not None:
        try:
            p = psutil.Process(pid)
            name = p.name()
            if name:
                return name.lower()
        except Exception:
            pass

    # 2. Fallback qua Win32 QueryFullProcessImageNameW
    if os.name == "nt":
        PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
        kernel32 = ctypes.windll.kernel32
        h_proc = kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, pid)
        if h_proc:
            try:
                buf = ctypes.create_unicode_buffer(512)
                size = ctypes.wintypes.DWORD(512)
                if kernel32.QueryFullProcessImageNameW(h_proc, 0, buf, ctypes.byref(size)):
                    return os.path.basename(buf.value).lower()
            except Exception:
                pass
            finally:
                kernel32.CloseHandle(h_proc)

    return ""


def is_poe2_pid(pid: int) -> bool:
    """Kiểm tra xem Process ID (PID) có thuộc về tiến trình game Path of Exile 2 hay không.

    Đối chiếu tên tệp thực thi với POE2_PROCESS_NAMES hoặc chuỗi 'pathofexile'.

    Args:
        pid: Process ID cần kiểm tra.

    Returns:
        bool: True nếu là tiến trình POE2, ngược lại False.

    Thread-safety:
        Thread-safe.
    """
    name = get_process_name_by_pid(pid)
    if not name:
        return False
    return name in POE2_PROCESS_NAMES or "pathofexile" in name

