from __future__ import annotations
import hashlib, json, re, urllib.request, urllib.parse
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

USER_AGENT = "PharmacistGuide-CRD-ResearchWorkbench/0.6 (+https://downloads.carry.pub)"


def utcnow() -> str:
    return datetime.now(timezone.utc).isoformat()


def fetch_bytes(url: str, timeout: int = 25, headers: dict[str,str] | None = None) -> tuple[bytes, dict[str,str]]:
    h = {"User-Agent": USER_AGENT, "Accept": "application/json, application/xml, text/xml, text/html;q=0.9, */*;q=0.8"}
    if headers: h.update(headers)
    req = urllib.request.Request(url, headers=h)
    with urllib.request.urlopen(req, timeout=timeout) as resp:
        body = resp.read()
        meta = {k.lower(): v for k,v in resp.headers.items()}
        meta["status"] = str(resp.status)
        meta["final_url"] = resp.geturl()
        return body, meta


def fetch_json(url: str, timeout: int = 25) -> tuple[Any, dict[str,str]]:
    body, meta = fetch_bytes(url, timeout=timeout, headers={"Accept":"application/json"})
    return json.loads(body.decode("utf-8", errors="replace")), meta


def sha256_bytes(data: bytes) -> str:
    return hashlib.sha256(data).hexdigest()


def cache_payload(cache_dir: Path, connector: str, query: str, url: str, body: bytes, meta: dict[str,str]) -> dict[str,Any]:
    cache_dir.mkdir(parents=True, exist_ok=True)
    digest = sha256_bytes(body)
    key = hashlib.sha256(f"{connector}|{query}|{url}".encode()).hexdigest()[:20]
    raw_path = cache_dir / f"{connector}_{key}.raw"
    raw_path.write_bytes(body)
    metadata = {
        "connector": connector,
        "query": query,
        "url": url,
        "retrievedAt": utcnow(),
        "sha256": digest,
        "bytes": len(body),
        "http": meta,
        "rawPath": str(raw_path),
    }
    (cache_dir / f"{connector}_{key}.json").write_text(json.dumps(metadata, indent=2), encoding="utf-8")
    return metadata


def normalized_source(*, connector: str, title: str, url: str, source_type: str,
                      version: str = "", publication_date: str = "", sections: list[dict[str,Any]] | None = None,
                      raw_meta: dict[str,Any] | None = None, identifiers: dict[str,Any] | None = None,
                      warnings: list[str] | None = None) -> dict[str,Any]:
    return {
        "sourceId": f"src_{hashlib.sha1((connector+'|'+url+'|'+title).encode()).hexdigest()[:16]}",
        "connector": connector,
        "sourceType": source_type,
        "title": title,
        "url": url,
        "sourceVersion": version,
        "publicationDate": publication_date,
        "retrievedAt": utcnow(),
        "identifiers": identifiers or {},
        "sections": sections or [],
        "raw": raw_meta or {},
        "warnings": warnings or [],
        "reviewStatus": "unreviewed",
    }


def clean_text(value: Any) -> str:
    text = re.sub(r"\s+", " ", str(value or "")).strip()
    return text


def query_string(params: dict[str, Any]) -> str:
    return urllib.parse.urlencode({k:v for k,v in params.items() if v not in (None, "")})
