from __future__ import annotations

import hashlib
from typing import Any

from app.config import CACHE_TTL_GEOCODE, NOMINATIM_BASE_URL, NOMINATIM_USER_AGENT
from app.http_client import get_client
from app.database import get_cached_json, set_cached_json


def _query_hash(query: str) -> str:
    return hashlib.sha256(query.strip().lower().encode("utf-8")).hexdigest()


async def _nominatim_get(path: str, params: dict[str, Any]) -> Any:
    headers = {"User-Agent": NOMINATIM_USER_AGENT, "Accept-Language": "de"}
    client = get_client()
    response = await client.get(f"{NOMINATIM_BASE_URL}{path}", params=params, headers=headers, timeout=12.0)
    response.raise_for_status()
    return response.json()


async def search_places(query: str, limit: int = 8) -> list[dict[str, Any]]:
    if len(query.strip()) < 2:
        return []

    qhash = _query_hash(f"search:{query}:{limit}")
    cached = await get_cached_json("geocode_cache", {"query_hash": qhash}, CACHE_TTL_GEOCODE)
    if cached is not None:
        return cached

    raw = await _nominatim_get(
        "/search",
        {
            "q": query,
            "format": "jsonv2",
            "addressdetails": 1,
            "limit": limit,
            "countrycodes": "de,at,ch",
        },
    )
    results = []
    for item in raw:
        address = item.get("address") or {}
        label = item.get("display_name", "")
        place = (
            address.get("city")
            or address.get("town")
            or address.get("village")
            or address.get("municipality")
            or address.get("county")
            or label.split(",")[0]
        )
        results.append(
            {
                "label": label,
                "place": place,
                "lat": float(item["lat"]),
                "lon": float(item["lon"]),
                "type": item.get("type"),
                "postcode": address.get("postcode"),
                "county": address.get("county"),
                "state": address.get("state"),
            }
        )

    await set_cached_json(
        "geocode_cache",
        {"query_hash": qhash, "query_text": query},
        results,
    )
    return results


async def reverse_geocode(lat: float, lon: float) -> dict[str, Any]:
    qhash = _query_hash(f"reverse:{lat:.4f}:{lon:.4f}")
    cached = await get_cached_json("geocode_cache", {"query_hash": qhash}, CACHE_TTL_GEOCODE)
    if cached is not None:
        return cached

    raw = await _nominatim_get(
        "/reverse",
        {"lat": lat, "lon": lon, "format": "jsonv2", "addressdetails": 1, "zoom": 10},
    )
    address = raw.get("address") or {}
    payload = {
        "label": raw.get("display_name"),
        "place": address.get("city")
        or address.get("town")
        or address.get("village")
        or address.get("municipality")
        or address.get("county"),
        "county": address.get("county"),
        "state": address.get("state"),
        "postcode": address.get("postcode"),
        "admin_names": [
            value
            for value in [
                address.get("city"),
                address.get("town"),
                address.get("village"),
                address.get("municipality"),
                address.get("county"),
                address.get("state"),
            ]
            if value
        ],
    }
    await set_cached_json(
        "geocode_cache",
        {"query_hash": qhash, "query_text": f"{lat},{lon}"},
        payload,
    )
    return payload
