"""
HTTP代理工具模块

提供带代理的HTTP请求功能，支持：
- 代理配置解析
- 多代理随机轮换
- GET/POST请求代理封装
- 无代理时自动降级

配置环境变量：
    export XSDK_PROXY="http://{usr}:{pwd}@{host}:{port}"
    多个代理用 ^^ 分隔：
    export XSDK_PROXY="http://usr1:pwd1@host1:port1^^http://usr2:pwd2@host2:port2"

代理配置来源（IP代理商）：
    https://proxymesh.com/account/dashboard/
"""
import time
import random
import logging
from typing import Optional, Dict, Any, List, Union

import requests

from xsdk.xutil import Xutil
from urllib.parse import urlparse, quote_plus

logger = logging.getLogger(__name__)


class ProxyConfig:
    """
    代理配置类

    解析和管理HTTP代理配置信息。

    Attributes:
        usr: 代理用户名
        pwd: 代理密码
        host: 代理主机
        port: 代理端口
        schema: 协议类型（http/https）

    Example:
        >>> config = ProxyConfig.parse_config("http://user:pass@proxy.example.com:8080")
        >>> proxies = {"http": config.to_str(), "https": config.to_str()}
    """

    def __init__(
        self,
        usr: str = "",
        pwd: str = "",
        host: str = "",
        port: int = 80,
        schema: str = "http"
    ):
        self.usr = usr
        self.pwd = pwd
        self.host = host
        self.port = port
        self.schema = schema

    def __str__(self) -> str:
        return str(self.__dict__)

    def to_str(self) -> str:
        """
        转换为代理URL字符串

        Returns:
            格式化的代理URL
        """
        return f"{self.schema}://{self.usr}:{self.pwd}@{self.host}:{self.port}"

    @staticmethod
    def parse_config(url: str) -> "ProxyConfig":
        """
        解析代理URL为配置对象

        Args:
            url: 代理URL字符串

        Returns:
            ProxyConfig实例
        """
        url_parsed = urlparse(url)
        proxy_config = ProxyConfig()
        proxy_config.usr = quote_plus(url_parsed.username or "")
        proxy_config.pwd = quote_plus(url_parsed.password or "")
        proxy_config.host = url_parsed.hostname or ""
        proxy_config.port = url_parsed.port or 80
        proxy_config.schema = url_parsed.scheme
        return proxy_config

    @staticmethod
    def get_config_list() -> List["ProxyConfig"]:
        """
        从环境变量获取代理配置列表

        Returns:
            ProxyConfig列表，配置不存在时返回空列表
        """
        proxy_str = Xutil.get_env_param("XSDK_PROXY")
        if Xutil.is_empty(proxy_str):
            logger.debug("XSDK_PROXY environment variable not set")
            return []

        proxy_list = proxy_str.strip().split("^^")
        if not proxy_list:
            logger.debug("XSDK_PROXY is empty")
            return []

        res_list = []
        for item in proxy_list:
            if item.strip():
                config = ProxyConfig.parse_config(item.strip())
                res_list.append(config)

        return res_list

    @staticmethod
    def get_config() -> Optional["ProxyConfig"]:
        """
        随机获取一个代理配置

        Returns:
            随机选择的ProxyConfig，无配置时返回None
        """
        proxy_list = ProxyConfig.get_config_list()
        if not proxy_list:
            return None

        idx = random.randint(0, len(proxy_list) - 1)
        return proxy_list[idx]


class HttpProxy:
    """
    HTTP代理请求类

    提供带代理支持的GET和POST请求方法，无代理时自动降级为直连。

    Example:
        >>> response = HttpProxy.do_get("https://api.example.com/data")
        >>> response = HttpProxy.do_post("https://api.example.com/data", json={"key": "value"})
    """

    @staticmethod
    def do_get(
        url: str,
        params: Optional[Dict[str, Any]] = None,
        headers: Optional[Dict[str, str]] = None,
        cookies: Optional[Dict[str, str]] = None,
        logger: logging.Logger = logger,
        timeout: int = 150
    ) -> Optional[Union[str, int]]:
        """
        发送带代理的GET请求

        Args:
            url: 请求URL
            params: 查询参数
            headers: 请求头
            cookies: Cookie
            logger: 日志记录器
            timeout: 超时时间（秒）

        Returns:
            响应文本内容，失败时返回None或状态码
        """
        url_proxy = ProxyConfig.get_config()
        if url_proxy is None:
            logger.warning("Missing proxy config, using direct connection")
            time.sleep(1)  # 无代理时增加延时
            proxies = {}
        else:
            logger.info(f"Using proxy host: {url_proxy.host}")
            proxies = {
                "http": url_proxy.to_str(),
                "https": url_proxy.to_str()
            }

        try:
            logger.info(f"GET {url}")
            response = requests.get(
                url,
                params=params,
                headers=headers,
                cookies=cookies,
                proxies=proxies,
                timeout=timeout
            )
            return HttpProxy._parse_response(url, response, logger)
        except Exception as e:
            logger.warning(f"Proxy request failed: {e}")
            return None

    @staticmethod
    def do_post(
        url: str,
        params: Optional[Dict[str, Any]] = None,
        data: Optional[Dict[str, Any]] = None,
        json: Optional[Dict[str, Any]] = None,
        headers: Optional[Dict[str, str]] = None,
        cookies: Optional[Dict[str, str]] = None,
        logger: logging.Logger = logger,
        timeout: int = 1000
    ) -> Optional[Union[str, int]]:
        """
        发送带代理的POST请求

        Args:
            url: 请求URL
            params: 查询参数
            data: 表单数据
            json: JSON数据
            headers: 请求头
            cookies: Cookie
            logger: 日志记录器
            timeout: 超时时间（秒）

        Returns:
            响应文本内容，失败时返回None或状态码
        """
        url_proxy = ProxyConfig.get_config()
        if url_proxy is None:
            logger.warning("Missing proxy config, using direct connection")
            time.sleep(1)  # 无代理时增加延时
            proxies = {}
        else:
            logger.info(f"Using proxy host: {url_proxy.host}")
            proxies = {
                "http": url_proxy.to_str(),
                "https": url_proxy.to_str()
            }

        try:
            logger.info(f"POST {url}")
            response = requests.post(
                url,
                params=params,
                data=data,
                json=json,
                headers=headers,
                cookies=cookies,
                proxies=proxies,
                timeout=timeout
            )
            return HttpProxy._parse_response(url, response, logger)
        except Exception as e:
            logger.warning(f"Proxy request failed: {e}")
            return None

    @staticmethod
    def _parse_response(
        url: str,
        response: requests.Response,
        logger: logging.Logger = logger
    ) -> Optional[Union[str, int]]:
        """
        解析HTTP响应

        Args:
            url: 请求URL
            response: 响应对象
            logger: 日志记录器

        Returns:
            成功时返回响应文本，失败时返回状态码或None
        """
        if response is None:
            return None
        if response.status_code != 200:
            logger.error(f"HTTP error: {url} | {response.status_code} | {response.reason}")
            return response.status_code
        return response.text


if __name__ == '__main__':
    proxy_config_list = ProxyConfig.get_config_list()
    for item in proxy_config_list:
        print(item)
