"""Fetch the raw OpenStreetMap extracts for the Mesto 2 city (Prague, the Vltava bend) from the Overpass API, once.

Output: raw/*.json.gz (Overpass JSON, `out geom`). Data (c) OpenStreetMap contributors, ODbL 1.0.
Run:  python fetch.py            (skips files that already exist; --force refetches)

Three tiers:
  core   everything the close-up flight and the top-down name need: every highway class, rail/tram, buildings,
         water, parks, street lamps, bridges, floodlit landmarks.
  mid    a 12 x 12 km ring for the far field of a low flight: drivable streets only (no buildings).
  outer  a 26 x 24 km box: main roads only (motorway..tertiary), water, for the horizon glitter.
"""
import gzip
import json
import os
import sys
import time
import urllib.parse
import urllib.request

HERE = os.path.dirname(os.path.abspath(__file__))
RAW = os.path.join(HERE, 'raw')
UA = 'nolentra-city-study/1.0 (offline bake, one-off)'
ENDPOINTS = ['https://overpass-api.de/api/interpreter', 'https://overpass.kumi.systems/api/interpreter']

# (south, west, north, east)
CORE = (50.0700, 14.3900, 50.1150, 14.4650)
MID = (50.0280, 14.3350, 50.1530, 14.5280)
OUTER = (49.9700, 14.2300, 50.1950, 14.6500)


def bb(b):
    return f'{b[0]},{b[1]},{b[2]},{b[3]}'


QUERIES = {
    'core': f"""
[out:json][timeout:300][maxsize:1073741824][bbox:{bb(CORE)}];
(
  way["highway"];
  way["railway"~"^(rail|tram|light_rail|subway|narrow_gauge)$"];
  way["building"];
  relation["building"];
  way["building:part"];
  way["natural"="water"];
  relation["natural"="water"];
  way["waterway"~"^(riverbank|river|canal|dock)$"];
  relation["waterway"="riverbank"];
  way["leisure"~"^(park|garden|pitch|stadium)$"];
  relation["leisure"~"^(park|garden)$"];
  way["landuse"~"^(grass|forest|cemetery|railway|meadow|recreation_ground|allotments)$"];
  relation["landuse"~"^(grass|forest|cemetery|railway)$"];
  way["natural"~"^(wood|scrub)$"];
  node["highway"="street_lamp"];
  way["man_made"="bridge"];
  relation["man_made"="bridge"];
);
out geom qt;
""",
    'mid': f"""
[out:json][timeout:600][maxsize:1073741824][bbox:{bb(MID)}];
way["highway"~"^(motorway|trunk|primary|secondary|tertiary|motorway_link|trunk_link|primary_link|secondary_link|tertiary_link|residential|unclassified|living_street)$"];
out geom qt;
""",
    'midwater': f"""
[out:json][timeout:300][maxsize:1073741824][bbox:{bb(MID)}];
(
  way["natural"="water"];
  way["waterway"="riverbank"];
  relation["natural"="water"]["water"="river"];
);
out geom qt;
""",
    'outer': f"""
[out:json][timeout:600][maxsize:1073741824][bbox:{bb(OUTER)}];
way["highway"~"^(motorway|trunk|primary|secondary|tertiary|motorway_link|trunk_link|primary_link)$"];
out geom qt;
""",
}


def fetch(name, q, force):
    out = os.path.join(RAW, f'{name}.json.gz')
    if os.path.exists(out) and not force:
        print(f'{name}: exists ({os.path.getsize(out)} bytes)')
        return
    data = urllib.parse.urlencode({'data': q}).encode()
    last = None
    for ep in ENDPOINTS:
        for attempt in range(3):
            try:
                t0 = time.time()
                req = urllib.request.Request(ep, data=data, headers={'User-Agent': UA, 'Accept': 'application/json'})
                with urllib.request.urlopen(req, timeout=400) as r:
                    body = r.read()
                js = json.loads(body)
                with gzip.open(out, 'wb') as f:
                    f.write(body)
                print(f'{name}: {len(js["elements"])} elements, {len(body)/1e6:.1f} MB raw, {time.time()-t0:.1f} s from {ep}')
                return
            except Exception as e:  # noqa: BLE001
                last = e
                print(f'{name}: {ep} attempt {attempt + 1} failed: {e}')
                time.sleep(8 * (attempt + 1))
    raise SystemExit(f'{name}: all endpoints failed: {last}')


if __name__ == '__main__':
    os.makedirs(RAW, exist_ok=True)
    force = '--force' in sys.argv
    only = [a for a in sys.argv[1:] if not a.startswith('--')]
    for n, q in QUERIES.items():
        if only and n not in only:
            continue
        fetch(n, q, force)
