import t, c
from stdint import *
import platmacro
import memhub
import string

# Windows 后端依赖
import w32.win32base
import w32.win32file
import w32.win32process
import w32.win32sync

# POSIX 后端依赖
import posix


# 子进程输出缓冲区大小
OUTPUT_BUF_SIZE: t.CDefine = 65536

# 模块级全局 MBuddy 指针（由用户在 main 中赋值初始化）
_mbuddy: memhub.MemBuddy | t.CPtr


# =============================================================================
# CompletedProcess 结果类
# =============================================================================

class CompletedProcess:
    """subprocess.run 的返回结果"""
    args: str           # 原始命令行
    returncode: int     # 进程退出码（0 表示成功）
    stdout: str         # 标准输出（capture_output=True 时）
    stderr: str         # 标准错误（capture_output=True 时）

    def __new__(self, args: str, returncode: int, stdout: str, stderr: str) -> t.CPtr:
        # 堆分配（避免返回栈地址导致 use-after-return）
        return t.CPtr(_mbuddy.alloc(CompletedProcess.__sizeof__()))

    def __init__(self, args: str, returncode: int, stdout: str, stderr: str):
        self.args = args
        self.returncode = returncode
        self.stdout = stdout
        self.stderr = stderr


# =============================================================================
# 入口函数
# =============================================================================

def run(args: str, capture_output: bool = True, text: bool = True) -> CompletedProcess | t.CPtr:
    """同步执行子进程并等待完成。
    args 为完整命令行字符串。
    capture_output=True 时捕获 stdout/stderr。
    text=True 时输出为字符串（当前实现总是返回字符串）。
    返回 CompletedProcess 对象。"""
    if platmacro.IS_WINDOWS:
        return _run_win32(args, capture_output, text)
    return _run_posix(args, capture_output, text)


# =============================================================================
# Windows 后端实现
# =============================================================================

