#!/usr/bin/env python3
"""designer — 한국 OSM place 점 중 시·군·구·도(county/city/borough/province/state) 수집(카드 t_9b88c1e1, 일회성 조사).
앱·타일과 무관 — 'Shortbread 가 무엇을 버렸나'의 근거이자, 수정안 미리보기(지도 판 렌더)에 얹을 군·구 지명 자료.

출력
  stdout(로그): 종류별 개수·이름·population 태그 유무. 좌표 0.
  --save <json>: 이름·종류·population·위경도(공개 OSM 지명 점 — 사용자 자료 아님). 미리보기 렌더 입력.
상자를 나눠 묻고, 받은 상자는 --save 파일에 바로 쌓는다(중간에 끊겨도 이어서 — 이미 받은 상자는 건너뜀).
OSM 데이터 © OpenStreetMap 기여자, ODbL 1.0.

사용: python3 tools/design/route_map/osm_admin_places.py --save tools/design/route_map/data/kr_admin_places.json \
        > reports/logs/2026-10-13_designer_osm_admin_places.log
"""
import argparse
import collections
import json
import os
import subprocess
import time

EP = ['https://overpass-api.de/api/interpreter', 'https://overpass.private.coffee/api/interpreter']
# 남한 상자(위도 33.0~39.0, 경도 124.5~131.1)를 위도 1° × 경도 2.2° 칸으로(18칸)
BOXES = [(round(33.0 + i, 2), round(124.5 + 2.2 * j, 2), round(34.0 + i, 2), round(126.7 + 2.2 * j, 2))
         for i in range(6) for j in range(3)]
Q = '[out:json][timeout:90];node({},{},{},{})[place~"^(county|borough|city|province|state)$"];out;'


def ask(box):
    for attempt in range(8):
        ep = EP[attempt % len(EP)]
        r = subprocess.run(['curl', '-s', '-m', '120', '-A', 'rich-rider-designer/1.0 (one-off label audit)',
                            '--data-urlencode', 'data=' + Q.format(*box), ep], capture_output=True)
        try:
            return json.loads(r.stdout)['elements']
        except Exception:
            time.sleep(15 + attempt * 10)
    return None


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument('--save', required=True)
    a = ap.parse_args()
    os.makedirs(os.path.dirname(a.save), exist_ok=True)
    data = json.load(open(a.save)) if os.path.exists(a.save) else {'boxes_done': [], 'nodes': {}}
    failed = []
    for b in BOXES:
        if list(b) in data['boxes_done']:
            continue
        els = ask(b)
        if els is None:
            failed.append(b)
            continue
        for e in els:
            t = e.get('tags', {})
            data['nodes'][str(e['id'])] = {'name': t.get('name'), 'place': t.get('place'),
                                           'population': t.get('population'), 'lon': e['lon'], 'lat': e['lat']}
        data['boxes_done'].append(list(b))
        data['note'] = 'OSM place 점(시·군·구·도). © OpenStreetMap 기여자, ODbL 1.0. 디자인 미리보기 입력 — 앱에 들어가지 않음.'
        data['fetched'] = time.strftime('%Y-%m-%d %H:%M')
        with open(a.save, 'w') as f:
            json.dump(data, f, ensure_ascii=False, indent=0)
        time.sleep(3)
    nodes = list(data['nodes'].values())
    by = collections.defaultdict(list)
    for t in nodes:
        by[t['place']].append(t)
    print(f'OSM place 점(남한 상자 {len(data["boxes_done"])}/{len(BOXES)}칸, 시·군·구·도 계열) {len(nodes)}개 — 조회 {data.get("fetched")}')
    if failed:
        print(f'못 받은 상자 {len(failed)}: {failed} ← 다시 돌리면 이어서 받음')
    for p in ('province', 'state', 'city', 'county', 'borough'):
        ts = by.get(p, [])
        names = sorted(t['name'] or '?' for t in ts)
        pop = sum(1 for t in ts if t['population'])
        print(f'\nplace={p}: {len(ts)}개, population 태그 있음 {pop}개')
        print('  ' + ', '.join(names))


if __name__ == '__main__':
    main()
