from loguru import logger

__all__ = [
    "async_fetch",
    "async_httpx",
    "curl_cffi",
    "download",
    "fetch",
    "httpx",
    "http_primp",
    "requests",
    "requests_go",
]


async def _async_httpx(
    url: str,
    method: str = "GET",
    *,
    params: dict | None = None,
    content=None,
    data=None,
    json=None,
    headers=None,
    cookies=None,
    files=None,
    auth=None,
    proxy=None,
    proxies=None,
    mounts=None,
    timeout=None,
    follow_redirects: bool = True,
    verify: bool = True,
    cert=None,
    trust_env: bool = True,
    http1=True,
    http2=False,
    default_encoding="utf-8",
):
    """
    ## 发送异步请求（基于 httpx.AsyncClient）

    - url: 请求地址
    - method: 请求方法, 默认为 GET, 支持 GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD, TRACE, CONNECT
    - params: 查询参数
    - content: 请求内容
    - data: 请求内容
    - json: 请求内容
    - headers: 请求头
    - cookies: 请求 Cookie
    - files: 文件
    - auth: 认证
    - proxy: 代理
    - proxies: 代理
    - mounts: 挂载
    - timeout: 超时
    - follow_redirects: 是否跟随重定向
    - verify: 是否验证 SSL 证书
    - cert: 证书
    - trust_env: 是否信任环境变量
    - http1: 是否使用 HTTP/1.1
    - http2: 是否使用 HTTP/2
    - default_encoding: 默认编码
    """
    import httpx

    if not proxy and proxies:
        if isinstance(proxies, dict):
            proxy = (
                proxies.get("http")
                or proxies.get("https")
                or proxies.get("all")
                or proxies.get("http://")
                or proxies.get("https://")
            )
        elif isinstance(proxies, str):
            proxy = proxies
    async with httpx.AsyncClient(
        verify=verify,
        cert=cert,
        http1=http1,
        http2=http2,
        proxy=proxy,
        mounts=mounts,
        trust_env=trust_env,
        default_encoding=default_encoding,
    ) as client:
        return await client.request(
            method=method,
            url=url,
            content=content,
            data=data,
            files=files,
            json=json,
            params=params,
            headers=headers,
            cookies=cookies,
            auth=auth,
            follow_redirects=follow_redirects,
            timeout=timeout,
        )


async def async_httpx(
    url: str,
    method: str = "GET",
    *,
    params: dict | None = None,
    content=None,
    data=None,
    json=None,
    headers=None,
    cookies=None,
    files=None,
    auth=None,
    proxy=None,
    proxies=None,
    mounts=None,
    timeout=None,
    follow_redirects: bool = True,
    verify: bool = True,
    cert=None,
    trust_env: bool = True,
    http1=True,
    http2=False,
    default_encoding="utf-8",
    retry: int = 1,
):
    """
    ## 发送异步请求（基于 httpx.AsyncClient）

    - url: 请求地址
    - method: 请求方法, 默认为 GET, 支持 GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD, TRACE, CONNECT
    - params: 查询参数
    - content: 请求内容
    - data: 请求内容
    - json: 请求内容
    - headers: 请求头
    - cookies: 请求 Cookie
    - files: 文件
    - auth: 认证
    - proxy: 代理
    - proxies: 代理
    - mounts: 挂载
    - timeout: 超时
    - follow_redirects: 是否跟随重定向
    - verify: 是否验证 SSL 证书
    - cert: 证书
    - trust_env: 是否信任环境变量
    - http1: 是否使用 HTTP/1.1
    - http2: 是否使用 HTTP/2
    - default_encoding: 默认编码
    - retry: 重试次数
    """
    for _ in range(retry):
        try:
            response = await _async_httpx(
                url=url,
                method=method,
                params=params,
                content=content,
                data=data,
                json=json,
                headers=headers,
                cookies=cookies,
                files=files,
                auth=auth,
                proxy=proxy,
                proxies=proxies,
                mounts=mounts,
                timeout=timeout,
                follow_redirects=follow_redirects,
                verify=verify,
                cert=cert,
                trust_env=trust_env,
                http1=http1,
                http2=http2,
                default_encoding=default_encoding,
            )
            return response
        except Exception as e:
            logger.error(f"{url} 请求失败: {e!r}")


