"""PDOK Locatieserver — postcode geocoding."""
from __future__ import annotations

import json
import re
import urllib.parse
import urllib.request
from typing import Any, Optional

PDOK_URL = "https://api.pdok.nl/bzk/locatieserver/search/v3_1/free"
POSTCODE_RE = re.compile(r"^(\d{4})\s?([A-Za-z]{2})$")


def normalize_postcode(raw: str) -> str:
    cleaned = (raw or "").strip().upper().replace(" ", "")
    m = POSTCODE_RE.match(cleaned)
    if m:
        return f"{m.group(1)}{m.group(2)}"
    return cleaned[:6] if cleaned else ""


def _parse_point(value: Optional[str]) -> tuple[Optional[float], Optional[float]]:
    if not value or "POINT" not in value:
        return None, None
    nums = re.findall(r"[-+]?\d*\.?\d+", value)
    if len(nums) >= 2:
        return float(nums[1]), float(nums[0])  # lat, lon
    return None, None


def lookup_postcode(postcode: str) -> Optional[dict[str, Any]]:
    pc = normalize_postcode(postcode)
    if len(pc) < 6:
        return None
    q = urllib.parse.urlencode({"q": pc, "rows": 1, "fq": "type:postcode"})
    with urllib.request.urlopen(f"{PDOK_URL}?{q}", timeout=20) as resp:
        data = json.loads(resp.read().decode())
    docs = data.get("response", {}).get("docs", [])
    if not docs:
        return None
    doc = docs[0]
    lat, lon = _parse_point(doc.get("centroide_ll"))
    gemeente_code = (doc.get("gemeentecode") or "").strip()
    if gemeente_code and not gemeente_code.startswith("GM"):
        gemeente_code = f"GM{gemeente_code}"
    return {
        "postcode": pc,
        "city": (doc.get("woonplaatsnaam") or "").strip(),
        "province": (doc.get("provincienaam") or "").strip(),
        "province_code": (doc.get("provinciecode") or "").strip(),
        "municipality": (doc.get("gemeentenaam") or "").strip(),
        "municipality_code": gemeente_code,
        "street": (doc.get("straatnaam") or "").strip(),
        "latitude": lat,
        "longitude": lon,
    }
