import t, c
from stdint import *
import string
import socket
import memhub
import stdio


# ============================================================
# requests.py — HTTP 客户端库（Python 原版风格）
#
# 用法:
#   requests._mbuddy = my_mbuddy
#   resp = requests.get("http://example.com/path")
#   if resp is not None:
#       print(resp.status_code)
#       print(resp.text)
#       requests.release(resp)
# ============================================================

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

# Socket 初始化标记
_socket_ready: bool = False

HTTP_PORT: t.CDefine = 80
HTTP_BUFSIZ: t.CDefine = 4096


# ============================================================
# Response — HTTP 响应对象
# ============================================================

class Response:
    """HTTP 响应对象，贴近 Python requests.Response 用法。

    从 _mbuddy 分配，使用完毕后调用 release(resp) 释放。
    """
    status_code: INT       # HTTP 状态码（如 200、404）
    text: str              # 响应体文本（指向 _raw_buf 内部）
    body_len: INT          # 响应体长度
    _raw_buf: str          # 原始缓冲区指针


# ============================================================
# 内部辅助函数
# ============================================================

def _EnsureSocketInit() -> INT:
    """确保 Socket 库已初始化（幂等）。"""
    global _socket_ready
    if _socket_ready: return 0
    result: INT = socket.SocketInit()
    if result == 0:
        _socket_ready = True
    return result


def _AllocResponse() -> Response | t.CPtr:
    """从 _mbuddy 分配 Response 结构体并就地初始化。"""
    if _mbuddy is None: return None
    resp: Response | t.CPtr = _mbuddy.alloc(Response.__sizeof__())
    if resp is None: return None
    resp.status_code = 0
    resp.text = None
    resp.body_len = 0
    resp._raw_buf = None
    return resp


def _ParseUrl(url: str, host_buf: str, host_size: INT, path_buf: str, path_size: INT, port: INT | t.CPtr) -> INT:
    """解析 URL，提取 host、port、path。"""
    if url is None: return -1
    if host_buf is None or path_buf is None or port is None: return -1

    p: str = url

    # 跳过 "http://"
    if string.strncmp(p, "http://", 7) == 0:
        p += 7

    # 解析 host
    host_start: str = p
    host_len: INT = 0
    while p[0] != '\0' and p[0] != ':' and p[0] != '/':
        host_len += 1
        p += 1

    if host_len == 0 or host_len >= host_size: return -1
    string.strncpy(host_buf, host_start, host_len)
    host_buf[host_len] = 0

    # 解析 port
    if p[0] == ':':
        p += 1
        port_val: INT = 0
        while p[0] >= '0' and p[0] <= '9':
            port_val = port_val * 10 + INT(p[0] - '0')
            p += 1
        c.DerefAs(port, port_val)
    else:
        c.DerefAs(port, HTTP_PORT)

    # 解析 path
    if p[0] == '/':
        path_start: str = p
        path_len: INT = 0
        while p[0] != '\0':
            path_len += 1
            p += 1
        if path_len >= path_size: return -1
        string.strncpy(path_buf, path_start, path_len)
        path_buf[path_len] = 0
    else:
        if path_size < 2: return -1
        path_buf[0] = '/'
        path_buf[1] = 0

    return 0


def _ParseStatusLine(resp_text: str, out_code: INT | t.CPtr) -> INT:
    """解析 HTTP 状态行，提取状态码。返回状态行结束偏移。"""
    if resp_text is None or out_code is None: return -1

    p: str = resp_text

    # 跳过 "HTTP/x.x "
    while p[0] != '\0' and p[0] != ' ':
        p += 1
    if p[0] == ' ': p += 1

    # 解析状态码
    code: INT = 0
    while p[0] >= '0' and p[0] <= '9':
        code = code * 10 + INT(p[0] - '0')
        p += 1

    c.DerefAs(out_code, code)

    # 找到 \n
    while p[0] != '\0' and p[0] != '\n':
        p += 1
    if p[0] == '\n': p += 1

    return INT(t.CUInt64T(p) - t.CUInt64T(resp_text))


def _FindBodyStart(resp_text: str, resp_len: INT) -> INT:
    """找到 HTTP 响应体起始位置（\\r\\n\\r\\n 之后）。"""
    if resp_text is None: return -1

    end_of_headers: str = string.strstr(resp_text, "\r\n\r\n")
    if end_of_headers is None:
        end_of_headers = string.strstr(resp_text, "\n\n")
        if end_of_headers is None:
            return -1
        return INT(t.CUInt64T(end_of_headers) - t.CUInt64T(resp_text)) + 2

    return INT(t.CUInt64T(end_of_headers) - t.CUInt64T(resp_text)) + 4


# ============================================================
# 公共 API
# ============================================================

