#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
HTTP请求模块
提供HTTP请求相关功能
"""

import json
import requests
import xmltodict
import asyncio
import concurrent.futures
import aiohttp
from typing import Dict, Optional, Callable
from utils.logger import get_logger
from utils.json_parser import safe_parse_dict

logger = get_logger("services.online_script.http_module")


class VWEDHttpModule:
    """HTTP请求模块"""
    
    def __init__(self, script_id: str):
        self.script_id = script_id
        self.session = requests.Session()
        self.global_headers = {}
        
        # 强制禁用所有代理，包括环境变量中的代理设置
        self.session.proxies = {}
        self.session.trust_env = False  # 忽略环境变量中的代理设置
    
    def _execute_safely(self, func, *args, **kwargs):
        """安全执行HTTP请求，在事件循环中使用独立线程池"""
        try:
            # 检查是否在事件循环中
            in_event_loop = False
            try:
                asyncio.get_running_loop()
                in_event_loop = True
            except RuntimeError:
                in_event_loop = False
            
            if in_event_loop:
                # 在事件循环中，使用独立的线程池执行HTTP请求
                # 增加线程数量和超时时间，避免死锁
                with concurrent.futures.ThreadPoolExecutor(max_workers=3, thread_name_prefix=f"http_script_{self.script_id}") as executor:
                    future = executor.submit(func, *args, **kwargs)
                    return future.result(timeout=60)  # 增加到60秒超时
            else:
                # 没有事件循环，直接执行
                return func(*args, **kwargs)
                
        except concurrent.futures.TimeoutError:
            logger.error(f"HTTP请求线程池执行超时: {func.__name__}, 脚本: {self.script_id}")
            return None
        except Exception as e:
            logger.error(f"HTTP请求执行失败: {func.__name__}, 脚本: {self.script_id}, 错误: {str(e)}")
            return None
        
    async def request_post(self, url: str, param: str) -> Optional[str]:
        """
        发送POST请求，参数为JSON格式
        
        Args:
            url: 请求的URL
            param: JSON字符串，请求的参数
            
        Returns:
            若请求成功：返回响应的JSON字符串
            若请求失败：None
        """
        # 检查是否是本地请求，如果是则使用异步方式避免死锁
        is_local_request = any(host in url for host in ['127.0.0.1', 'localhost', '::1'])
        
        if is_local_request:
            try:
                # 检查是否在事件循环中
                asyncio.get_running_loop()
                return await self._async_request_post(url, param)
            except RuntimeError:
                # 不在事件循环中，使用同步方式
                pass
        
        return self._execute_safely(self._sync_request_post, url, param)
    
    async def _async_request_post(self, url: str, param: str) -> Optional[str]:
        """异步执行POST请求的内部方法，专门处理本地循环请求"""
        try:
            headers = {"Content-Type": "application/json"}
            headers.update(self.global_headers)
            
            # 尝试将参数转换为有效JSON
            dict_param = safe_parse_dict(param)
            if dict_param is None:
                logger.error(f"异步POST请求失败: {url}, 参数无法转换为有效JSON格式")
                return None

            # 记录请求信息用于调试
            logger.info(f"发送异步POST请求: {url}, 脚本: {self.script_id}")
            
            # 使用aiohttp进行异步请求，避免阻塞事件循环
            timeout = aiohttp.ClientTimeout(total=60)  # 60秒超时
                
            async with aiohttp.ClientSession(timeout=timeout) as session:
                # 使用json参数而不是data参数，让aiohttp自动处理JSON序列化和Content-Type
                async with session.post(url, json=dict_param, headers={k: v for k, v in headers.items() if k.lower() != 'content-type'}) as response:
                    response.raise_for_status()
                    result = await response.text()
                    logger.info(f"异步POST请求成功: {url}, 状态码: {response.status}")
                    return result
                    
        except asyncio.TimeoutError:
            logger.error(f"异步POST请求超时: {url}")
            return None
            
    def _sync_request_post(self, url: str, param: str) -> Optional[str]:
        """同步执行POST请求的内部方法"""
        try:
            headers = {"Content-Type": "application/json"}
            headers.update(self.global_headers)
            
            # 尝试将参数转换为有效JSON
            json_param = self._prepare_json_param(param)
            if json_param is None:
                logger.error(f"POST请求失败: {url}, 参数无法转换为有效JSON格式")
                return None

            # 记录请求信息用于调试
            logger.info(f"发送POST请求: {url}, 代理设置: {self.session.proxies}")
            
            # 检查是否是本地循环请求，如果是则增加超时时间
            is_local_request = any(host in url for host in ['127.0.0.1', 'localhost', '::1'])
            timeout = 45 if is_local_request else 30
            
            # 设置超时时间，避免无限阻塞，强制不使用代理
            response = self.session.post(
                url, 
                data=json_param, 
                headers=headers, 
                timeout=timeout,
                proxies={}  # 强制在请求级别也不使用代理
            )
            response.raise_for_status()
            
            logger.info(f"POST请求成功: {url}, 状态码: {response.status_code}")
            return response.text
            
        except requests.exceptions.Timeout:
            logger.error(f"POST请求超时: {url}")
            return None
        except requests.exceptions.ConnectionError as e:
            logger.error(f"POST请求连接失败: {url}, 错误: {str(e)}")
            return None
        except Exception as e:
            logger.error(f"POST请求失败: {url}, 错误: {str(e)}")
            return None
    
    def _prepare_json_param(self, param: str) -> Optional[str]:
        """准备JSON参数，处理多种输入格式"""
        try:
            # 首先尝试直接解析为JSON
            test_parse = safe_parse_dict(param)
            if test_parse is not None:
                # 是有效JSON，直接返回
                return param
            
            # 如果解析失败，尝试使用eval()解析Python字典字符串（安全性有限制）
            # 只在字符串包含特定Python字典格式时使用
            if param.strip().startswith('{') and param.strip().endswith('}'):
                try:
                    # 尝试使用安全的字面量评估
                    import ast
                    dict_obj = ast.literal_eval(param)
                    if isinstance(dict_obj, dict):
                        return json.dumps(dict_obj, ensure_ascii=False)
                except (ValueError, SyntaxError):
                    # 如果ast.literal_eval失败，记录错误
                    logger.warning(f"参数格式无法解析: {param[:100]}...")
            
            return None
            
        except Exception as e:
            logger.error(f"准备JSON参数时发生错误: {str(e)}")
            return None
    
    async def _generic_request(self, url: str, method: str = "POST", param: str = None, 
                             headers: Dict[str, str] = None, 
                             data_processor: Callable = None,
                             response_processor: Callable = None) -> Optional[str]:
        """
        通用请求处理函数，支持异步和同步请求，避免死锁
        
        Args:
            url: 请求的URL
            method: HTTP方法 (GET, POST, PUT等)
            param: 请求参数
            headers: 请求头
            data_processor: 数据预处理函数
            response_processor: 响应后处理函数
            
        Returns:
            请求结果或None
        """
        # 检查是否是本地请求，如果是则使用异步方式避免死锁
        is_local_request = any(host in url for host in ['127.0.0.1', 'localhost', '::1'])
        
        if is_local_request:
            try:
                # 检查是否在事件循环中
                asyncio.get_running_loop()
                return await self._async_generic_request(url, method, param, headers, data_processor, response_processor)
            except RuntimeError:
                # 不在事件循环中，使用同步方式
                pass
        
        return self._execute_safely(self._sync_generic_request, url, method, param, headers, data_processor, response_processor)
    
    async def _async_generic_request(self, url: str, method: str, param: str, 
                                   headers: Dict[str, str], data_processor: Callable,
                                   response_processor: Callable) -> Optional[str]:
        """异步执行通用请求的内部方法"""
        try:
            # 合并请求头
            request_headers = headers or {}
            request_headers.update(self.global_headers)
            
            # 数据预处理
            processed_data = data_processor(param) if data_processor and param else param
            if param and not processed_data and data_processor:
                logger.error(f"异步{method}请求失败: {url}, 数据预处理失败")
                return None
            
            # 记录请求信息用于调试
            logger.info(f"发送异步{method}请求: {url}, 脚本: {self.script_id}")
            
            # 使用aiohttp进行异步请求，避免阻塞事件循环
            timeout = aiohttp.ClientTimeout(total=60)  # 60秒超时
            async with aiohttp.ClientSession(timeout=timeout) as session:
                # 根据方法选择合适的请求方式
                if method.upper() == "GET":
                    async with session.get(url, headers=request_headers) as response:
                        response.raise_for_status()
                        result = await response.text()
                elif method.upper() == "POST":

                    # 判断数据类型，选择合适的参数
                    if 'application/json' in request_headers.get('Content-Type', ''):
                        # JSON数据使用json参数，过滤Content-Type避免重复
                        filtered_headers = {k: v for k, v in request_headers.items() if k.lower() != 'content-type'}
                        async with session.post(url, json=processed_data, headers=filtered_headers) as response:
                            response.raise_for_status()
                            result = await response.text()
                    else:
                        # 其他数据使用data参数
                        async with session.post(url, data=processed_data, headers=request_headers) as response:
                            response.raise_for_status()
                            result = await response.text()
                elif method.upper() == "PUT":
                    if 'application/json' in request_headers.get('Content-Type', ''):
                        filtered_headers = {k: v for k, v in request_headers.items() if k.lower() != 'content-type'}
                        async with session.put(url, json=processed_data, headers=filtered_headers) as response:
                            response.raise_for_status()
                            result = await response.text()
                    else:
                        async with session.put(url, data=processed_data, headers=request_headers) as response:
                            response.raise_for_status()
                            result = await response.text()
                else:
                    logger.error(f"不支持的异步HTTP方法: {method}")
                    return None
                
                logger.info(f"异步{method}请求成功: {url}, 状态码: {response.status}")
                
                # 响应后处理
                return response_processor(result) if response_processor else result
                    
        except asyncio.TimeoutError:
            logger.error(f"异步{method}请求超时: {url}")
            return None
    
    def _sync_generic_request(self, url: str, method: str, param: str, 
                            headers: Dict[str, str], data_processor: Callable,
                            response_processor: Callable) -> Optional[str]:
        """同步执行通用请求的内部方法"""
        try:
            # 合并请求头
            request_headers = headers or {}
            request_headers.update(self.global_headers)
            
            # 数据预处理
            processed_data = data_processor(param) if data_processor and param else param
            if param and not processed_data and data_processor:
                logger.error(f"{method}请求失败: {url}, 数据预处理失败")
                return None
            
            # 记录请求信息用于调试
            logger.info(f"发送{method}请求: {url}, 代理设置: {self.session.proxies}")
            
            # 检查是否是本地循环请求，如果是则增加超时时间
            is_local_request = any(host in url for host in ['127.0.0.1', 'localhost', '::1'])
            timeout = 45 if is_local_request else 30
            
            # 根据方法选择合适的请求方式
            if method.upper() == "GET":
                response = self.session.get(
                    url, 
                    headers=request_headers, 
                    timeout=timeout,
                    proxies={}  # 强制在请求级别也不使用代理
                )
            elif method.upper() == "POST":
                response = self.session.post(
                    url, 
                    data=processed_data, 
                    headers=request_headers, 
                    timeout=timeout,
                    proxies={}  # 强制在请求级别也不使用代理
                )
            elif method.upper() == "PUT":
                response = self.session.put(
                    url, 
                    data=processed_data, 
                    headers=request_headers, 
                    timeout=timeout,
                    proxies={}  # 强制在请求级别也不使用代理
                )
            else:
                logger.error(f"不支持的HTTP方法: {method}")
                return None
            
            response.raise_for_status()
            
            logger.info(f"{method}请求成功: {url}, 状态码: {response.status_code}")
            
            # 响应后处理
            return response_processor(response.text) if response_processor else response.text
            
        except requests.exceptions.Timeout:
            logger.error(f"{method}请求超时: {url}")
            return None
        except requests.exceptions.ConnectionError as e:
            logger.error(f"{method}请求连接失败: {url}, 错误: {str(e)}")
            return None
        except Exception as e:
            logger.error(f"{method}请求失败: {url}, 错误: {str(e)}")
            return None
    
    async def request_post_xml(self, url: str, param: str) -> Optional[str]:
        """
        发送POST请求，参数为XML格式
        在本方法中会自动将传入的JSON格式字符串转化为XML格式
        
        Args:
            url: 请求的URL
            param: JSON字符串，请求的参数
            
        Returns:
            若请求成功：将响应的XML格式字符串转化为JSON格式并返回
            若请求失败：None
        """
        def json_to_xml_processor(param_str: str) -> str:
            """将JSON参数转换为XML数据"""
            param_dict = safe_parse_dict(param_str)
            if param_dict is None:
                return None
            return xmltodict.unparse({"root": param_dict})
        
        def xml_to_json_processor(response_text: str) -> str:
            """将XML响应转换为JSON格式"""
            xml_dict = xmltodict.parse(response_text)
            return json.dumps(xml_dict, ensure_ascii=False)
        
        headers = {"Content-Type": "application/xml"}
        return await self._generic_request(
            url=url,
            method="POST", 
            param=param,
            headers=headers,
            data_processor=json_to_xml_processor,
            response_processor=xml_to_json_processor
        )
    
    async def request_put_json(self, url: str, param: str) -> Optional[str]:
        """
        发送PUT请求，参数为JSON格式
        
        Args:
            url: 请求的URL
            param: JSON字符串，请求的参数
            
        Returns:
            若请求成功：返回响应的JSON字符串
            若请求失败：None
        """
        def json_to_dict_processor(param_str: str) -> Optional[dict]:
            """将JSON字符串转换为字典对象，用于异步请求"""
            return safe_parse_dict(param_str)
        
        headers = {"Content-Type": "application/json"}
        return await self._generic_request(
            url=url,
            method="PUT", 
            param=param,
            headers=headers,
            data_processor=json_to_dict_processor
        )
    
    async def request_get(self, url: str) -> Optional[str]:
        """
        发送GET请求
        
        Args:
            url: 请求的URL
            
        Returns:
            若请求成功：请求返回的字符串
            若请求失败：None
        """
        # 检查是否是本地请求，如果是则使用异步方式避免死锁
        is_local_request = any(host in url for host in ['127.0.0.1', 'localhost', '::1'])
        
        if is_local_request:
            try:
                # 检查是否在事件循环中
                asyncio.get_running_loop()
                return await self._async_request_get(url)
            except RuntimeError:
                # 不在事件循环中，使用同步方式
                pass
        
        return self._execute_safely(self._sync_request_get, url)
    
    async def _async_request_get(self, url: str) -> Optional[str]:
        """异步执行GET请求的内部方法，专门处理本地循环请求"""
        try:
            headers = self.global_headers.copy()
            
            # 记录请求信息用于调试
            logger.info(f"发送异步GET请求: {url}, 脚本: {self.script_id}")
            
            # 使用aiohttp进行异步请求，避免阻塞事件循环
            timeout = aiohttp.ClientTimeout(total=60)  # 60秒超时
            async with aiohttp.ClientSession(timeout=timeout) as session:
                async with session.get(url, headers=headers) as response:
                    response.raise_for_status()
                    result = await response.text()
                    logger.info(f"异步GET请求成功: {url}, 状态码: {response.status}")
                    return result
                    
        except asyncio.TimeoutError:
            logger.error(f"异步GET请求超时: {url}")
            return None
        except aiohttp.ClientError as e:
            logger.error(f"异步GET请求客户端错误: {url}, 错误: {str(e)}")
            return None
        except Exception as e:
            logger.error(f"异步GET请求失败: {url}, 错误: {str(e)}")
            return None
    
    def _sync_request_get(self, url: str) -> Optional[str]:
        """同步执行GET请求的内部方法"""
        try:
            headers = self.global_headers.copy()
            
            response = self.session.get(url, headers=headers, timeout=30)
            response.raise_for_status()
            
            return response.text
            
        except Exception as e:
            logger.error(f"GET请求失败: {url}, 错误: {str(e)}")
            return None
    
    def set_header(self, key: str, value: str) -> None:
        """
        设置请求头，设置一次之后，对所有请求都有效
        
        Args:
            key: 请求头的key
            value: 请求头的value
        """
        self.global_headers[key] = value
    
    async def request_http_post(self, url: str, param: str, head_param: str, media_type: str) -> Optional[str]:
        """
        发送POST请求（增强版）
        可设置请求媒体类型(MediaType)，可设置请求头
        
        Args:
            url: 请求的URL
            param: 请求的入参json字符串
            head_param: 请求头，如有传json字符串，无传空字符串
            media_type: 请求类型，可选择：JSON、JAVASCRIPT、HTML、XML、XWWWFORMURLENCODED
            
        Returns:
            成功返回json字符串，失败返回None
        """
        headers = self._get_media_type_header(media_type)
        
        # 解析额外请求头
        if head_param:
            extra_headers = safe_parse_dict(head_param)
            if extra_headers:
                headers.update(extra_headers)
        
        # 如果是JSON类型，使用字典处理器
        data_processor = None
        if media_type.upper() == "JSON":
            def json_to_dict_processor(param_str: str) -> Optional[dict]:
                return safe_parse_dict(param_str)
            data_processor = json_to_dict_processor
        
        return await self._generic_request(
            url=url,
            method="POST", 
            param=param,
            headers=headers,
            data_processor=data_processor
        )
    
    async def request_http_get(self, url: str, head_param: str) -> Optional[str]:
        """
        发送GET请求（增强版）
        可设置请求头
        
        Args:
            url: 请求的URL
            head_param: 请求头，如有传json字符串，无传空字符串
            
        Returns:
            成功返回json字符串，失败返回None
        """
        headers = {}
        
        # 解析额外请求头
        if head_param:
            extra_headers = safe_parse_dict(head_param)
            if extra_headers:
                headers.update(extra_headers)
        
        return await self._generic_request(
            url=url,
            method="GET",
            headers=headers
        )
    
    async def request_http_put(self, url: str, head_param: str, media_type: str, param: str) -> Optional[str]:
        """
        发送PUT请求（增强版）
        可设置请求媒体类型(MediaType)，可设置请求头
        
        Args:
            url: 请求的URL
            head_param: 请求头，如有传json字符串，无传空字符串
            media_type: 请求类型，可选择：JSON、JAVASCRIPT、HTML、XML、XWWWFORMURLENCODED
            param: 请求的入参json字符串
            
        Returns:
            成功返回json字符串，失败返回None
        """
        headers = self._get_media_type_header(media_type)
        
        # 解析额外请求头
        if head_param:
            extra_headers = safe_parse_dict(head_param)
            if extra_headers:
                headers.update(extra_headers)
        
        # 如果是JSON类型，使用字典处理器
        data_processor = None
        if media_type.upper() == "JSON":
            def json_to_dict_processor(param_str: str) -> Optional[dict]:
                return safe_parse_dict(param_str)
            data_processor = json_to_dict_processor
        
        return await self._generic_request(
            url=url,
            method="PUT", 
            param=param,
            headers=headers,
            data_processor=data_processor
        )
    
    def _get_media_type_header(self, media_type: str) -> Dict[str, str]:
        """
        根据媒体类型获取对应的Content-Type请求头
        
        Args:
            media_type: 媒体类型
            
        Returns:
            包含Content-Type的字典
        """
        media_type_mapping = {
            "JSON": "application/json",
            "JAVASCRIPT": "application/javascript", 
            "HTML": "text/html",
            "XML": "application/xml",
            "XWWWFORMURLENCODED": "application/x-www-form-urlencoded"
        }
        
        content_type = media_type_mapping.get(media_type.upper(), "application/json")
        return {"Content-Type": content_type}