from configparser import ConfigParser
import requests
import socket
import time
import threading
import gc
import os
from datetime import datetime, timedelta
from random import randint
from urllib.parse import urlparse


# 全局变量
total_traffic = 0  # 总流量（字节）
start_time = datetime.now()  # 开始时间
last_traffic = 0  # 上一次记录的流量
last_time = time.time()  # 上一次记录的时间
lock = threading.Lock()  # 线程锁，用于保护共享变量
running = True  # 控制线程运行的标志
current_ip_type = ""  # 当前IP类型
config_url = ""  # 视频URL，从配置文件加载
config_thread = 0  # 线程数，从配置文件加载
target_ipv6 = None  # 目标资源的IPv6地址


def load_config(config_path="Config.ini"):
    """加载配置文件"""
    if not os.path.exists(config_path):
        raise FileNotFoundError(f"配置文件不存在：{config_path}")

    config = ConfigParser()
    config.read(config_path, encoding="utf-8")
    return config


def get_ip_type():
    """检测当前网络环境是IPv4还是IPv6"""
    try:
        # 尝试获取外部IPv6地址
        s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
        s.connect(("2001:4860:4860::8888", 80))
        ipv6_addr = s.getsockname()[0]
        s.close()

        # 判断是否以2409开头
        if ipv6_addr.startswith("2409"):
            return "IPv6", ipv6_addr
        else:
            return "非2409开头的IPv6", ipv6_addr
    except Exception as e:
        try:
            # 尝试创建IPv4套接字连接
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect(("8.8.8.8", 80))
            ipv4_addr = s.getsockname()[0]
            s.close()
            return "IPv4", ipv4_addr
        except Exception:
            return "未知", "无法获取IP地址"


def get_target_ipv6(url):
    """解析URL并获取目标服务器的IPv6地址"""
    try:
        # 解析URL获取主机名
        parsed_url = urlparse(url)
        hostname = parsed_url.hostname
        
        if not hostname:
            return "无法解析主机名"
            
        # 查询IPv6地址
        # 使用getaddrinfo查询，只获取IPv6地址
        addr_info = socket.getaddrinfo(
            hostname, 
            None, 
            socket.AF_INET6,  # 只查询IPv6
            socket.SOCK_STREAM
        )
        
        # 提取IPv6地址（去重）
        ipv6_addresses = list({info[4][0] for info in addr_info})
        
        if ipv6_addresses:
            # 返回第一个IPv6地址，如果有多个则用逗号分隔
            return ", ".join(ipv6_addresses)
        else:
            return "目标资源没有IPv6地址"
            
    except Exception as e:
        return f"获取IPv6地址失败: {str(e)}"


# 创建一个IPv6专用的会话
def create_ipv6_session():
    """创建一个只使用IPv6的会话，带有合理的超时设置和请求头"""
    session = requests.Session()
    
    # 设置超时时间
    session.timeout = (10, 30)  # 连接超时10秒，读取超时30秒
    
    # 设置请求头，模拟浏览器
    session.headers.update({
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
        'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
        'Connection': 'keep-alive',
        'Upgrade-Insecure-Requests': '1'
    })
    
    # 设置连接适配器，强制使用IPv6
    for prefix in ['http://', 'https://']:
        session.mount(prefix, IPv6HttpAdapter())
    
    return session


