"""Download free-license orthopaedic / medical imagery from Wikimedia Commons.

All source files are located via the Commons "imageinfo" API, which returns a
direct image URL plus license / author metadata. Wikimedia Commons files are
released under free licenses (CC0, CC-BY, CC-BY-SA, or public domain) — we track
the photographer and source page for every download regardless of whether
attribution is strictly required, so the /credits page can surface them.

Usage: `python site/scripts/download-photos.py` from anywhere. Paths are
resolved relative to this file so it works from any cwd.
"""

from __future__ import annotations

import json
import logging
import re
import sys
import time
from dataclasses import dataclass
from pathlib import Path
from typing import Optional
from urllib.parse import quote

import requests

try:
    from PIL import Image
    PIL_OK = True
except ImportError:
    PIL_OK = False

# ---------------------------------------------------------------------------
# Paths
# ---------------------------------------------------------------------------

SCRIPT_DIR = Path(__file__).resolve().parent
SITE_DIR = SCRIPT_DIR.parent
PHOTOS_ROOT = SITE_DIR / "public" / "photos"
MANIFEST_PATH = SITE_DIR / "src" / "data" / "photos.json"
LOG_PATH = SCRIPT_DIR / "download-photos.log"

USER_AGENT = (
    "OrthoGlobeSiteBot/1.0 (https://orthoglobe.org; hello@orthoglobe.org) "
    "requests/2"
)
COMMONS_API = "https://commons.wikimedia.org/w/api.php"

# ---------------------------------------------------------------------------
# Curated targets — each entry uses Wikimedia Commons file names we vetted
# via the search API. Alt text is hand-written; credit/source are resolved at
# download time.
# ---------------------------------------------------------------------------


@dataclass
class Target:
    category: str
    slug: str
    commons_file: str  # without "File:" prefix
    alt: str