def _run_win32(args: str, capture_output: bool, text: bool) -> CompletedProcess | t.CPtr:
    """Windows 平台子进程执行（CreateProcessA + 管道）"""
    # 复制命令行（CreateProcessA 需要可修改的 lpCommandLine）
    args_len: t.CSizeT = len(args)
    cmd_line: bytes = _mbuddy.alloc(args_len + 1)
    if cmd_line == None:
        return CompletedProcess(args, -1, None, None)
    i: t.CSizeT = 0
    for ch in args:
        cmd_line[i] = ch
        i += 1
    cmd_line[args_len] = 0

    # 安全属性（允许句柄继承）
    sa: w32.win32base.SECURITY_ATTRIBUTES = w32.win32base.SECURITY_ATTRIBUTES()
    sa.nLength = w32.win32base.SECURITY_ATTRIBUTES.__sizeof__()
    sa.lpSecurityDescriptor = t.CPtr(0)
    sa.bInheritHandle = 1  # TRUE

    # 创建 stdout 管道
    stdout_read: w32.win32base.HANDLE = t.CPtr(0)
    stdout_write: w32.win32base.HANDLE = t.CPtr(0)
    if capture_output:
        pipe_result: BOOL = w32.win32file.CreatePipe(
            t.CPtr(c.Addr(stdout_read)),
            t.CPtr(c.Addr(stdout_write)),
            t.CPtr(c.Addr(sa)),
            0
        )
        if pipe_result == 0:
            return CompletedProcess(args, -1, None, None)
        # 防止读端被子进程继承
        w32.win32file.SetHandleInformation(stdout_read, w32.win32file.HANDLE_FLAG_INHERIT, 0)

    # 设置启动信息
    si: w32.win32process.STARTUPINFOA = w32.win32process.STARTUPINFOA()
    si.cb = w32.win32process.STARTUPINFOA.__sizeof__()
    if capture_output:
        si.dwFlags = w32.win32process.STARTF_USESTDHANDLES
        si.hStdOutput = stdout_write
        si.hStdError = stdout_write  # stderr 合并到 stdout

    pi: w32.win32process.PROCESS_INFORMATION = w32.win32process.PROCESS_INFORMATION()

    # 创建进程
    # capture_output=True 时用 CREATE_NO_WINDOW（输出到管道，不需要控制台）
    # capture_output=False 时用 0（继承父进程控制台，让 stdout 直接显示）
    create_flags: ULONG = w32.win32process.CREATE_NO_WINDOW if capture_output else 0
    create_result: BOOL = w32.win32process.CreateProcessA(
        t.CPtr(0),       # lpApplicationName (NULL，从命令行解析)
        cmd_line,        # lpCommandLine (可修改)
        t.CPtr(0),       # lpProcessAttributes
        t.CPtr(0),       # lpThreadAttributes
        1,               # bInheritHandles = TRUE
        create_flags,    # dwCreationFlags
        t.CPtr(0),       # lpEnvironment (继承父进程)
        t.CPtr(0),       # lpCurrentDirectory (继承父进程)
        t.CPtr(c.Addr(si)),
        t.CPtr(c.Addr(pi))
    )

    if create_result == 0:
        # 创建失败
        if capture_output:
            w32.win32base.CloseHandle(stdout_read)
            w32.win32base.CloseHandle(stdout_write)
        return CompletedProcess(args, -1, None, None)

    # 关闭父进程的写端（子进程已有写端的继承副本）
    if capture_output:
        w32.win32base.CloseHandle(stdout_write)

    # 读取 stdout + 等待进程结束
    # 注意：必须边读边等，否则子进程输出超过管道缓冲区时会死锁
    # （子进程阻塞在写 stdout，父进程阻塞在 WaitForSingleObject）
    stdout_str: str = None
    if capture_output:
        buf_size: ULONG = OUTPUT_BUF_SIZE
        buf: bytes = _mbuddy.alloc(buf_size)
        if buf != None:
            total: ULONG = 0
            while 1:
                bytes_read: ULONG = 0
                read_result: BOOL = w32.win32file.ReadFile(
                    stdout_read,
                    buf + total,
                    buf_size - 1 - total,
                    t.CPtr(c.Addr(bytes_read)),
                    t.CPtr(0)
                )
                if read_result == 0 or bytes_read == 0:
                    break
                total += bytes_read
                # 缓冲区将满，扩容（翻倍）
                if total >= buf_size - 1 - 4096:
                    new_size: ULONG = buf_size * 2
                    new_buf: bytes = _mbuddy.alloc(new_size)
                    if new_buf != None:
                        string.memcpy(new_buf, buf, total)
                        buf = new_buf
                        buf_size = new_size
                    else:
                        # 扩容失败，停止读取
                        break
            buf[total] = 0
            stdout_str = str(buf)

    # 等待进程结束（如果还没退出的话）
    w32.win32sync.WaitForSingleObject(pi.hProcess, w32.win32base.INFINITE)

    # 获取退出码
    exit_code: ULONG = 0
    w32.win32process.GetExitCodeProcess(pi.hProcess, t.CPtr(c.Addr(exit_code)))

    # 关闭句柄
    w32.win32base.CloseHandle(pi.hProcess)
    w32.win32base.CloseHandle(pi.hThread)
    if capture_output:
        w32.win32base.CloseHandle(stdout_read)

    return CompletedProcess(args, exit_code, stdout_str, None)


# =============================================================================
# POSIX 后端实现
# =============================================================================

def _run_posix(args: str, capture_output: bool, text: bool) -> CompletedProcess | t.CPtr:
    """POSIX 平台子进程执行（fork + execvp + pipe + waitpid）
    注意：当前为桩函数。POSIX 实现需要 pipe/fork/waitpid，
    这些函数在 Windows MinGW 上不可用，待 POSIX 平台测试时再完善。"""
    return CompletedProcess(args, -1, None, None)