# 自定义适配器，强制使用IPv6
class IPv6HttpAdapter(requests.adapters.HTTPAdapter):
    def init_poolmanager(self, *args, **kwargs):
        kwargs['socket_options'] = [
            (socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
        ]
        return super(IPv6HttpAdapter, self).init_poolmanager(*args, **kwargs)


def format_size(size_bytes):
    """将字节大小转换为可读的格式"""
    if size_bytes < 1024:
        return f"{size_bytes} B"
    elif size_bytes < 1024 * 1024:
        return f"{size_bytes / 1024:.2f} KB"
    elif size_bytes < 1024 * 1024 * 1024:
        return f"{size_bytes / (1024 * 1024):.2f} MB"
    else:
        return f"{size_bytes / (1024 * 1024 * 1024):.2f} GB"


def calculate_speed():
    """计算当前网速"""
    global last_traffic, last_time
    current_time = time.time()
    with lock:
        current_traffic = total_traffic

    time_diff = current_time - last_time
    traffic_diff = current_traffic - last_traffic

    if time_diff > 0:
        speed = traffic_diff / time_diff
    else:
        speed = 0

    last_traffic = current_traffic
    last_time = current_time

    return speed


def request_video(thread_id):
    """请求视频并更新流量统计，带有重试机制"""
    global total_traffic, running
    
    max_retries = 5  # 最大重试次数
    retry_delay = 5  # 重试延迟（秒）
    
    while running:
        try:
            # 创建新的会话以避免长时间连接问题
            session = create_ipv6_session()
            
            # 随机延迟开始，避免所有线程同时请求
            time.sleep(randint(1, 3))
            
            print(f"线程 {thread_id} 开始下载...")
            
            # 使用流式请求，避免一次性加载整个视频到内存
            with session.get(config_url, stream=True) as response:
                response.raise_for_status()
                chunk_size = 8192  # 8KB
                
                # 记录上次接收数据的时间
                last_receive_time = time.time()
                
                for chunk in response.iter_content(chunk_size=chunk_size):
                    if not running:
                        break
                        
                    # 检查是否超时（30秒内没有收到数据）
                    if time.time() - last_receive_time > 30:
                        raise TimeoutError("超过30秒未收到数据")
                        
                    # 更新总流量
                    chunk_length = len(chunk)
                    if chunk_length > 0:
                        with lock:
                            total_traffic += chunk_length
                        last_receive_time = time.time()
            
            print(f"线程 {thread_id} 完成一次下载循环")
            
            # 每次下载完成后短暂休息，避免过度请求
            time.sleep(2)
            
        except Exception as e:
            print(f"线程 {thread_id} 请求出错: {e}")
            max_retries -= 1
            if max_retries <= 0 or not running:
                print(f"线程 {thread_id} 达到最大重试次数，退出")
                break
                
            print(f"线程 {thread_id} 将在 {retry_delay} 秒后重试...")
            time.sleep(retry_delay)
            retry_delay *= 2  # 指数退避
        finally:
            # 清理内存
            gc.collect()


def status_reporter():
    """定期报告状态（每10秒一次）"""
    # 每10秒报告一次
    next_report_time = datetime.now() + timedelta(seconds=10)
    last_total = 0  # 用于计算间隔内的流量

    while running:
        now = datetime.now()
        if now >= next_report_time:
            # 获取当前网络环境
            ip_type, ip_addr = get_ip_type()
            # 计算运行时长
            runtime = now - start_time
            hours, remainder = divmod(runtime.total_seconds(), 3600)
            minutes, seconds = divmod(remainder, 60)
            runtime_str = f"{int(hours)}小时{int(minutes)}分钟{int(seconds)}秒"
            # 计算当前速度
            current_speed = calculate_speed()
            # 计算过去10秒的平均速度
            with lock:
                current_total = total_traffic
            interval_speed = (current_total - last_total) / 10  # 除以10秒
            last_total = current_total

            # 输出状态信息
            print("\n" + "=" * 70)
            print(f"状态报告 - {now.strftime('%Y-%m-%d %H:%M:%S')}")
            print(f"当前网络环境: {ip_type}")
            print(f"当前IP地址: {ip_addr}")
            print(f"目标资源URL: {config_url}")
            print(f"目标资源IPv6地址: {target_ipv6}")  # 显示目标IPv6地址
            print(f"已消耗流量: {format_size(total_traffic)}")
            print(f"当前速度: {format_size(current_speed)}/秒")
            print(f"平均速度(10秒): {format_size(interval_speed)}/秒")
            print(f"运行时长: {runtime_str}")
            print("=" * 70 + "\n")

            # 设置下一次报告时间（10秒后）
            next_report_time = now + timedelta(seconds=10)

        # 每秒更新一次速度计算
        calculate_speed()
        time.sleep(1)


def main():
    global running, current_ip_type, config_url, config_thread, target_ipv6

    print("正在检测网络环境...")
    current_ip_type, ip_addr = get_ip_type()
    print(f"当前网络环境: {current_ip_type}")
    print(f"当前IP地址: {ip_addr}")

    if not current_ip_type.startswith("IPv6") or not ip_addr.startswith("2409"):
        print("当前不是以2409开头的IPv6网络环境，程序将退出。")
        return

    try:
        config = load_config()
        config_url = config.get("URL", "resource_url")
        config_thread = int(config.get("THREAD", "thread"))
        
        # 获取目标资源的IPv6地址
        print("正在解析目标资源的IPv6地址...")
        target_ipv6 = get_target_ipv6(config_url)
        print(f"目标资源IPv6地址: {target_ipv6}")
        
        print("符合要求的IPv6网络环境已确认，开始运行...")
        print(f"目标视频URL: {config_url}")
        print(f"线程数量: {config_thread}")
        print("程序已启动，每10秒将输出一次状态报告")
        print("注意：程序将只使用IPv6连接，即使在双栈环境下也不会使用IPv4")

        # 启动状态报告线程
        reporter_thread = threading.Thread(target=status_reporter)
        reporter_thread.daemon = True
        reporter_thread.start()

        # 创建多个工作线程
        threads = []
        for i in range(config_thread):
            thread = threading.Thread(target=request_video, args=(i,))
            thread.daemon = True
            threads.append(thread)
            thread.start()
            # 错开线程启动时间，避免同时请求
            time.sleep(1)

        # 主线程等待用户中断
        while True:
            time.sleep(1)

    except KeyboardInterrupt:
        print("\n检测到用户中断，正在停止...")
        running = False
        # 等待所有线程结束
        for thread in threads:
            thread.join(timeout=5)
        print("程序已停止")
    except Exception as e:
        print(f"程序出错: {e}")
        running = False


if __name__ == "__main__":
    main()
