import math

from odoo import api, fields, models


def xeno_orr_haversine(lat1, lon1, lat2, lon2):
    """Distance in meters between two GPS points -- same formula as
    xeno_attendance's xeno.attendance.location._xeno_haversine. Kept as a
    free function here (not a shared import) so this module has no hard
    dependency on xeno_attendance for one small, self-contained formula."""
    earth_radius = 6371000
    d_lat = math.radians(lat2 - lat1)
    d_lon = math.radians(lon2 - lon1)
    a = (math.sin(d_lat / 2) ** 2
         + math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) * math.sin(d_lon / 2) ** 2)
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
    return earth_radius * c


class XenoOrrStop(models.Model):
    """One Place & Purpose stop on an Outside Request's itinerary.
    latitude/longitude are filled by the GPS trip-stops dialog (browser
    geolocation, "Record GPS" per stop) -- left blank means this stop is
    relying on the request's manual distance/map-link fallback instead."""

    _name = "xeno.orr.stop"
    _description = "Outside Request Stop"
    _order = "request_id, sequence, id"

    request_id = fields.Many2one("xeno.orr.request", required=True, ondelete="cascade", index=True)
    sequence = fields.Integer(default=10)
    place = fields.Char(required=True)
    purpose = fields.Char(required=True)
    latitude = fields.Float(digits=(10, 7))
    longitude = fields.Float(digits=(10, 7))
    recorded_at = fields.Datetime(readonly=True)
    gps_captured = fields.Boolean(compute="_compute_gps_captured", store=True)

    @api.depends("latitude", "longitude")
    def _compute_gps_captured(self):
        for rec in self:
            rec.gps_captured = bool(rec.latitude or rec.longitude)

    def action_xeno_record_gps(self, latitude, longitude):
        self.ensure_one()
        self.write({
            "latitude": latitude,
            "longitude": longitude,
            "recorded_at": fields.Datetime.now(),
        })

    @api.model
    def xeno_compute_route_distance_km(self, stops):
        """Chains consecutive GPS-recorded stops (in sequence order) into a
        total route distance. Stops without a recorded GPS point are
        skipped when picking the "previous point" for the next segment,
        so one missed stop doesn't zero out the whole route -- it just
        means that one leg isn't measured."""
        points = [(s.latitude, s.longitude) for s in stops.sorted("sequence") if s.gps_captured]
        if len(points) < 2:
            return 0.0
        total_m = 0.0
        for (lat1, lon1), (lat2, lon2) in zip(points, points[1:]):
            total_m += xeno_orr_haversine(lat1, lon1, lat2, lon2)
        return total_m / 1000.0
