"""BAN YARO — Gassi-Treffen"""

import math
import httpx
from datetime import date
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from typing import Optional, List
from database import db
from auth import get_current_user
from routes.push import send_push_to_user

router = APIRouter()


def _haversine(lat1, lon1, lat2, lon2):
    R  = 6_371_000
    p1, p2 = math.radians(lat1), math.radians(lat2)
    dp = math.radians(lat2 - lat1)
    dl = math.radians(lon2 - lon1)
    a  = math.sin(dp / 2) ** 2 + math.cos(p1) * math.cos(p2) * math.sin(dl / 2) ** 2
    return 2 * R * math.asin(math.sqrt(a))


def _haversine_km(lat1, lon1, lat2, lon2):
    return _haversine(lat1, lon1, lat2, lon2) / 1000


# ------------------------------------------------------------------
# Schemas
# ------------------------------------------------------------------
class WalkCreate(BaseModel):
    titel:          str
    datum:          str           # YYYY-MM-DD
    uhrzeit:        str           # HH:MM
    lat:            float
    lon:            float
    ort_name:       Optional[str] = None
    max_teilnehmer: int           = 10
    beschreibung:   Optional[str] = None

class WalkUpdate(BaseModel):
    titel:          Optional[str] = None
    datum:          Optional[str] = None
    uhrzeit:        Optional[str] = None
    lat:            Optional[float] = None
    lon:            Optional[float] = None
    ort_name:       Optional[str] = None
    max_teilnehmer: Optional[int] = None
    beschreibung:   Optional[str] = None

class JoinRequest(BaseModel):
    dog_ids: List[int] = []   # leere Liste = ohne Hund (selten)

class InviteRequest(BaseModel):
    friend_id: int

class RsvpRequest(BaseModel):
    status: str  # 'yes' | 'maybe' | 'no'


# ------------------------------------------------------------------
# GET /api/walks — alle offenen Treffen (ab heute, optional Umkreis)
# ------------------------------------------------------------------
@router.get("")
async def list_walks(
    lat:    Optional[float] = None,
    lon:    Optional[float] = None,
    radius: int             = 20000,
    alle:   bool            = False,   # True → auch vergangene / stornierte
):
    today = date.today().isoformat()
    with db() as conn:
        q = """
            SELECT w.*,
                   u.name AS veranstalter_name,
                   COUNT(DISTINCT wp.user_id) AS teilnehmer_count
            FROM walks w
            LEFT JOIN users u ON u.id = w.user_id
            LEFT JOIN walk_participants wp ON wp.walk_id = w.id
            WHERE w.status != 'storniert'
        """
        if not alle:
            q += f" AND w.datum >= '{today}'"
        q += " GROUP BY w.id ORDER BY w.datum ASC, w.uhrzeit ASC"
        rows = conn.execute(q).fetchall()

    result = [dict(r) for r in rows]

    # Umkreis-Filter
    if lat is not None and lon is not None:
        result = [r for r in result if _haversine(lat, lon, r['lat'], r['lon']) <= radius]

    return result


# ------------------------------------------------------------------
# POST /api/walks — Treffen erstellen
# ------------------------------------------------------------------
@router.post("", status_code=201)
async def create_walk(data: WalkCreate, user=Depends(get_current_user)):
    with db() as conn:
        cur = conn.execute("""
            INSERT INTO walks (user_id, titel, datum, uhrzeit, lat, lon,
                               ort_name, max_teilnehmer, beschreibung)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
        """, (user['id'], data.titel, data.datum, data.uhrzeit,
              data.lat, data.lon, data.ort_name,
              data.max_teilnehmer, data.beschreibung))
        row = conn.execute(
            "SELECT w.*, u.name AS veranstalter_name FROM walks w "
            "LEFT JOIN users u ON u.id = w.user_id WHERE w.id = ?",
            (cur.lastrowid,)
        ).fetchone()
    return {**dict(row), 'teilnehmer_count': 0}


