"""Utility helpers: geo distance calculations & duplicate-listing detection."""

import math
from difflib import SequenceMatcher

from core.models import Listing, PotentialDuplicate


def haversine_km(lat1, lon1, lat2, lon2):
    """Great-circle distance between two points in kilometers."""
    if None in (lat1, lon1, lat2, lon2):
        return None
    lat1, lon1, lat2, lon2 = map(float, (lat1, lon1, lat2, lon2))
    r = 6371.0
    phi1, phi2 = math.radians(lat1), math.radians(lat2)
    d_phi = math.radians(lat2 - lat1)
    d_lambda = math.radians(lon2 - lon1)
    a = math.sin(d_phi / 2) ** 2 + math.cos(phi1) * math.cos(phi2) * math.sin(d_lambda / 2) ** 2
    return 2 * r * math.asin(math.sqrt(a))


def title_similarity(a, b):
    return SequenceMatcher(None, a.lower().strip(), b.lower().strip()).ratio()


def scan_for_duplicates(listing, title_threshold=0.82):
    """
    Flags near-identical listings: same coordinate fingerprint (~11m precision)
    plus a similar title. Cheap heuristic, good enough to surface likely spam
    for a moderator to review — not a hard block.
    """
    if not listing.location_fingerprint:
        return []

    candidates = Listing.objects.filter(
        location_fingerprint=listing.location_fingerprint,
    ).exclude(pk=listing.pk)

    flagged = []
    for other in candidates:
        similarity = title_similarity(listing.title, other.title)
        if similarity >= title_threshold:
            pair = tuple(sorted([listing.pk, other.pk]))
            dup, _ = PotentialDuplicate.objects.get_or_create(
                listing_a_id=pair[0],
                listing_b_id=pair[1],
                defaults={
                    "similarity_reason": f"Same location, title similarity {similarity:.0%}",
                },
            )
            flagged.append(dup)
    return flagged
