#!/usr/bin/env python3
"""
Download gridMET daily NetCDF files into year-based folders.

Default layout:
data/gridmet/1991/sph.nc
data/gridmet/1991/rmax.nc
data/gridmet/1991/rmin.nc

gridMET source:
https://www.northwestknowledge.net/metdata/data/{variable}_{year}.nc
"""

from __future__ import annotations

import argparse
import sys
import tempfile
import urllib.error
import urllib.request
from pathlib import Path


DEFAULT_BASE_URL = "https://www.northwestknowledge.net/metdata/data"
DEFAULT_VARIABLES = ("sph", "rmax", "rmin")


def _parse_years(text: str) -> list[int]:
    years: set[int] = set()
    for part in text.split(","):
        token = part.strip()
        if not token:
            continue
        if "-" in token:
            start_text, end_text = token.split("-", 1)
            start = int(start_text)
            end = int(end_text)
            if start > end:
                raise argparse.ArgumentTypeError(f"Invalid descending year range: {token}")
            years.update(range(start, end + 1))
        else:
            years.add(int(token))
    if not years:
        raise argparse.ArgumentTypeError("At least one year is required.")
    return sorted(years)


def _parse_variables(text: str) -> list[str]:
    variables = [part.strip() for part in text.split(",") if part.strip()]
    if not variables:
        raise argparse.ArgumentTypeError("At least one variable is required.")
    return variables


def _download_file(url: str, destination: Path, overwrite: bool) -> bool:
    if destination.exists() and not overwrite:
        print(f"skip existing {destination}")
        return False

    destination.parent.mkdir(parents=True, exist_ok=True)
    with tempfile.NamedTemporaryFile(
        prefix=destination.stem + ".",
        suffix=".download",
        dir=destination.parent,
        delete=False,
    ) as handle:
        temp_path = Path(handle.name)

    try:
        print(f"download {url}")
        with urllib.request.urlopen(url) as response, temp_path.open("wb") as output:
            total_text = response.headers.get("Content-Length")
            total = int(total_text) if total_text and total_text.isdigit() else None
            copied = 0
            while True:
                chunk = response.read(1024 * 1024)
                if not chunk:
                    break
                output.write(chunk)
                copied += len(chunk)
                if total:
                    percent = copied / total * 100
                    print(f"\r  {copied / 1024 / 1024:,.1f} MiB / {total / 1024 / 1024:,.1f} MiB ({percent:5.1f}%)", end="")
            if total:
                print()

        temp_path.replace(destination)
        print(f"saved {destination}")
        return True
    except (urllib.error.URLError, urllib.error.HTTPError, OSError) as exc:
        temp_path.unlink(missing_ok=True)
        print(f"failed {url}: {exc}", file=sys.stderr)
        raise


def main() -> int:
    parser = argparse.ArgumentParser(
        description="Download gridMET NetCDF files into data/gridmet/<year>/<variable>.nc."
    )
    parser.add_argument(
        "--years",
        type=_parse_years,
        default=_parse_years("1991-2020"),
        help="Comma-separated years/ranges. Default: 1991-2020.",
    )
    parser.add_argument(
        "--variables",
        type=_parse_variables,
        default=list(DEFAULT_VARIABLES),
        help="Comma-separated gridMET variables. Default: sph,rmax,rmin.",
    )
    parser.add_argument(
        "--output-dir",
        type=Path,
        default=Path("data/gridmet"),
        help="Root folder for downloaded files. Default: data/gridmet.",
    )
    parser.add_argument(
        "--base-url",
        default=DEFAULT_BASE_URL,
        help=f"Base gridMET download URL. Default: {DEFAULT_BASE_URL}.",
    )
    parser.add_argument(
        "--overwrite",
        action="store_true",
        help="Redownload files that already exist.",
    )
    args = parser.parse_args()

    downloaded = 0
    for year in args.years:
        for variable in args.variables:
            url = f"{args.base_url.rstrip('/')}/{variable}_{year}.nc"
            destination = args.output_dir / str(year) / f"{variable}.nc"
            if _download_file(url, destination, args.overwrite):
                downloaded += 1

    print(f"done; downloaded {downloaded} file(s)")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