async_fetch = async_httpx


def httpx(
    url: str,
    method: str = "GET",
    *,
    params: dict | None = None,
    content=None,
    data=None,
    json=None,
    headers=None,
    cookies=None,
    files=None,
    auth=None,
    proxy=None,
    proxies=None,
    mounts=None,
    timeout=None,
    follow_redirects: bool = True,
    verify: bool = True,
    cert=None,
    trust_env: bool = True,
    http1=True,
    http2=False,
    default_encoding="utf-8",
    retry: int = 1,
):
    """
    - 发送同步请求（基于 httpx.AsyncClient）

    - url: 请求地址
    - method: 请求方法, 默认为 GET, 支持 GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD, TRACE, CONNECT
    - params: 查询参数
    - content: 请求内容
    - data: 请求内容
    - json: 请求内容
    - headers: 请求头
    - cookies: 请求 Cookie
    - files: 文件
    - auth: 认证
    - proxy: 代理
    - proxies: 代理
    - mounts: 挂载
    - timeout: 超时
    - follow_redirects: 是否跟随重定向
    - verify: 是否验证 SSL 证书
    - cert: 证书
    - trust_env: 是否信任环境变量
    - http1: 是否使用 HTTP/1.1
    - http2: 是否使用 HTTP/2
    - default_encoding: 默认编码
    - retry: 重试次数
    """
    import asyncio

    response = asyncio.run(
        async_httpx(
            url,
            method,
            params=params,
            content=content,
            data=data,
            json=json,
            headers=headers,
            cookies=cookies,
            files=files,
            auth=auth,
            proxy=proxy,
            proxies=proxies,
            mounts=mounts,
            timeout=timeout,
            follow_redirects=follow_redirects,
            verify=verify,
            cert=cert,
            trust_env=trust_env,
            http1=http1,
            http2=http2,
            default_encoding=default_encoding,
            retry=retry,
        )
    )
    return response


fetch = httpx


