"""
GPS-based geographic boundary enforcement.

Define a geographic boundary. If the device leaves it, trigger an action.
Requires GPS hardware or location services.
"""

from __future__ import annotations

import json
import logging
import math
from dataclasses import dataclass
from pathlib import Path

logger = logging.getLogger(__name__)


@dataclass
class GeoCircle:
    """A circular geofence defined by center + radius in meters."""

    lat: float
    lon: float
    radius_m: float
    name: str = "default"


def haversine_distance(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
    """Distance in meters between two lat/lon points."""
    earth_r = 6371000  # Earth radius in meters
    phi1 = math.radians(lat1)
    phi2 = math.radians(lat2)
    dphi = math.radians(lat2 - lat1)
    dlambda = math.radians(lon2 - lon1)

    a = math.sin(dphi / 2) ** 2 + math.cos(phi1) * math.cos(phi2) * math.sin(dlambda / 2) ** 2
    return earth_r * 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))


def is_inside(fence: GeoCircle, lat: float, lon: float) -> bool:
    """Check if a point is inside the geofence."""
    return haversine_distance(fence.lat, fence.lon, lat, lon) <= fence.radius_m


def load_fence(path: Path | None = None) -> GeoCircle | None:
    """Load a saved geofence from disk. Returns None if no fence is configured."""
    from soosef.paths import GEOFENCE_CONFIG

    fence_path = path or GEOFENCE_CONFIG
    if not fence_path.exists():
        return None
    with open(fence_path) as f:
        data = json.load(f)
    return GeoCircle(
        lat=data["lat"],
        lon=data["lon"],
        radius_m=data["radius_m"],
        name=data.get("name", "default"),
    )


def save_fence(fence: GeoCircle, path: Path | None = None) -> None:
    """Persist a geofence to disk."""
    from soosef.paths import GEOFENCE_CONFIG

    fence_path = path or GEOFENCE_CONFIG
    fence_path.parent.mkdir(parents=True, exist_ok=True)
    with open(fence_path, "w") as f:
        json.dump(
            {
                "lat": fence.lat,
                "lon": fence.lon,
                "radius_m": fence.radius_m,
                "name": fence.name,
            },
            f,
            indent=2,
        )


def get_current_location(timeout: float = 5.0) -> tuple[float, float] | None:
    """Try to get current GPS coordinates from gpsd.

    Returns (lat, lon) or None if unavailable.
    """
    try:
        import socket

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(timeout)
        sock.connect(("127.0.0.1", 2947))
        sock.sendall(b'?WATCH={"enable":true,"json":true}\n')

        # Read until we get a TPV (time-position-velocity) report
        buffer = b""
        while True:
            data = sock.recv(4096)
            if not data:
                break
            buffer += data
            for line in buffer.split(b"\n"):
                line = line.strip()
                if not line:
                    continue
                try:
                    report = json.loads(line)
                    if report.get("class") == "TPV" and "lat" in report and "lon" in report:
                        sock.close()
                        return (report["lat"], report["lon"])
                except (json.JSONDecodeError, KeyError):
                    continue
        sock.close()
    except (OSError, TimeoutError):
        pass
    return None


def clear_fence(path: Path | None = None) -> bool:
    """Remove the saved geofence. Returns True if a fence was present and removed."""
    from soosef.paths import GEOFENCE_CONFIG

    fence_path = path or GEOFENCE_CONFIG
    if fence_path.exists():
        fence_path.unlink()
        return True
    return False
