#!/usr/bin/env python3
"""designer — 2024~2026 개편으로 새로 생긴 구의 OSM 점 받기(카드 t_ef7905b3, guard (193) C193-2). 일회성.

왜: 입력 자료(kr_admin_places.json, OSM place=borough 점)에 아래 구가 없다. OSM 에는 이 구들이 place 점이 아니라
    행정경계(relation, boundary=administrative)로만 들어 있다. 그래서 relation 을 이름으로 찾아, Nominatim 이 그 경계에
    붙인 대표점(centroid)을 받아 둔다. 손으로 찍은 좌표 0.
      수원 팔달구 / 부천 원미·소사·오정구(2024-01-02) / 화성 만세·효행·병점·동탄구(2026-02-01)
      인천 제물포·영종·서해·검단구(2026-07-01)
무엇: 이름마다 Nominatim search(countrycodes=kr) → boundary=administrative relation 하나 → OSM API 로 relation 태그(name·admin_level·
      버전·수정 시각) 확인 → Nominatim details 의 centroid. 결과를 --out JSON 하나에 쓴다(이 파일 말고 쓰는 곳 0).
      요청은 1.5초 간격, 이름당 3번(검색·relation·details) — Nominatim 이용 기준(초당 1건 이하) 안.
사용: python3 osm_gu_relations.py --out data/kr_gu_2026.json
OSM 데이터 © OpenStreetMap 기여자, ODbL 1.0 https://opendatacommons.org/licenses/odbl/1-0/
"""
import argparse
import json
import os
import subprocess
import time

UA = 'rich-rider-designer/1.0 (one-off gu audit)'
# (시, 구 이름, 공식 출처) — 출처는 그 구가 2026-10-14 현재 공식 이름이라는 근거
GU = [
    ('수원', '팔달구', 'https://ko.wikipedia.org/wiki/%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD%EC%9D%98_%EA%B5%AC'),
    ('부천', '원미구', 'https://www.khan.co.kr/article/202401021500001'),
    ('부천', '소사구', 'https://www.khan.co.kr/article/202401021500001'),
    ('부천', '오정구', 'https://www.khan.co.kr/article/202401021500001'),
    ('화성', '만세구', 'https://www.yna.co.kr/view/AKR20260120103000061'),
    ('화성', '효행구', 'https://www.yna.co.kr/view/AKR20260120103000061'),
    ('화성', '병점구', 'https://www.yna.co.kr/view/AKR20260120103000061'),
    ('화성', '동탄구', 'https://www.yna.co.kr/view/AKR20260120103000061'),
    ('인천', '제물포구', 'https://www.incheon.go.kr/IC010601/2187729'),
    ('인천', '영종구', 'https://www.incheon.go.kr/IC010601/2187729'),
    ('인천', '서해구', 'https://www.incheon.go.kr/IC010601/2187729'),
    ('인천', '검단구', 'https://www.incheon.go.kr/IC010601/2187729'),
]


def get(url, params=None):
    cmd = ['curl', '-s', '-m', '40', '-A', UA, '-G', url]
    for k, v in (params or {}).items():
        cmd += ['--data-urlencode', f'{k}={v}']
    time.sleep(1.5)
    return json.loads(subprocess.run(cmd, capture_output=True, check=True).stdout)


def one(city, name):
    hits = get('https://nominatim.openstreetmap.org/search',
               {'q': name, 'format': 'jsonv2', 'countrycodes': 'kr', 'limit': '8'})
    rel = [h for h in hits if h['osm_type'] == 'relation' and h['category'] == 'boundary' and h['type'] == 'administrative'
           and h['name'] == name]
    if len(rel) != 1:
        return None, f'relation {len(rel)}개(검색 {len(hits)}건)'
    rid = rel[0]['osm_id']
    e = get(f'https://api.openstreetmap.org/api/0.6/relation/{rid}.json')['elements'][0]
    t = e['tags']
    if t.get('name') != name or t.get('boundary') != 'administrative':
        return None, f'relation {rid} 태그 불일치 name={t.get("name")}'
    det = get('https://nominatim.openstreetmap.org/details', {'osmtype': 'R', 'osmid': str(rid), 'format': 'json'})
    lon, lat = det['centroid']['coordinates']
    label = next((m['ref'] for m in e['members'] if m['type'] == 'node' and m['role'] == 'label'), None)
    return {'name': name, 'city': city, 'relation': rid, 'admin_level': t.get('admin_level'), 'version': e['version'],
            'timestamp': e['timestamp'], 'label_node': label, 'lon': lon, 'lat': lat,
            'centroid_from': 'Nominatim 경계 대표점(centroid)'}, None


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument('--out', required=True)
    a = ap.parse_args()
    out = {'note': 'OSM 행정경계 relation 의 대표점(Nominatim centroid). © OpenStreetMap 기여자, ODbL 1.0 '
                   'https://opendatacommons.org/licenses/odbl/1-0/',
           'fetched': time.strftime('%Y-%m-%d %H:%M %Z'), 'source': 'nominatim.openstreetmap.org + api.openstreetmap.org',
           'gu': [], 'missing': []}
    for city, name, src in GU:
        g, err = one(city, name)
        if g is None:
            out['missing'].append({'name': name, 'city': city, 'why': err, 'official': src})
            print(f'{city} {name}: 못 찾음 — {err}', flush=True)
            continue
        g['official'] = src
        out['gu'].append(g)
        print(f'{city} {name}: relation {g["relation"]} v{g["version"]} {g["timestamp"]} ({g["lat"]:.4f},{g["lon"]:.4f}) {g["centroid_from"]}',
              flush=True)
    with open(a.out, 'w') as f:
        json.dump(out, f, ensure_ascii=False, indent=1)
    print(f'{len(out["gu"])}/{len(GU)} → {a.out}')


if __name__ == '__main__':
    main()