def curl_cffi(
    url: str,
    method="GET",
    *,
    params: dict | list | tuple | None = None,
    data: dict[str, str] | list[tuple] | str | bytes | None = None,
    json: dict | None = None,
    headers: dict | None = None,
    cookies: dict | None = None,
    timeout: float = 30,
    allow_redirects: bool = True,
    max_redirects: int = 30,
    proxies: None = None,
    proxy: None = None,
    verify: bool | None = None,
    impersonate: str | None = "chrome",
    retry: int = 1,
    **kwargs,
):
    """
    基于 curl_cffi 封装, 可以过 ja3 和 cloudflare 等验证

    - url: 请求地址
    - method: 请求方法, 默认为 GET, 支持 GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD, TRACE, CONNECT
    - params: 查询参数
    - data: 请求内容
    - json: 请求内容
    - headers: 请求头
    - cookies: 请求 Cookie
    - files: 文件
    - auth: 认证
    - timeout: 超时
    - allow_redirects: 是否跟随重定向
    - max_redirects: 最大重定向次数
    - proxies: 代理
    - proxy: 代理
    - proxy_auth: 代理认证
    - verify: 是否验证 SSL 证书
    - referer: 来源
    - accept_encoding: 接受编码
    - content_callback: 内容回调
    - impersonate: 模拟浏览器, 'edge99', 'edge101', 'chrome99', 'chrome100', 'chrome101', 'chrome104', 'chrome107', 'chrome110', 'chrome116', 'chrome119', 'chrome120', 'chrome123', 'chrome124', 'chrome131', 'chrome133a', 'chrome136', 'chrome142', 'chrome99_android', 'chrome131_android', 'safari153', 'safari155', 'safari170', 'safari172_ios', 'safari180', 'safari180_ios', 'safari184', 'safari184_ios', 'safari260', 'safari2601', 'safari260_ios', 'firefox133', 'firefox135', 'firefox144', 'tor145', 'chrome', 'edge', 'safari', 'safari_ios', 'safari_beta', 'safari_ios_beta', 'chrome_android', 'firefox', 'safari15_3', 'safari15_5', 'safari17_0', 'safari17_2_ios', 'safari18_0', 'safari18_0_ios', 'safari18_4', 'safari18_4_ios'
    - ja3: JA3 指纹
    - akamai: Akamai 指纹
    - extra_fp: 额外的指纹
    - thread: 多线程引擎, 支持 eventlet, gevent
    - default_headers: 默认头
    - default_encoding: 默认编码
    - curl_options: curl 选项
    - http_version: http 版本, 默认为 HTTP/2
    - debug: 是否调试
    - interface: 接口
    - cert: 证书
    - stream: 是否流式
    - max_recv_speed: 最大接收速度
    - multipart: 多部分
    - retry: 重试次数
    """
    from curl_cffi import requests

    if proxies:
        if isinstance(proxies, str):
            proxies = {"all": proxies}
        elif isinstance(proxies, dict):
            proxies = {
                "all": (
                    proxies.get("http")
                    or proxies.get("https")
                    or proxies.get("all")
                    or proxies.get("http://")
                    or proxies.get("https://")
                )
            }

    except_list = []
    for _ in range(retry):
        try:
            response = requests.request(
                method,
                url=url,
                params=params,
                data=data,
                json=json,
                headers=headers,
                cookies=cookies,
                timeout=timeout,
                allow_redirects=allow_redirects,
                max_redirects=max_redirects,
                proxies=proxies,
                proxy=proxy,
                verify=verify,
                impersonate=impersonate,
                **kwargs,
            )
            return response
        except Exception as e:
            except_list.append(e)
            logger.error(f"{url} 请求失败: {e!r}")
    raise except_list[-1]


def requests(
    url: str,
    method: str = "GET",
    *,
    params: dict | None = None,
    data: dict | str | None = None,
    headers: dict | None = None,
    cookies: dict | None = None,
    files: dict | None = None,
    auth: None = None,
    timeout: int | float | None = None,
    allow_redirects: bool | None = True,
    proxies: dict | None = None,
    hooks: None = None,
    stream: bool | None = False,
    verify: bool | str | None = True,
    cert: None = None,
    json: dict | None = None,
    retry: int = 1,
):
    """
    发送同步请求(基于 requests)

    - method: 请求方法, 默认为 GET, 支持 GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD, TRACE, CONNECT
    - url: 请求地址
    - params: 查询参数
    - data: 请求内容
    - headers: 请求头
    - cookies: 请求 Cookie
    - files: 文件
    - auth: 认证
    - timeout: 超时
    - allow_redirects: 是否跟随重定向
    - proxies: 代理
    - hooks: 钩子
    - stream: 是否流式
    - verify: 是否验证 SSL 证书
    - cert: 证书
    - json: 请求内容
    - retry: 重试次数
    """
    import requests

    if proxies:
        if isinstance(proxies, dict):
            proxies = {
                "all": (
                    proxies.get("http")
                    or proxies.get("https")
                    or proxies.get("all")
                    or proxies.get("http://")
                    or proxies.get("https://")
                )
            }
        elif isinstance(proxies, str):
            proxies = {"all": proxies}

    except_list = []
    for _ in range(retry):
        try:
            response = requests.request(
                method=method,
                url=url,
                params=params,
                data=data,
                headers=headers,
                cookies=cookies,
                files=files,
                auth=auth,
                timeout=timeout,
                allow_redirects=allow_redirects,
                proxies=proxies,
                hooks=hooks,
                stream=stream,
                verify=verify,
                cert=cert,
                json=json,
            )
            return response
        except Exception as e:
            except_list.append(e)
            logger.error(f"{url} 请求失败: {e!r}")
    raise except_list[-1]