TARGETS: list[Target] = [
    # --- surgery ----------------------------------------------------------
    Target(
        "surgery",
        "burundian-surgeons-or",
        "US Army, Burundian orthopedic surgeons work together in the operating room (8558543).jpg",
        "Orthopaedic surgeons collaborating in the operating room during a joint training mission.",
    ),
    Target(
        "surgery",
        "afghanistan-ortho-surgery",
        "Orthopedic surgery in Afghanistan 141103-N-JY715-055.jpg",
        "Orthopaedic surgery in progress inside a field operating room in Afghanistan.",
    ),
    Target(
        "surgery",
        "joint-replacement-vietnam",
        "Navy doctors from USNS Mercy perform joint replacement surgery in Vietnam during Pacific Partnership 2015 150818-N-UQ938-103.jpg",
        "Surgical team performing a total joint replacement during an international partnership mission.",
    ),
    Target(
        "surgery",
        "chad-army-reserve",
        "Army Reserve surgeon performs surgery in Chad hospital 160617-A-ZP772-647.jpg",
        "Orthopaedic surgeon operating in a resource-limited hospital in Chad.",
    ),

    # --- anatomy ----------------------------------------------------------
    Target(
        "anatomy",
        "human-skeleton-front",
        "Human skeleton front numbered.png",
        "Anatomical illustration of the full human skeleton, anterior view with numbered bones.",
    ),
    Target(
        "anatomy",
        "skeleton-upper-body",
        "Human Skeleton Upper Body Anterior View.jpg",
        "Anterior view of the human upper-body skeleton used in anatomy teaching.",
    ),
    Target(
        "anatomy",
        "xray-hand",
        "X-ray of hand.jpg",
        "Plain radiograph of a human hand showing the carpal, metacarpal, and phalangeal bones.",
    ),
    Target(
        "anatomy",
        "xray-hip-fracture",
        "X-ray of subtle compressive hip fracture, labeled.jpg",
        "Labeled plain radiograph demonstrating a subtle compressive hip fracture.",
    ),

    # --- training ---------------------------------------------------------
    Target(
        "training",
        "students-stitches",
        "Medical students learning about stitches (2760577402).jpg",
        "Medical students practising suturing technique during a hands-on training session.",
    ),
    Target(
        "training",
        "anatomage-table",
        "Anatomage Table at Kharkiv International Medical University.jpg",
        "Students gathered around an Anatomage virtual dissection table during an anatomy class.",
    ),
    Target(
        "training",
        "medical-lecture-room",
        "Lecture room, Army Medical School, 1901-2, in Army Medical Museum and Library building.jpg",
        "A large tiered medical lecture hall set up for teaching.",
    ),
    Target(
        "training",
        "chest-tube-instructor",
        "US Navy 120114-N-GO179-023 Kandahar Role 3 Operational Medical Training Program guest instructor Cmdr. Michael Coffel, explains chest tube placemen.jpg",
        "Senior clinician instructing trainees on a procedural skill during a hospital teaching round.",
    ),

    # --- rehab ------------------------------------------------------------
    Target(
        "rehab",
        "physical-therapy",
        "Physical Therapy.jpg",
        "Patient working through a rehabilitation exercise with a physical therapist.",
    ),
    Target(
        "rehab",
        "spangdahlem-rehab-1",
        "Spangdahlem physical therapy rehabilitates Airmen after injury (7270862).jpg",
        "Therapist guiding a patient through post-injury rehabilitation exercises.",
    ),
    Target(
        "rehab",
        "spangdahlem-rehab-2",
        "Spangdahlem physical therapy rehabilitates Airmen after injury (7270859).jpg",
        "Patient performing resistance-band rehabilitation work under supervision.",
    ),
    Target(
        "rehab",
        "spangdahlem-rehab-3",
        "Spangdahlem physical therapy rehabilitates Airmen after injury (7270861).jpg",
        "Physical therapy session focused on knee range of motion.",
    ),

    # --- global-health ----------------------------------------------------
    Target(
        "global-health",
        "banaadir-hospital-1",
        "2014 02 04 Banaadir Hospital-3 (12316155713).jpg",
        "Clinical staff at work inside Banaadir Hospital, Mogadishu.",
    ),
    Target(
        "global-health",
        "pumwani-obgyn",
        "Obs Gyn surgeon at Pumwani maternity Hospital Nairobi.jpg",
        "A surgeon at work inside Pumwani Maternity Hospital, Nairobi.",
    ),
    Target(
        "global-health",
        "png-nurse-patient",
        "Nurse and patient at hospital in East New Britain, PNG.jpg",
        "Nurse caring for a patient at a community hospital in Papua New Guinea.",
    ),
    Target(
        "global-health",
        "idf-nairobi",
        "Israeli IDF surgeon operating injured victim Nairobi hospital.jpg",
        "International surgical team operating in a hospital in Nairobi.",
    ),

    # --- consent ----------------------------------------------------------
    Target(
        "consent",
        "doctor-talking-patient",
        "Doctor talking with a patient.jpg",
        "Clinician in conversation with a patient during a consultation.",
    ),
    Target(
        "consent",
        "nurse-attending",
        "Nurse attending to a patient.jpg",
        "Nurse attending to a patient at the bedside.",
    ),
    Target(
        "consent",
        "nurse-blood-pressure",
        "Nurse takes a patient's blood pressure.jpg",
        "A nurse takes a patient's blood pressure during a clinic visit.",
    ),
    Target(
        "consent",
        "leukemia-consultation",
        "CP25 Leukemia Patient Consultation (9123625).jpg",
        "Physician discussing a diagnosis and treatment plan with a patient.",
    ),

    # --- exam-prep --------------------------------------------------------
    Target(
        "exam-prep",
        "library-reading",
        "Public Library Bhiswa – Students Reading.jpg",
        "Students studying quietly at a public library reading room.",
    ),
    Target(
        "exam-prep",
        "anatomy-textbook",
        "A textbook of obstetrics (1899) (14590989838).jpg",
        "Open medical textbook page used during study.",
    ),
    Target(
        "exam-prep",
        "surgical-instruments",
        "Surgical Instruments 01.jpg",
        "Orthopaedic surgical instruments laid out for study and reference.",
    ),

    # --- classroom --------------------------------------------------------
    Target(
        "classroom",
        "video-conference-class",
        "A video conference with a class 2.jpg",
        "A remote teaching session delivered over video conference to a class of students.",
    ),
    Target(
        "classroom",
        "biology-classroom",
        "Biology Classroom Christ School.jpg",
        "Students seated in a biology classroom during a lecture.",
    ),
    Target(
        "classroom",
        "laptops-students",
        "Laptops for students 130618-A-WF509-002.jpg",
        "Students working together on laptops during a class.",
    ),
]


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def setup_logging() -> None:
    LOG_PATH.parent.mkdir(parents=True, exist_ok=True)
    logging.basicConfig(
        level=logging.INFO,
        format="%(asctime)s %(levelname)-7s %(message)s",
        handlers=[
            logging.FileHandler(LOG_PATH, mode="w", encoding="utf-8"),
            logging.StreamHandler(sys.stdout),
        ],
    )


def strip_html(text: str) -> str:
    """Commons metadata fields often contain small HTML fragments."""
    if not text:
        return ""
    text = re.sub(r"<[^>]+>", "", text)
    return text.strip()