# ------------------------------------------------------------------
# GET /api/walks/nearby — POI-Suche für Treffpunkt-Autocomplete
# WICHTIG: Muss VOR /{walk_id} stehen (FastAPI Route-Reihenfolge)
# ------------------------------------------------------------------
@router.get("/nearby")
async def nearby_places(lat: float, lon: float, user=Depends(get_current_user)):
    results = []

    with db() as conn:
        # 1. User-eigene Places
        places = conn.execute(
            "SELECT name, typ, lat, lon FROM places WHERE lat IS NOT NULL",
        ).fetchall()
        for p in places:
            km = _haversine_km(lat, lon, p["lat"], p["lon"])
            if km <= 5:
                results.append({"name": p["name"], "type": p["typ"] or "place",
                                 "lat": p["lat"], "lon": p["lon"],
                                 "distance_m": int(km * 1000), "source": "places"})

        # 2. Gecachte OSM-POIs
        osm = conn.execute(
            "SELECT name, type, lat, lon FROM osm_pois WHERE name IS NOT NULL AND name != ''"
        ).fetchall()
        for p in osm:
            km = _haversine_km(lat, lon, p["lat"], p["lon"])
            if km <= 2:
                results.append({"name": p["name"], "type": p["type"],
                                 "lat": p["lat"], "lon": p["lon"],
                                 "distance_m": int(km * 1000), "source": "osm"})

    # 3. Overpass: benannte POIs in 1000m
    try:
        async with httpx.AsyncClient(timeout=6) as client:
            q = (
                f'[out:json][timeout:6];'
                f'(node["name"]["leisure"](around:1000,{lat},{lon});'
                f' node["name"]["amenity"](around:1000,{lat},{lon});'
                f' node["name"]["tourism"](around:1000,{lat},{lon});'
                f' way["name"]["leisure"](around:1000,{lat},{lon});'
                f');out center;'
            )
            r = await client.post("https://overpass-api.de/api/interpreter",
                                  data={"data": q})
            if r.status_code == 200:
                for el in r.json().get("elements", []):
                    name = el.get("tags", {}).get("name")
                    if not name:
                        continue
                    elat = el.get("lat") or el.get("center", {}).get("lat")
                    elon = el.get("lon") or el.get("center", {}).get("lon")
                    if elat is None or elon is None:
                        continue
                    km = _haversine_km(lat, lon, elat, elon)
                    if km <= 1:
                        results.append({"name": name, "type": "osm",
                                         "lat": elat, "lon": elon,
                                         "distance_m": int(km * 1000), "source": "osm"})
    except Exception:
        pass

    # Deduplizieren nach Name + Sortieren nach Distanz
    seen = set()
    unique = []
    for r in sorted(results, key=lambda x: x["distance_m"]):
        key = r["name"].lower()
        if key not in seen:
            seen.add(key)
            unique.append(r)

    return unique[:20]


# ------------------------------------------------------------------
# GET /api/walks/{id}/invite-candidates — einladbare Freunde
# WICHTIG: Muss VOR /{walk_id} stehen
# ------------------------------------------------------------------
@router.get("/{walk_id}/invite-candidates")
async def invite_candidates(walk_id: int, user=Depends(get_current_user)):
    with db() as conn:
        walk = conn.execute("SELECT * FROM walks WHERE id=?", (walk_id,)).fetchone()
        if not walk:
            raise HTTPException(404, "Treffen nicht gefunden.")
        if walk['user_id'] != user['id']:
            raise HTTPException(403, "Nur der Veranstalter kann diese Liste abrufen.")

        # Freunde die noch nicht eingeladen wurden
        already = {r[0] for r in conn.execute(
            "SELECT user_id FROM walk_invitations WHERE walk_id=?", (walk_id,)
        ).fetchall()}

    friends = _get_accepted_friends(user['id'])
    return [f for f in friends if f['friend_id'] not in already]


