from __future__ import annotations
from pathlib import Path
from typing import Dict, Optional, Union
from urllib.parse import urlsplit, unquote

import requests


def 图片下载(
        url: str,
        保存路径: Optional[str] = None,
        保存目录: Optional[str] = None,
        文件名: Optional[str] = None,
        headers: Optional[Dict[str, str]] = None,
        cookies: Optional[Dict[str, str]] = None,
        timeout: Union[int, float] = 15,
        verify: bool = True,
        proxies: Optional[Dict[str, str]] = None,
        chunk_size: int = 1024 * 64,
        覆盖: bool = True,
) -> Optional[str]:
    """
    下载图片并保存到本地文件，返回保存后的文件路径。

    设计目标：
        - 适合批量采集：流式下载、不抛异常、失败返回 None
        - 可控：支持 headers/cookies/proxy/verify/timeout
        - 轻量：不引入额外第三方依赖

    保存规则：
        - 若提供 保存路径：直接使用该路径（优先级最高）
        - 否则使用 保存目录 + 文件名 组合生成路径
        - 文件名未提供时，将尝试从 URL 路径中提取；提取失败则使用 "image"

    Args:
        url (str): 图片 URL。
        保存路径 (Optional[str]): 完整文件路径，例如 "D:/imgs/a.png"。
        保存目录 (Optional[str]): 目录路径，例如 "D:/imgs"。
        文件名 (Optional[str]): 文件名，例如 "a.png"。
        headers (Optional[Dict[str, str]]): 请求头。
        cookies (Optional[Dict[str, str]]): Cookie。
        timeout (Union[int, float]): 超时时间（秒）。
        verify (bool): 是否验证 HTTPS 证书。
        proxies (Optional[Dict[str, str]]): 代理配置。
        chunk_size (int): 流式写入的块大小（字节），默认 64KB。
        覆盖 (bool): 若目标文件已存在，是否覆盖。

    Returns:
        Optional[str]:
            - 成功：返回保存后的文件路径（str）
            - 失败：返回 None
    """
    if not isinstance(url, str) or not url.strip():
        return None

    try:
        # 1) 计算最终保存路径
        if 保存路径 and isinstance(保存路径, str) and 保存路径.strip():
            最终路径 = Path(保存路径)
        else:
            目录 = Path(保存目录) if 保存目录 else Path.cwd()

            if 文件名 and isinstance(文件名, str) and 文件名.strip():
                名称 = 文件名.strip()
            else:
                # 从 URL 提取文件名
                try:
                    路径部分 = urlsplit(url).path
                    名称 = Path(unquote(路径部分)).name or "image"
                except Exception:
                    名称 = "image"

            最终路径 = 目录 / 名称

        # 确保目录存在
        最终路径.parent.mkdir(parents=True, exist_ok=True)

        # 已存在且不允许覆盖
        if 最终路径.exists() and not 覆盖:
            return str(最终路径)

        # 2) 发起请求（流式）
        with requests.get(
                url=url,
                headers=headers,
                cookies=cookies,
                timeout=timeout,
                verify=verify,
                proxies=proxies,
                stream=True,
                allow_redirects=True,
        ) as 响应:
            # 非 2xx 直接视为失败
            if not (200 <= getattr(响应, "status_code", 0) < 300):
                return None

            # 3) 写入文件
            with open(最终路径, "wb") as f:
                for 块 in 响应.iter_content(chunk_size=chunk_size):
                    if 块:  # 过滤 keep-alive 空块
                        f.write(块)

        # 4) 简单校验：空文件视为失败（可选但很实用）
        try:
            if 最终路径.stat().st_size <= 0:
                return None
        except Exception:
            pass

        return str(最终路径)
    except Exception:
        return None