def resolve_commons_file(
    session: requests.Session, filename: str
) -> Optional[dict]:
    """Hit the Commons imageinfo API for a file. Return dict with url/credit/source/license."""
    params = {
        "action": "query",
        "format": "json",
        "titles": f"File:{filename}",
        "prop": "imageinfo",
        "iiprop": "url|extmetadata|size|mime",
        "iiurlwidth": 1600,
    }
    r = session.get(COMMONS_API, params=params, timeout=15)
    r.raise_for_status()
    data = r.json()
    pages = data.get("query", {}).get("pages", {})
    if not pages:
        return None
    page = next(iter(pages.values()))
    if "imageinfo" not in page or not page["imageinfo"]:
        return None
    info = page["imageinfo"][0]
    meta = info.get("extmetadata", {}) or {}
    artist = strip_html(meta.get("Artist", {}).get("value", "")) or "Unknown"
    license_short = strip_html(meta.get("LicenseShortName", {}).get("value", "")) or "CC"
    # Prefer the 1600px thumbnail so we don't download originals that can be huge.
    url = info.get("thumburl") or info.get("url")
    return {
        "url": url,
        "credit": artist,
        "license": license_short,
        "source": info.get("descriptionurl", ""),
        "mime": info.get("mime", "image/jpeg"),
    }


def download(session: requests.Session, url: str, dest: Path) -> bool:
    dest.parent.mkdir(parents=True, exist_ok=True)
    try:
        r = session.get(url, timeout=30, stream=True)
    except requests.RequestException as e:
        logging.warning("  fetch failed: %s", e)
        return False
    if r.status_code == 404:
        logging.warning("  404: %s", url)
        return False
    if r.status_code != 200:
        logging.warning("  status %d: %s", r.status_code, url)
        return False
    with dest.open("wb") as f:
        for chunk in r.iter_content(65536):
            if chunk:
                f.write(chunk)
    return True


def resize_to_max(path: Path, max_edge: int = 1600, quality: int = 82) -> None:
    """Open image, cap the long edge at max_edge, re-save as JPEG at given quality."""
    if not PIL_OK:
        return
    try:
        with Image.open(path) as im:
            im = im.convert("RGB")
            w, h = im.size
            long_edge = max(w, h)
            if long_edge > max_edge:
                scale = max_edge / long_edge
                new_size = (int(w * scale), int(h * scale))
                im = im.resize(new_size, Image.LANCZOS)
            im.save(path, format="JPEG", quality=quality, optimize=True)
    except Exception as e:  # pragma: no cover
        logging.warning("  resize failed for %s: %s", path, e)


# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------


def main() -> int:
    setup_logging()
    logging.info("Downloading %d photos to %s", len(TARGETS), PHOTOS_ROOT)
    if not PIL_OK:
        logging.warning("Pillow not available — skipping resize step.")

    session = requests.Session()
    session.headers.update({"User-Agent": USER_AGENT})

    manifest: list[dict] = []
    per_category: dict[str, int] = {}

    for i, t in enumerate(TARGETS, 1):
        logging.info("[%d/%d] %s/%s", i, len(TARGETS), t.category, t.slug)
        info = resolve_commons_file(session, t.commons_file)
        if not info or not info["url"]:
            logging.warning("  could not resolve Commons file")
            continue

        ext = ".jpg"  # we always re-save as JPEG
        dest_rel = Path("photos") / t.category / f"{t.slug}{ext}"
        dest_abs = SITE_DIR / "public" / dest_rel

        if not download(session, info["url"], dest_abs):
            continue

        resize_to_max(dest_abs)
        size_kb = dest_abs.stat().st_size // 1024
        logging.info("  ok (%d KB) %s", size_kb, info["license"])

        manifest.append(
            {
                "category": t.category,
                "filename": f"{t.slug}{ext}",
                "path": "/" + dest_rel.as_posix(),
                "alt": t.alt,
                "credit": info["credit"],
                "source": info["source"],
                "license": info["license"],
            }
        )
        per_category[t.category] = per_category.get(t.category, 0) + 1

        # Be polite to Wikimedia — spread requests out.
        time.sleep(0.25)

    # Write manifest
    MANIFEST_PATH.parent.mkdir(parents=True, exist_ok=True)
    MANIFEST_PATH.write_text(
        json.dumps(manifest, indent=2, ensure_ascii=False), encoding="utf-8"
    )
    logging.info("Wrote manifest: %s (%d entries)", MANIFEST_PATH, len(manifest))

    logging.info("Per-category counts:")
    for cat, count in sorted(per_category.items()):
        logging.info("  %-14s %d", cat, count)

    # Total size
    total_bytes = 0
    for p in PHOTOS_ROOT.rglob("*"):
        if p.is_file():
            total_bytes += p.stat().st_size
    logging.info("Total public/photos size: %.1f MB", total_bytes / 1024 / 1024)

    return 0 if manifest else 1


if __name__ == "__main__":
    sys.exit(main())