def get(url: str) -> Response | t.CPtr:
    """HTTP GET 请求。

    Args:
        url: 完整 URL (如 "http://example.com/path")
    Returns:
        Response 对象指针（从 _mbuddy 分配），失败返回 None。
        使用完毕后调用 release(resp) 释放。
    """
    if _mbuddy is None: return None

    resp: Response | t.CPtr = _AllocResponse()
    if resp is None: return None

    if _EnsureSocketInit() != 0:
        release(resp)
        return None

    # 解析 URL
    host_buf: t.CArray[t.CChar, 256]
    path_buf: t.CArray[t.CChar, 1024]
    port: INT = 0

    if _ParseUrl(url, host_buf, 256, path_buf, 1024, c.Addr(port)) != 0:
        release(resp)
        return None

    # 创建并连接 Socket
    sock = socket.Socket(socket.AF_INET, socket.SOCK_STREAM)
    if sock.fd == socket.INVALID_SOCKET:
        release(resp)
        return None

    if sock.connect(host_buf, port) != 0:
        sock.close()
        release(resp)
        return None

    # 构建 HTTP 请求
    req_buf: str = _mbuddy.alloc(HTTP_BUFSIZ)
    if req_buf is None:
        sock.close()
        release(resp)
        return None

    stdio.snprintf(req_buf, HTTP_BUFSIZ,
        "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n",
        path_buf, host_buf)

    req_len: INT = INT(string.strlen(req_buf))
    sent: INT = sock.send(req_buf, req_len)
    _mbuddy.free(req_buf)

    if sent <= 0:
        sock.close()
        release(resp)
        return None

    # 接收响应
    resp_buf: str = _mbuddy.alloc(HTTP_BUFSIZ)
    if resp_buf is None:
        sock.close()
        release(resp)
        return None

    total: INT = 0
    while total < HTTP_BUFSIZ - 1:
        n: INT = sock.recv(resp_buf + total, HTTP_BUFSIZ - 1 - total)
        if n <= 0: break
        total += n

    resp_buf[total] = 0
    sock.close()

    resp._raw_buf = resp_buf

    # 解析状态码
    _ParseStatusLine(resp_buf, c.Addr(resp.status_code))

    # 找到 body
    body_offset: INT = _FindBodyStart(resp_buf, total)
    if body_offset < 0 or body_offset >= total:
        resp.text = None
        resp.body_len = 0
    else:
        resp.text = resp_buf + body_offset
        resp.body_len = total - body_offset

    return resp


def post(url: str, data: str = None, content_type: str = "application/x-www-form-urlencoded") -> Response | t.CPtr:
    """HTTP POST 请求。

    Args:
        url: 完整 URL
        data: 请求体数据
        content_type: Content-Type 头（默认 "application/x-www-form-urlencoded"）
    Returns:
        Response 对象指针（从 _mbuddy 分配），失败返回 None。
    """
    if _mbuddy is None: return None

    resp: Response | t.CPtr = _AllocResponse()
    if resp is None: return None

    if _EnsureSocketInit() != 0:
        release(resp)
        return None

    # 解析 URL
    host_buf: t.CArray[t.CChar, 256]
    path_buf: t.CArray[t.CChar, 1024]
    port: INT = 0

    if _ParseUrl(url, host_buf, 256, path_buf, 1024, c.Addr(port)) != 0:
        release(resp)
        return None

    body_len: INT = INT(string.strlen(data)) if data is not None else 0
    ct: str = content_type if content_type is not None else "application/x-www-form-urlencoded"

    # 创建并连接 Socket
    sock = socket.Socket(socket.AF_INET, socket.SOCK_STREAM)
    if sock.fd == socket.INVALID_SOCKET:
        release(resp)
        return None

    if sock.connect(host_buf, port) != 0:
        sock.close()
        release(resp)
        return None

    # 构建 HTTP 请求
    req_buf: str = _mbuddy.alloc(HTTP_BUFSIZ)
    if req_buf is None:
        sock.close()
        release(resp)
        return None

    stdio.snprintf(req_buf, HTTP_BUFSIZ,
        "POST %s HTTP/1.1\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\nConnection: close\r\n\r\n",
        path_buf, host_buf, ct, body_len)

    hdr_len: INT = INT(string.strlen(req_buf))

    # 发送头部 + body
    sent: INT = sock.send(req_buf, hdr_len)
    if sent > 0 and body_len > 0 and data is not None:
        sock.send(data, body_len)

    _mbuddy.free(req_buf)

    # 接收响应
    resp_buf: str = _mbuddy.alloc(HTTP_BUFSIZ)
    if resp_buf is None:
        sock.close()
        release(resp)
        return None

    total: INT = 0
    while total < HTTP_BUFSIZ - 1:
        n: INT = sock.recv(resp_buf + total, HTTP_BUFSIZ - 1 - total)
        if n <= 0: break
        total += n

    resp_buf[total] = 0
    sock.close()

    resp._raw_buf = resp_buf

    _ParseStatusLine(resp_buf, c.Addr(resp.status_code))
    body_offset: INT = _FindBodyStart(resp_buf, total)
    if body_offset < 0 or body_offset >= total:
        resp.text = None
        resp.body_len = 0
    else:
        resp.text = resp_buf + body_offset
        resp.body_len = total - body_offset

    return resp


def release(resp: Response | t.CPtr):
    """释放 Response 对象及其内部缓冲区。

    Args:
        resp: 要释放的 Response 对象
    """
    if resp is None: return
    if _mbuddy is None: return
    if resp._raw_buf is not None:
        _mbuddy.free(resp._raw_buf)
    _mbuddy.free(t.CVoid(t.CUInt64T(resp), t.CPtr))