from __future__ import annotations

from datetime import datetime
from pathlib import Path
from typing import Optional, Tuple
from urllib.parse import urlparse

try:
    import requests
except ModuleNotFoundError:  # pragma: no cover - exercised only in minimal local env
    requests = None  # type: ignore[assignment]

from .exceptions import GrsAIError

_ALLOWED_EXTENSIONS = {".png", ".jpg", ".jpeg", ".webp", ".gif"}


def _sanitize_task_id(task_id: str) -> str:
    return "".join(c if c.isalnum() or c in ("-", "_") else "_" for c in task_id)


def infer_extension(url: str) -> str:
    path = urlparse(url).path
    ext = Path(path).suffix.lower()
    if ext in _ALLOWED_EXTENSIONS:
        return ext
    return ".png"


def build_output_filename(
    task_id: str, *, now: Optional[datetime] = None, extension: str = ".png"
) -> str:
    ts = (now or datetime.now()).strftime("%Y%m%d_%H%M%S")
    return f"{ts}_{_sanitize_task_id(task_id)}{extension}"


def download_image(
    url: str,
    *,
    output_dir: str,
    task_id: str,
    timeout: Tuple[float, float] = (5.0, 60.0),
    session: Optional[object] = None,
) -> str:
    if requests is None and session is None:
        raise GrsAIError("缺少依赖 requests，请先安装：pip install requests")
    if not url:
        raise GrsAIError("下载失败：图片 URL 为空")

    extension = infer_extension(url)
    directory = Path(output_dir)
    directory.mkdir(parents=True, exist_ok=True)

    file_path = directory / build_output_filename(task_id, extension=extension)

    own_session = session is None
    sess = session or requests.Session()  # type: ignore[union-attr]
    try:
        response = sess.get(url, stream=True, timeout=timeout)
        if response.status_code >= 400:
            text = (response.text or "")[:500]
            raise GrsAIError(f"下载失败: HTTP {response.status_code}: {text}")

        with open(file_path, "wb") as handle:
            for chunk in response.iter_content(chunk_size=8192):
                if chunk:
                    handle.write(chunk)
    except Exception as exc:
        if requests is not None and isinstance(exc, requests.RequestException):
            raise GrsAIError(f"下载失败: {exc}") from exc
        raise GrsAIError(f"下载失败: {exc}") from exc
    finally:
        if own_session:
            sess.close()

    return str(file_path.resolve())
