"""Download tool: save files from URLs to the agent workspace."""

from __future__ import annotations

import json
import mimetypes
import re
import time
from pathlib import Path
from typing import Any
from urllib.parse import unquote, urlparse

import httpx
from loguru import logger

from madpod_agent.agent.tools.base import Tool

_MAX_FILE_SIZE = 500 * 1024 * 1024  # 500 MB hard cap
_DOWNLOAD_TIMEOUT = 120.0
_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_7_2) AppleWebKit/537.36"

_SAFE_FILENAME_RE = re.compile(r"[^\w\-.]")


def _sanitize_filename(name: str) -> str:
    """Strip unsafe characters, keep extension."""
    cleaned = _SAFE_FILENAME_RE.sub("_", name.strip())
    return cleaned[:200] if cleaned else f"download-{int(time.time())}"


def _filename_from_response(url: str, response: httpx.Response) -> str:
    """Derive a filename from Content-Disposition header, URL path, or content-type."""
    cd = response.headers.get("content-disposition", "")
    if cd:
        match = re.search(r'filename\*?=["\']?(?:UTF-8\'\')?([^"\';]+)', cd, re.I)
        if match:
            return _sanitize_filename(unquote(match.group(1)))

    path = urlparse(str(response.url)).path.rstrip("/")
    if path and "." in path.split("/")[-1]:
        return _sanitize_filename(path.split("/")[-1])

    content_type = response.headers.get("content-type", "").split(";")[0].strip()
    ext = mimetypes.guess_extension(content_type) or ""
    return f"download-{int(time.time())}{ext}"


class DownloadTool(Tool):
    """Download a file from a URL into the agent workspace."""

    def __init__(self, workspace: Path, proxy: str | None = None):
        self._workspace = workspace
        self._proxy = proxy

    @property
    def name(self) -> str:
        return "download"

    @property
    def description(self) -> str:
        return (
            "Download a file from a URL and save it to the workspace. "
            "Use this for saving PDFs, images, videos, spreadsheets, or any other file from the web. "
            "Returns the local file path for further processing with other tools."
        )

    @property
    def parameters(self) -> dict[str, Any]:
        return {
            "type": "object",
            "properties": {
                "url": {"type": "string", "description": "URL of the file to download"},
                "filename": {
                    "type": "string",
                    "description": "Optional filename to save as (auto-detected if omitted)",
                },
                "subdir": {
                    "type": "string",
                    "description": "Optional subdirectory within workspace (e.g. 'downloads')",
                },
            },
            "required": ["url"],
        }

    async def execute(self, url: str, filename: str | None = None, subdir: str | None = None, **kwargs: Any) -> str:
        parsed = urlparse(url)
        if parsed.scheme not in ("http", "https"):
            return json.dumps({"error": f"Only http/https URLs supported, got '{parsed.scheme}'"})

        target_dir = self._workspace / (subdir or "downloads")
        target_dir.mkdir(parents=True, exist_ok=True)

        try:
            async with httpx.AsyncClient(
                follow_redirects=True,
                timeout=_DOWNLOAD_TIMEOUT,
                proxy=self._proxy,
                headers={"User-Agent": _USER_AGENT},
            ) as client:
                async with client.stream("GET", url) as response:
                    response.raise_for_status()

                    resolved_name = _sanitize_filename(filename) if filename else _filename_from_response(url, response)
                    target_path = target_dir / resolved_name

                    content_length = int(response.headers.get("content-length", 0))
                    if content_length > _MAX_FILE_SIZE:
                        return json.dumps({
                            "error": f"File too large: {content_length:,} bytes (max {_MAX_FILE_SIZE:,})",
                        })

                    downloaded = 0
                    with open(target_path, "wb") as f:
                        async for chunk in response.aiter_bytes(chunk_size=65536):
                            downloaded += len(chunk)
                            if downloaded > _MAX_FILE_SIZE:
                                f.close()
                                target_path.unlink(missing_ok=True)
                                return json.dumps({"error": "Download exceeded size limit"})
                            f.write(chunk)

            content_type = response.headers.get("content-type", "").split(";")[0].strip()
            logger.info("Downloaded {} -> {} ({:,} bytes)", url, target_path, downloaded)

            return json.dumps({
                "ok": True,
                "path": str(target_path),
                "filename": resolved_name,
                "size_bytes": downloaded,
                "content_type": content_type,
                "url": str(response.url),
            })

        except httpx.HTTPStatusError as exc:
            return json.dumps({"error": f"HTTP {exc.response.status_code}: {url}"})
        except Exception as exc:
            logger.error("Download failed for {}: {}", url, exc)
            return json.dumps({"error": str(exc)})