# ------------------------------------------------------------------
# POST /api/walks/{id}/invite — Freund einladen (nur Veranstalter)
# WICHTIG: Muss VOR /{walk_id} stehen
# ------------------------------------------------------------------
@router.post("/{walk_id}/invite", status_code=201)
async def invite_friend(walk_id: int, data: InviteRequest, user=Depends(get_current_user)):
    with db() as conn:
        walk = conn.execute("SELECT * FROM walks WHERE id = ?", (walk_id,)).fetchone()
        if not walk:
            raise HTTPException(404, "Treffen nicht gefunden.")
        if walk['user_id'] != user['id']:
            raise HTTPException(403, "Nur der Veranstalter kann einladen.")

        # Freundschaft prüfen
        friendship = conn.execute("""
            SELECT 1 FROM friendships
            WHERE status='accepted'
              AND ((requester_id=? AND addressee_id=?) OR (requester_id=? AND addressee_id=?))
        """, (user['id'], data.friend_id, data.friend_id, user['id'])).fetchone()
        if not friendship:
            raise HTTPException(400, "Dieser Nutzer ist nicht in deiner Freundesliste.")

        # Bereits eingeladen?
        existing = conn.execute(
            "SELECT status FROM walk_invitations WHERE walk_id=? AND user_id=?",
            (walk_id, data.friend_id)
        ).fetchone()
        if existing:
            raise HTTPException(409, f"Nutzer wurde bereits eingeladen (Status: {existing['status']}).")

        conn.execute(
            "INSERT INTO walk_invitations (walk_id, user_id, status) VALUES (?, ?, 'invited')",
            (walk_id, data.friend_id)
        )

        friend = conn.execute("SELECT name FROM users WHERE id=?", (data.friend_id,)).fetchone()

    # Push-Notification
    send_push_to_user(data.friend_id, {
        "type":    "walk_invite",
        "title":   f"Einladung: {walk['titel']}",
        "body":    f"{user['name']} lädt dich zu einem Gassi-Treffen ein ({walk['datum']} {walk['uhrzeit']})",
        "page":    "walks",
        "walk_id": walk_id,
    })

    return {"status": "invited", "friend_name": friend['name'] if friend else ""}


# ------------------------------------------------------------------
# POST /api/walks/{id}/rsvp — RSVP setzen (yes / maybe / no)
# WICHTIG: Muss VOR /{walk_id} stehen
# ------------------------------------------------------------------
@router.post("/{walk_id}/rsvp")
async def rsvp_walk(walk_id: int, data: RsvpRequest, user=Depends(get_current_user)):
    if data.status not in ('yes', 'maybe', 'no'):
        raise HTTPException(400, "Ungültiger RSVP-Status. Erlaubt: yes, maybe, no")

    with db() as conn:
        walk = conn.execute("SELECT * FROM walks WHERE id=?", (walk_id,)).fetchone()
        if not walk:
            raise HTTPException(404, "Treffen nicht gefunden.")

        inv = conn.execute(
            "SELECT * FROM walk_invitations WHERE walk_id=? AND user_id=?",
            (walk_id, user['id'])
        ).fetchone()
        if not inv:
            raise HTTPException(403, "Du wurdest nicht zu diesem Treffen eingeladen.")

        conn.execute(
            """UPDATE walk_invitations
               SET status=?, responded_at=datetime('now')
               WHERE walk_id=? AND user_id=?""",
            (data.status, walk_id, user['id'])
        )

    return {"status": data.status}


# ------------------------------------------------------------------
# GET /api/walks/{id}/participants — Teilnehmerliste mit RSVP
# WICHTIG: Muss VOR /{walk_id} stehen
# ------------------------------------------------------------------
@router.get("/{walk_id}/participants")
async def get_participants(walk_id: int, user=Depends(get_current_user)):
    with db() as conn:
        walk = conn.execute("SELECT * FROM walks WHERE id=?", (walk_id,)).fetchone()
        if not walk:
            raise HTTPException(404, "Treffen nicht gefunden.")

        is_organizer = walk['user_id'] == user['id']

        if is_organizer:
            # Veranstalter sieht alle Einladungen
            invitations = conn.execute("""
                SELECT wi.user_id, wi.status, wi.invited_at, wi.responded_at,
                       u.name AS user_name,
                       GROUP_CONCAT(d.name, ', ') AS hunde
                FROM walk_invitations wi
                JOIN users u ON u.id = wi.user_id
                LEFT JOIN walk_participant_dogs wpd
                       ON wpd.walk_id = wi.walk_id AND wpd.user_id = wi.user_id
                LEFT JOIN dogs d ON d.id = wpd.dog_id
                WHERE wi.walk_id = ?
                GROUP BY wi.user_id
            """, (walk_id,)).fetchall()
        else:
            # Eingeladene sehen nur Zugesagte + sich selbst
            invitations = conn.execute("""
                SELECT wi.user_id, wi.status, wi.invited_at, wi.responded_at,
                       u.name AS user_name,
                       GROUP_CONCAT(d.name, ', ') AS hunde
                FROM walk_invitations wi
                JOIN users u ON u.id = wi.user_id
                LEFT JOIN walk_participant_dogs wpd
                       ON wpd.walk_id = wi.walk_id AND wpd.user_id = wi.user_id
                LEFT JOIN dogs d ON d.id = wpd.dog_id
                WHERE wi.walk_id = ? AND (wi.status = 'yes' OR wi.user_id = ?)
                GROUP BY wi.user_id
            """, (walk_id, user['id'])).fetchall()

        my_invitation = conn.execute(
            "SELECT status FROM walk_invitations WHERE walk_id=? AND user_id=?",
            (walk_id, user['id'])
        ).fetchone()

    return {
        "invitations":    [dict(r) for r in invitations],
        "my_rsvp":        my_invitation['status'] if my_invitation else None,
        "is_organizer":   is_organizer,
    }


