"""
Yoro Django services — encode GPS, create/retrieve codes, assign to models.

Usage::

    from yoro.django.services import assign_yoro, get_or_create_yoro

    # Standalone
    obj = get_or_create_yoro(6.8, -5.3, domain="CI")

    # On a model instance (modifies in place, does NOT save)
    assign_yoro(producer_instance, domain="CI")
"""

from __future__ import annotations

from django.contrib.gis.geos import Point, Polygon

from yoro import codec
from yoro.django.models import GeoAltiusCode


def get_or_create_yoro(
    lat: float,
    lon: float,
    precision: int = 12,
    domain: str = "CI",
) -> GeoAltiusCode:
    """Encode GPS coordinates and get or create a ``GeoAltiusCode`` record."""
    code_str = codec.encode(lat, lon, precision=precision, domain=domain)
    decoded = codec.decode(code_str)
    bounds = decoded["bounds"]
    resolution_m = codec.resolution(decoded["precision"], domain=domain)

    location = Point(lon, lat, srid=4326)
    cell = Polygon.from_bbox((
        bounds["lon_min"],
        bounds["lat_min"],
        bounds["lon_max"],
        bounds["lat_max"],
    ))
    cell.srid = 4326

    obj, _created = GeoAltiusCode.objects.get_or_create(
        code=code_str,
        defaults={
            "domain": domain,
            "location": location,
            "cell": cell,
            "precision": decoded["precision"],
            "resolution_m": resolution_m,
        },
    )
    return obj


def assign_yoro(instance: object, domain: str = "CI", precision: int = 12) -> GeoAltiusCode | None:
    """Assign an Altius Code to any model instance with ``location`` and ``yoro`` fields.

    Modifies the instance in place but does **not** save it.
    """
    location = getattr(instance, "location", None)
    if not location:
        return None

    lat = location.y
    lon = location.x
    obj = get_or_create_yoro(lat, lon, precision=precision, domain=domain)
    instance.yoro = obj  # type: ignore[attr-defined]
    return obj