def download(
    url: str,
    file_name: str | None = None,
    *,
    retry: int = 1,
    proxies: dict | None = None,
):
    """
    基于 requests 下载文件，支持断点续传和 rich 进度条

    - url: 文件地址
    - file_name: 文件名, 默认为 md5(url)
    - retry: 重试次数
    - proxies: 代理
    """
    import os
    import requests
    import rich.progress
    from ._tools import md5

    # 处理文件名
    if not file_name:
        file_name = md5(url)
    elif os.path.isdir(file_name):
        file_name = os.path.join(file_name, md5(url))

    # 处理代理配置
    if proxies:
        if isinstance(proxies, dict):
            proxies = {
                "all": (
                    proxies.get("http")
                    or proxies.get("https")
                    or proxies.get("all")
                    or proxies.get("http://")
                    or proxies.get("https://")
                )
            }
        elif isinstance(proxies, str):
            proxies = {"all": proxies}

    except_list = []
    for attempt in range(retry + 1):
        try:
            file_exists = os.path.isfile(file_name)
            resume_byte_pos = os.path.getsize(file_name) if file_exists else 0
            headers = (
                {"Range": f"bytes={resume_byte_pos}-"} if resume_byte_pos > 0 else {}
            )
            response = requests.get(url, stream=True, proxies=proxies, headers=headers)
            if response.status_code == 206:  # 支持断点续传
                content_length = int(response.headers.get("Content-Length", 0))
                total_size = resume_byte_pos + content_length
            elif response.status_code == 200:  # 不支持断点续传
                if resume_byte_pos > 0:
                    os.remove(file_name)
                total_size = int(response.headers.get("Content-Length", 0))
                resume_byte_pos = 0
            else:
                response.raise_for_status()
            mode = "ab" if resume_byte_pos > 0 else "wb"
            with open(file_name, mode) as f:
                with rich.progress.Progress(
                    rich.progress.SpinnerColumn(),
                    rich.progress.TextColumn(
                        "[progress.description]{task.description}"
                    ),
                    rich.progress.BarColumn(),
                    rich.progress.TextColumn(
                        "[progress.percentage]{task.percentage:>3.0f}%"
                    ),
                    rich.progress.DownloadColumn(),
                    rich.progress.TransferSpeedColumn(),
                ) as progress:
                    task = progress.add_task(
                        f"[green]{file_name}下载中...",
                        total=total_size,
                        completed=resume_byte_pos,
                    )
                    for data in response.iter_content(chunk_size=8192):
                        if not data:
                            break
                        f.write(data)
                        progress.update(task, advance=len(data))

            return file_name

        except Exception as e:
            except_list.append(e)
            if attempt < retry:
                if os.path.exists(file_name):
                    os.remove(file_name)
                continue
            raise Exception(
                f"下载失败，已尝试 {retry + 1} 次\n最后一次错误: {e!r}"
            ) from e