# ------------------------------------------------------------------
# GET /api/walks/{id} — Detail mit Teilnehmerliste
# ------------------------------------------------------------------
@router.get("/{walk_id}")
async def get_walk(walk_id: int):
    with db() as conn:
        walk = conn.execute(
            "SELECT w.*, u.name AS veranstalter_name FROM walks w "
            "LEFT JOIN users u ON u.id = w.user_id WHERE w.id = ?",
            (walk_id,)
        ).fetchone()
        if not walk:
            raise HTTPException(404, "Treffen nicht gefunden.")

        # Teilnehmer mit Hunden
        participants = conn.execute("""
            SELECT wp.user_id, u.name AS user_name,
                   GROUP_CONCAT(d.name, ', ') AS hunde
            FROM walk_participants wp
            JOIN users u ON u.id = wp.user_id
            LEFT JOIN walk_participant_dogs wpd
                   ON wpd.walk_id = wp.walk_id AND wpd.user_id = wp.user_id
            LEFT JOIN dogs d ON d.id = wpd.dog_id
            WHERE wp.walk_id = ?
            GROUP BY wp.user_id
        """, (walk_id,)).fetchall()

    result = dict(walk)
    result['teilnehmer'] = [dict(p) for p in participants]
    result['teilnehmer_count'] = len(result['teilnehmer'])
    return result


# Helper: accepted friends list for invite-modal (reused from friends route)
def _get_accepted_friends(user_id: int):
    with db() as conn:
        rows = conn.execute("""
            SELECT CASE WHEN f.requester_id=? THEN f.addressee_id ELSE f.requester_id END AS friend_id,
                   u.name AS friend_name
            FROM friendships f
            JOIN users u ON u.id = CASE WHEN f.requester_id=? THEN f.addressee_id ELSE f.requester_id END
            WHERE (f.requester_id=? OR f.addressee_id=?) AND f.status='accepted'
            ORDER BY u.name
        """, (user_id, user_id, user_id, user_id)).fetchall()
    return [dict(r) for r in rows]


# ------------------------------------------------------------------
# PATCH /api/walks/{id}
# ------------------------------------------------------------------
@router.patch("/{walk_id}")
async def update_walk(walk_id: int, data: WalkUpdate, user=Depends(get_current_user)):
    with db() as conn:
        walk = conn.execute("SELECT * FROM walks WHERE id = ?", (walk_id,)).fetchone()
        if not walk:
            raise HTTPException(404, "Treffen nicht gefunden.")
        if walk['user_id'] != user['id']:
            raise HTTPException(403, "Nur der Veranstalter kann das Treffen bearbeiten.")

        updates = data.model_dump(exclude_none=True)
        if updates:
            cols = ', '.join(f"{k} = ?" for k in updates)
            conn.execute(f"UPDATE walks SET {cols} WHERE id = ?", [*updates.values(), walk_id])

        row = conn.execute(
            "SELECT w.*, u.name AS veranstalter_name FROM walks w "
            "LEFT JOIN users u ON u.id = w.user_id WHERE w.id = ?",
            (walk_id,)
        ).fetchone()
        count = conn.execute(
            "SELECT COUNT(*) FROM walk_participants WHERE walk_id = ?", (walk_id,)
        ).fetchone()[0]
    return {**dict(row), 'teilnehmer_count': count}


