"""Download the three selected Aral Lake acquisitions using a token on stdin."""
from pathlib import Path
import argparse
import json
import urllib.parse
import urllib.request
import shutil
import subprocess
import sys
import zipfile

PRODUCTS = [
    ("2023-05-26", "S2A_MSIL2A_20230526T065621_N0510_R063_T40TGS_20240907T073145.SAFE"),
    ("2023-06-05", "S2A_MSIL2A_20230605T065621_N0510_R063_T40TGS_20240911T135334.SAFE"),
    ("2023-07-05", "S2A_MSIL2A_20230705T065631_N0510_R063_T40TGS_20240912T130104.SAFE"),
]


def complete(product):
    if not (product / "MTD_MSIL2A.xml").is_file():
        return False
    granules = [p for p in (product / "GRANULE").glob("*") if p.is_dir()]
    return bool(granules) and all(
        len([p for p in granule.glob(f"IMG_DATA/R{resolution}m/*_{band}_{resolution}m.jp2") if p.stat().st_size]) == 1
        for granule in granules
        for band, resolution in [("B03", 10), ("B08", 10), ("SCL", 20)]
    )


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--catalogue-url", required=True, help="OData base URL shown in the accompanying article")
    parser.add_argument("--data-dir", type=Path, default=Path.home() / "Downloads" / "aral-data")
    args = parser.parse_args()
    base = args.catalogue_url.rstrip("/")
    parsed = urllib.parse.urlsplit(base)
    if parsed.scheme != "https" or not parsed.netloc or parsed.query or parsed.fragment or parsed.username:
        parser.error("--catalogue-url must be an HTTPS OData base URL.")
    if sys.stdin.isatty():
        raise SystemExit('Pipe ACCESS_TOKEN into this script in the terminal; see the command in the accompanying notebook.')
    token = sys.stdin.read().strip()
    if not token or token == "null" or any(c in token for c in '\r\n"'):
        raise SystemExit("ACCESS_TOKEN is empty or invalid. Authenticate again in this terminal.")
    if shutil.which("curl") is None:
        raise SystemExit("curl is required.")
    root = args.data_dir.expanduser().resolve()
    root.mkdir(parents=True, exist_ok=True)
    if shutil.disk_usage(root).free < 9 * 1024**3:
        raise SystemExit("Allow at least 9 GiB of free space for these archives, extracted products and outputs.")
    manifest = []
    for date, name in PRODUCTS:
        query = urllib.parse.urlencode({"$filter": f"Name eq '{name}'", "$select": "Id,Name", "$top": 2})
        with urllib.request.urlopen(f"{base}/Products?{query}", timeout=90) as response:
            matches = json.load(response)["value"]
        if len(matches) != 1 or matches[0]["Name"] != name:
            raise SystemExit(f"Expected one catalogue product named {name}; found {len(matches)}.")
        product_id = matches[0]["Id"]
        product = root / name
        archive = root / f"{name}.zip"
        part = root / f"{name}.zip.part"
        if complete(product):
            print(f"{date}: required files already present; skipping download.", flush=True)
        else:
            if not archive.exists():
                print(f"{date}: downloading {name}", flush=True)
                url = f"{base}/Products({product_id})/$value"
                # The token goes through curl's stdin configuration, never its command line.
                result = subprocess.run(
                    ["curl", "--config", "-", "--fail", "--location-trusted", "--progress-bar", "--connect-timeout", "30", url, "--output", str(part)],
                    input=f'header = "Authorization: Bearer {token}"\n',
                    text=True,
                )
                if result.returncode:
                    raise SystemExit(
                        f"Download stopped for {date} (curl exit {result.returncode}). "
                        "If curl reported HTTP 401, obtain a fresh ACCESS_TOKEN and rerun the same command. "
                        "Completed products will be reused."
                    )
                if not zipfile.is_zipfile(part):
                    raise SystemExit(f"The response is not a ZIP archive: {part}")
                part.replace(archive)
            print(f"{date}: extracting archive.", flush=True)
            with zipfile.ZipFile(archive) as source:
                names = source.namelist()
                if f"{name}/MTD_MSIL2A.xml" not in names:
                    raise SystemExit(f"Archive does not contain the expected product metadata: {archive}")
                for member in source.infolist():
                    destination = (root / member.filename).resolve()
                    if root.resolve() not in destination.parents:
                        raise SystemExit("Archive contains an unexpected destination path.")
                    if Path(member.filename).parts[0] != name:
                        raise SystemExit("Archive contains an unexpected top-level folder.")
                source.extractall(root)
            if not complete(product):
                raise SystemExit(f"Required metadata or bands are missing in {product}")
        manifest.append({"date": date, "id": product_id, "name": name, "path": str(product), "catalogue_url": base})
    (root / "comparison-products.json").write_text(json.dumps(manifest, indent=2) + "\n")
    print("All three comparison products are ready.", flush=True)
    print(f"Data folder: {root}", flush=True)


if __name__ == "__main__":
    main()
