import pghistory
from django.contrib.gis.db import models
from django.contrib.gis.geos import Point


@pghistory.track()
class ParkLocation(models.Model):
    """
    Represents the geographic location and address of a park, with PostGIS support.
    """

    park = models.OneToOneField("parks.Park", on_delete=models.CASCADE, related_name="location")

    # Spatial Data
    point = models.PointField(
        srid=4326,
        null=True,
        blank=True,
        help_text="Geographic coordinates (longitude, latitude)",
    )

    # Address Fields
    street_address = models.CharField(max_length=255, blank=True)
    city = models.CharField(max_length=100, db_index=True)
    state = models.CharField(max_length=100, db_index=True)
    country = models.CharField(max_length=100, default="USA")
    continent = models.CharField(
        max_length=50, blank=True, db_index=True, help_text="Continent where the park is located"
    )
    postal_code = models.CharField(max_length=20, blank=True)

    # Road Trip Metadata
    highway_exit = models.CharField(max_length=100, blank=True)
    parking_notes = models.TextField(blank=True)
    best_arrival_time = models.TimeField(null=True, blank=True)
    seasonal_notes = models.TextField(blank=True)

    # OSM Integration
    osm_id = models.BigIntegerField(null=True, blank=True)
    osm_type = models.CharField(
        max_length=10,
        blank=True,
        help_text="Type of OpenStreetMap object (node, way, or relation)",
    )

    @property
    def latitude(self):
        """Return latitude from point field."""
        if self.point:
            return self.point.y
        return None

    @property
    def longitude(self):
        """Return longitude from point field."""
        if self.point:
            return self.point.x
        return None

    @property
    def coordinates(self):
        """Return (latitude, longitude) tuple."""
        if self.point:
            return (self.latitude, self.longitude)
        return (None, None)

    @property
    def formatted_address(self):
        """Return a nicely formatted address string."""
        address_parts = [
            self.street_address,
            self.city,
            self.state,
            self.postal_code,
            self.country,
        ]
        return ", ".join(part for part in address_parts if part)

    def set_coordinates(self, latitude, longitude):
        """
        Set the location's point from latitude and longitude coordinates.
        Validates coordinate ranges.
        """
        if latitude is None or longitude is None:
            self.point = None
            return

        if not -90 <= latitude <= 90:
            raise ValueError("Latitude must be between -90 and 90.")
        if not -180 <= longitude <= 180:
            raise ValueError("Longitude must be between -180 and 180.")

        self.point = Point(longitude, latitude, srid=4326)

    def distance_to(self, other_location):
        """
        Calculate the distance to another ParkLocation instance.
        Returns distance in kilometers.
        """
        if not self.point or not other_location.point:
            return None
        # Use geodetic distance calculation which returns meters, convert to km
        distance_m = self.point.distance(other_location.point)
        return distance_m / 1000.0

    def __str__(self):
        return f"Location for {self.park.name}"

    class Meta:
        verbose_name = "Park Location"
        verbose_name_plural = "Park Locations"
        ordering = ["park__name"]
        indexes = [
            models.Index(fields=["city", "state"]),
        ]