# ------------------------------------------------------------------
# DELETE /api/walks/{id} — stornieren
# ------------------------------------------------------------------
@router.delete("/{walk_id}", status_code=204)
async def cancel_walk(walk_id: int, user=Depends(get_current_user)):
    with db() as conn:
        walk = conn.execute("SELECT * FROM walks WHERE id = ?", (walk_id,)).fetchone()
        if not walk:
            raise HTTPException(404, "Treffen nicht gefunden.")
        if walk['user_id'] != user['id']:
            raise HTTPException(403, "Nur der Veranstalter kann das Treffen stornieren.")
        conn.execute("UPDATE walks SET status = 'storniert' WHERE id = ?", (walk_id,))


# ------------------------------------------------------------------
# POST /api/walks/{id}/join — beitreten
# ------------------------------------------------------------------
@router.post("/{walk_id}/join")
async def join_walk(walk_id: int, data: JoinRequest, user=Depends(get_current_user)):
    with db() as conn:
        walk = conn.execute("SELECT * FROM walks WHERE id = ?", (walk_id,)).fetchone()
        if not walk:
            raise HTTPException(404, "Treffen nicht gefunden.")
        if walk['status'] != 'offen':
            raise HTTPException(400, "Dieses Treffen ist nicht mehr offen.")

        # Bereits beigetreten?
        existing = conn.execute(
            "SELECT 1 FROM walk_participants WHERE walk_id = ? AND user_id = ?",
            (walk_id, user['id'])
        ).fetchone()
        if existing:
            raise HTTPException(409, "Du nimmst bereits teil.")

        # Platz frei?
        count = conn.execute(
            "SELECT COUNT(*) FROM walk_participants WHERE walk_id = ?", (walk_id,)
        ).fetchone()[0]
        if count >= walk['max_teilnehmer']:
            raise HTTPException(400, "Das Treffen ist bereits voll.")

        # Beitreten
        primary_dog = data.dog_ids[0] if data.dog_ids else None
        conn.execute(
            "INSERT INTO walk_participants (walk_id, user_id, dog_id) VALUES (?, ?, ?)",
            (walk_id, user['id'], primary_dog)
        )
        # Hunde eintragen
        for dog_id in data.dog_ids:
            conn.execute(
                "INSERT OR IGNORE INTO walk_participant_dogs (walk_id, user_id, dog_id) VALUES (?, ?, ?)",
                (walk_id, user['id'], dog_id)
            )

        new_count = count + 1
        if new_count >= walk['max_teilnehmer']:
            conn.execute("UPDATE walks SET status = 'voll' WHERE id = ?", (walk_id,))

    return {"status": "joined", "teilnehmer_count": new_count}


# ------------------------------------------------------------------
# DELETE /api/walks/{id}/join — verlassen
# ------------------------------------------------------------------
@router.delete("/{walk_id}/join", status_code=200)
async def leave_walk(walk_id: int, user=Depends(get_current_user)):
    with db() as conn:
        walk = conn.execute("SELECT * FROM walks WHERE id = ?", (walk_id,)).fetchone()
        if not walk:
            raise HTTPException(404, "Treffen nicht gefunden.")
        if walk['user_id'] == user['id']:
            raise HTTPException(400, "Als Veranstalter kannst du nicht austreten — storniere das Treffen stattdessen.")

        conn.execute(
            "DELETE FROM walk_participants WHERE walk_id = ? AND user_id = ?",
            (walk_id, user['id'])
        )
        conn.execute(
            "DELETE FROM walk_participant_dogs WHERE walk_id = ? AND user_id = ?",
            (walk_id, user['id'])
        )
        # Status ggf. wieder auf offen setzen
        count = conn.execute(
            "SELECT COUNT(*) FROM walk_participants WHERE walk_id = ?", (walk_id,)
        ).fetchone()[0]
        if walk['status'] == 'voll':
            conn.execute("UPDATE walks SET status = 'offen' WHERE id = ?", (walk_id,))

    return {"status": "left", "teilnehmer_count": count}