def http_primp(
    url: str,
    method: str = "GET",
    headers: dict[str, str] | None = None,
    cookies: dict[str, str] | None = None,
    params: dict[str, str] | None = None,
    proxies: dict[str, str] | None = None,
    proxy: str | None = None,
    impersonate: str | None = None,
    impersonate_os: str | None = None,
    verify: bool = True,
    ca_cert_file: str | None = None,
    auth: tuple[str, str | None] | None = None,
    auth_bearer: str | None = None,
    cookie_store: bool | None = True,
    referer: bool | None = True,
    timeout: float | None = 30,
    follow_redirects: bool | None = True,
    max_redirects: int | None = 20,
    https_only: bool | None = False,
    http2_only: bool | None = False,
    **kwargs,
) -> str:
    """
    请求库, 基于 `primp` 库, 支持模拟浏览器, 模拟操作系统, 验证 SSL 证书, 自定义 CA 证书文件等功能

    - url: 请求地址
    - method: 请求方法, 默认为 GET, 支持 GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD, TRACE, CONNECT
    - headers: 请求头, 默认为 None
    - cookies: 请求 Cookies, 默认为 None
    - params: 查询参数, 默认为 None
    - proxies: 代理, 默认为 None
    - proxy: 代理, 默认为 None
    - impersonate: 模拟浏览器, 默认为 None, 支持 "random", "chrome_100", "chrome_101", "chrome_104",\
        "chrome_105", "chrome_106", "chrome_107", "chrome_108", "chrome_109", "chrome_114",\
        "chrome_116", "chrome_117", "chrome_118", "chrome_119", "chrome_120", "chrome_123",\
        "chrome_124", "chrome_126", "chrome_127", "chrome_128", "chrome_129", "chrome_130", "chrome_131", "chrome_133",\
        "safari_15.3", "safari_15.5", "safari_15.6.1", "safari_16", "safari_16.5", "safari_17.0",\
        "safari_17.2.1", "safari_17.4.1", "safari_17.5", "safari_18",  "safari_18.2",\
        "safari_ios_16.5", "safari_ios_17.2", "safari_ios_17.4.1", "safari_ios_18.1.1", "safari_ipad_18", \
        "okhttp_3.9", "okhttp_3.11", "okhttp_3.13", "okhttp_3.14", "okhttp_4.9", "okhttp_4.10", "okhttp_5", \
        "edge_101", "edge_122", "edge_127", "edge_131", "firefox_109", "firefox_117", "firefox_128", \
        "firefox_133", "firefox_135"
    - impersonate_os: 模拟操作系统, 默认为 None, 支持 "android", "ios", "linux", "macos", "windows"
    - verify: 是否验证 SSL 证书, 默认为 True
    - ca_cert_file: CA 证书文件路径, 默认为 None
    - auth: 认证, 默认为 None, 格式为 (username, password)
    - auth_bearer: Bearer 认证, 默认为 None
    - cookie_store: 是否启用 Cookie 存储, 默认为 True
    - referer: 是否添加 Referer 头, 默认为 True
    - timeout: 超时时间, 默认为 30 秒
    - follow_redirects: 是否遵循重定向, 默认为 True
    - max_redirects: 最大重定向次数, 默认为 20
    - https_only: 是否只允许 HTTPS 请求, 默认为 False
    - http2_only: 是否只允许 HTTP/2 请求, 默认为 False
    - **kwargs: 其他参数
    """
    import primp

    if not proxy and proxies:
        if isinstance(proxies, dict):
            proxy = (
                proxies.get("http")
                or proxies.get("https")
                or proxies.get("all")
                or proxies.get("http://")
                or proxies.get("https://")
            )
        elif isinstance(proxies, str):
            proxy = proxies

    return primp.Client(
        params=params,
        headers=headers,
        proxy=proxy,
        impersonate=impersonate,
        impersonate_os=impersonate_os,
        verify=verify,
        ca_cert_file=ca_cert_file,
        auth=auth,
        auth_bearer=auth_bearer,
        cookie_store=cookie_store,
        referer=referer,
        timeout=timeout,
        follow_redirects=follow_redirects,
        max_redirects=max_redirects,
        https_only=https_only,
        http2_only=http2_only,
    ).request(
        method,
        url,
        cookies=cookies,
        **kwargs,
    )


def requests_go(url: str, method: str = "GET", **kwargs) -> str:
    """
    请求库, 基于 `requests_go` 库, 支持模拟浏览器, 模拟操作系统, 验证 SSL 证书, 自定义 CA 证书文件等功能

    - url: 请求地址
    - method: 请求方法, 默认为 GET, 支持 GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD, TRACE, CONNECT
    - **kwargs: 其他参数, 参考 `requests_go.requests`
    """
    import requests_go

    return requests_go.request(method, url, **kwargs)
