"""
BAN YARO — Adoption (Tierheim-Hunde in der Nähe)

Strategie:
  1. PetFinder API (falls API-Key gesetzt) → hat kaum deutsche Tierheime, nur als Bonus
  2. Statische Daten: Liste großer deutscher Tierheime mit Koordinaten
  3. Fallback: Weiterleitung zu tierheimhelden.de

Caching: adoption_cache Tabelle, 24h TTL.
"""

import os
import math
import logging
import asyncio
import uuid
import httpx
from datetime   import datetime, timedelta
from fastapi    import APIRouter, Query, BackgroundTasks, Depends, Form, UploadFile, File, HTTPException
from pydantic   import BaseModel
from typing     import Optional
from database   import db
from auth       import get_current_user
from routes.push import send_push_to_user

MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media")

logger = logging.getLogger(__name__)
router = APIRouter()

PETFINDER_KEY    = os.getenv("PETFINDER_API_KEY", "")
PETFINDER_SECRET = os.getenv("PETFINDER_API_SECRET", "")

# ------------------------------------------------------------------
# Haversine — Distanz in km
# ------------------------------------------------------------------
def _haversine(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
    R  = 6371.0
    p1 = math.radians(lat1)
    p2 = 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))


# ------------------------------------------------------------------
# Statische Tierheim-Daten (große deutsche Tierheime)
# ------------------------------------------------------------------
GERMAN_SHELTERS = [
    # (id, name, plz, stadt, lat, lon, url)
    ("de_berlin_tierschutz",    "Tierheim Berlin",                      "12459", "Berlin",          52.4862,  13.5301, "https://www.tierheim-berlin.de/tiere/hunde/"),
    ("de_hamburg_tierschutz",   "Tierheim Hamburg (Süderstraße)",       "20537", "Hamburg",         53.5539,  10.0416, "https://www.hamburger-tierschutzverein.de/hunde/"),
    ("de_muenchen_tierschutz",  "Tierheim München",                     "81379", "München",         48.0982,  11.5248, "https://www.tierheim-muenchen.de/hunde/"),
    ("de_koeln_tierschutz",     "Tierheim Köln",                        "50769", "Köln",            51.0168,   6.9369, "https://www.tierschutzverein-koeln.de/tiere/hunde/"),
    ("de_frankfurt_tierschutz", "Tierheim Frankfurt (Fechenheim)",      "60386", "Frankfurt",       50.1246,   8.7597, "https://www.tierheim-frankfurt.de/hunde/"),
    ("de_stuttgart_tierschutz", "Tierschutzverein Stuttgart",           "70193", "Stuttgart",       48.7790,   9.1634, "https://www.tierschutzverein-stuttgart.de/vermittlung/hunde/"),
    ("de_duesseldorf_tierheim",  "Tierheim Düsseldorf",                 "40599", "Düsseldorf",      51.1948,   6.8488, "https://www.tierheim-duesseldorf.de/tiere/hunde/"),
    ("de_dortmund_tierheim",    "Tierheim Dortmund",                    "44339", "Dortmund",        51.5481,   7.4584, "https://www.tierschutzverein-dortmund.de/hunde/"),
    ("de_essen_tierheim",       "Tierheim Essen",                       "45276", "Essen",           51.4341,   7.0985, "https://www.tierheim-essen.de/tiere/hunde/"),
    ("de_leipzig_tierheim",     "Tierheim Leipzig",                     "04109", "Leipzig",         51.3396,  12.3713, "https://www.tierschutzverein-leipzig.de/hunde/"),
    ("de_dresden_tierheim",     "Tierheim Dresden",                     "01127", "Dresden",         51.0789,  13.7319, "https://www.tierschutzverein-dresden-heidenau.de/tiere/hunde/"),
    ("de_hannover_tierheim",    "Tierheim Hannover",                    "30855", "Hannover",        52.3484,   9.7411, "https://www.tierschutzverein-hannover.de/hunde/"),
    ("de_nuernberg_tierheim",   "Tierschutzverein Nürnberg",            "90461", "Nürnberg",        49.4182,  11.0830, "https://www.tierschutzverein-nuernberg.de/tiere/hunde/"),
    ("de_bremen_tierheim",      "Tierheim Bremen",                      "28307", "Bremen",          53.0440,   8.9128, "https://www.tierheim-bremen.de/hunde/"),
    ("de_bochum_tierheim",      "Tierheim Bochum",                      "44793", "Bochum",          51.4753,   7.2128, "https://www.tierschutzverein-bochum.de/hunde/"),
    ("de_wuppertal_tierheim",   "Tierheim Wuppertal",                   "42283", "Wuppertal",       51.2571,   7.1705, "https://www.tierschutz-wuppertal.de/hunde/"),
    ("de_bielefeld_tierheim",   "Tierheim Bielefeld",                   "33649", "Bielefeld",       51.9951,   8.5327, "https://www.tierschutzverein-bielefeld.de/hunde/"),
    ("de_mannheim_tierheim",    "Tierheim Mannheim",                    "68309", "Mannheim",        49.5079,   8.5033, "https://www.tierschutzverein-mannheim.de/hunde/"),
    ("de_karlsruhe_tierheim",   "Tierheim Karlsruhe",                   "76229", "Karlsruhe",       48.9960,   8.4290, "https://www.tierschutzverein-karlsruhe.de/hunde/"),
    ("de_augsburg_tierheim",    "Tierheim Augsburg",                    "86159", "Augsburg",        48.3668,  10.8978, "https://www.tierschutz-augsburg.de/tiere/hunde/"),
    ("de_freiburg_tierheim",    "Tierheim Freiburg",                    "79115", "Freiburg",        47.9855,   7.8352, "https://www.tierschutz-freiburg.de/tiere/hunde/"),
    ("de_kiel_tierheim",        "Tierheim Kiel",                        "24113", "Kiel",            54.3203,  10.1228, "https://www.tierschutzverein-kiel.de/hunde/"),
    ("de_magdeburg_tierheim",   "Tierheim Magdeburg",                   "39118", "Magdeburg",       52.0814,  11.5939, "https://www.tierschutz-magdeburg.de/hunde/"),
    ("de_erfurt_tierheim",      "Tierheim Erfurt",                      "99099", "Erfurt",          50.9985,  11.0424, "https://www.tierschutzverein-erfurt.de/hunde/"),
    ("de_rostock_tierheim",     "Tierheim Rostock",                     "18059", "Rostock",         54.0831,  12.0965, "https://www.tierschutzverein-rostock.de/hunde/"),
]


# ------------------------------------------------------------------
# PetFinder OAuth2 Token
# ------------------------------------------------------------------
_pf_token      = None
_pf_token_exp  = 0.0

async def _get_pf_token() -> str | None:
    global _pf_token, _pf_token_exp
    if not (PETFINDER_KEY and PETFINDER_SECRET):
        return None
    now = asyncio.get_event_loop().time()
    if _pf_token and now < _pf_token_exp - 60:
        return _pf_token
    try:
        async with httpx.AsyncClient(timeout=8) as client:
            r = await client.post(
                "https://api.petfinder.com/v2/oauth2/token",
                data={"grant_type": "client_credentials",
                      "client_id": PETFINDER_KEY,
                      "client_secret": PETFINDER_SECRET},
            )
            if r.status_code == 200:
                data         = r.json()
                _pf_token    = data.get("access_token")
                _pf_token_exp = now + data.get("expires_in", 3600)
                return _pf_token
    except Exception as e:
        logger.warning(f"PetFinder OAuth: {e}")
    return None


# ------------------------------------------------------------------
# PetFinder: Hunde in der Nähe holen
# ------------------------------------------------------------------
async def _fetch_petfinder(lat: float, lon: float, radius: int) -> list[dict]:
    token = await _get_pf_token()
    if not token:
        return []
    try:
        async with httpx.AsyncClient(timeout=12) as client:
            r = await client.get(
                "https://api.petfinder.com/v2/animals",
                headers={"Authorization": f"Bearer {token}"},
                params={
                    "type":     "dog",
                    "location": f"{lat},{lon}",
                    "distance": radius,
                    "limit":    20,
                    "sort":     "distance",
                    "status":   "adoptable",
                },
            )
            if r.status_code != 200:
                logger.warning(f"PetFinder API: HTTP {r.status_code}")
                return []
            animals = r.json().get("animals", [])
            result  = []
            for a in animals:
                org = a.get("organization_id", "")
                loc = a.get("contact", {}).get("address", {})
                photos = a.get("photos", [])
                foto   = photos[0].get("medium") if photos else None
                age_map = {"Baby": 0.25, "Young": 1.0, "Adult": 4.0, "Senior": 9.0}
                result.append({
                    "external_id":   f"pf_{a['id']}",
                    "name":          a.get("name", "Unbekannt"),
                    "rasse":         ", ".join(
                        filter(None, [
                            a.get("breeds", {}).get("primary"),
                            a.get("breeds", {}).get("secondary"),
                        ])
                    ) or None,
                    "alter_jahre":   age_map.get(a.get("age"), None),
                    "geschlecht":    {"Male": "männlich", "Female": "weiblich"}.get(a.get("gender"), None),
                    "foto_url":      foto,
                    "tierheim":      org,
                    "tierheim_plz":  loc.get("postcode"),
                    "tierheim_lat":  None,
                    "tierheim_lon":  None,
                    "adoptions_url": a.get("url", "https://www.petfinder.com/"),
                    "quelle":        "petfinder",
                })
            return result
    except Exception as e:
        logger.warning(f"PetFinder Fetch: {e}")
        return []


# ------------------------------------------------------------------
# Cache befüllen
# ------------------------------------------------------------------
async def _refresh_cache(lat: float, lon: float, radius: int):
    """Holt frische Daten und schreibt sie in adoption_cache."""
    animals = await _fetch_petfinder(lat, lon, radius)
    if not animals:
        return
    expires = (datetime.utcnow() + timedelta(hours=24)).strftime("%Y-%m-%d %H:%M:%S")
    with db() as conn:
        for a in animals:
            try:
                conn.execute("""
                    INSERT INTO adoption_cache
                        (external_id, name, rasse, alter_jahre, geschlecht,
                         foto_url, tierheim, tierheim_plz, tierheim_lat, tierheim_lon,
                         adoptions_url, expires_at)
                    VALUES (?,?,?,?,?,?,?,?,?,?,?,?)
                    ON CONFLICT(external_id) DO UPDATE SET
                        name=excluded.name,
                        rasse=excluded.rasse,
                        alter_jahre=excluded.alter_jahre,
                        geschlecht=excluded.geschlecht,
                        foto_url=excluded.foto_url,
                        tierheim=excluded.tierheim,
                        tierheim_plz=excluded.tierheim_plz,
                        tierheim_lat=excluded.tierheim_lat,
                        tierheim_lon=excluded.tierheim_lon,
                        adoptions_url=excluded.adoptions_url,
                        expires_at=excluded.expires_at
                """, (
                    a["external_id"], a["name"], a["rasse"], a["alter_jahre"],
                    a["geschlecht"], a["foto_url"], a["tierheim"], a["tierheim_plz"],
                    a["tierheim_lat"], a["tierheim_lon"], a["adoptions_url"], expires,
                ))
            except Exception as e:
                logger.warning(f"Cache insert: {e}")


# ------------------------------------------------------------------
# GET /api/adoption/nearby
# ------------------------------------------------------------------
@router.get("/nearby")
async def adoption_nearby(
    lat:    float = Query(..., description="Breitengrad"),
    lon:    float = Query(..., description="Längengrad"),
    radius: int   = Query(50,  ge=5, le=200, description="Radius in km"),
    background_tasks: BackgroundTasks = None,
):
    """
    Gibt Adoptionshunde in der Nähe zurück.

    Priorisierung:
    1. Frische PetFinder-Einträge aus Cache
    2. Statische Tierheim-Liste (immer vorhanden, mit Entfernung)
    """
    now_str = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")

    # ------ Cache lesen ------
    cached_animals = []
    with db() as conn:
        rows = conn.execute("""
            SELECT * FROM adoption_cache
            WHERE expires_at > ?
            ORDER BY created_at DESC
        """, (now_str,)).fetchall()
        for row in rows:
            d = dict(row)
            if d.get("tierheim_lat") and d.get("tierheim_lon"):
                dist = _haversine(lat, lon, d["tierheim_lat"], d["tierheim_lon"])
                if dist <= radius:
                    d["distanz_km"] = round(dist, 1)
                    cached_animals.append(d)
            else:
                # PetFinder-Einträge ohne Koordinaten: immer anzeigen
                d["distanz_km"] = None
                cached_animals.append(d)

    # ------ Cache refreshen wenn leer oder alt ------
    if not cached_animals and background_tasks is not None:
        background_tasks.add_task(_refresh_cache, lat, lon, radius)

    # ------ Statische Tierheime (immer) ------
    shelters = []
    for sid, name, plz, stadt, slat, slon, url in GERMAN_SHELTERS:
        dist = _haversine(lat, lon, slat, slon)
        if dist <= radius:
            shelters.append({
                "id":        sid,
                "name":      name,
                "plz":       plz,
                "stadt":     stadt,
                "lat":       slat,
                "lon":       slon,
                "url":       url,
                "distanz_km": round(dist, 1),
            })

    shelters.sort(key=lambda x: x["distanz_km"])

    return {
        "animals":  cached_animals[:40],
        "shelters": shelters[:10],
        "has_petfinder": bool(PETFINDER_KEY),
    }


# ------------------------------------------------------------------
# GET /api/adoption/geocode?plz=…  — PLZ → Koordinaten via Nominatim
# ------------------------------------------------------------------
@router.get("/geocode")
async def adoption_geocode(plz: str = Query(..., min_length=4, max_length=10)):
    """Wandelt eine PLZ in Koordinaten um (via Nominatim)."""
    try:
        async with httpx.AsyncClient(timeout=8) as client:
            r = await client.get(
                "https://nominatim.openstreetmap.org/search",
                params={
                    "q":              f"{plz}, Germany",
                    "format":         "json",
                    "limit":          1,
                    "accept-language": "de",
                    "countrycodes":   "de",
                },
                headers={"User-Agent": "BanYaro/1.0 (https://banyaro.app)"},
            )
            results = r.json()
            if results:
                return {"lat": float(results[0]["lat"]), "lon": float(results[0]["lon"]), "display": results[0].get("display_name", plz)}
    except Exception as e:
        logger.warning(f"Geocode PLZ {plz}: {e}")
    return {"lat": None, "lon": None, "display": plz}


# ==================================================================
# Community Adoption — Privates Weitervermittlungs-Board
# ==================================================================

class InterestBody(BaseModel):
    nachricht: Optional[str] = None


# ------------------------------------------------------------------
# GET /api/adoption/community/my — eigene Inserate
# ------------------------------------------------------------------
@router.get("/community/my")
def community_my(user=Depends(get_current_user)):
    with db() as conn:
        rows = conn.execute("""
            SELECT ca.*,
                   u.name AS besitzer_name,
                   (SELECT COUNT(*) FROM community_adoption_interest i WHERE i.listing_id = ca.id) AS interest_count
            FROM community_adoption ca
            JOIN users u ON u.id = ca.user_id
            WHERE ca.user_id = ? AND ca.status != 'deleted'
            ORDER BY ca.created_at DESC
        """, (user["id"],)).fetchall()
    return [dict(r) for r in rows]


# ------------------------------------------------------------------
# GET /api/adoption/community — alle aktiven Inserate (mit optionaler Nähe)
# ------------------------------------------------------------------
@router.get("/community")
def community_list(
    lat: Optional[float] = Query(None),
    lon: Optional[float] = Query(None),
    radius: float = Query(200.0, description="Radius in km (default 200)"),
    user=Depends(get_current_user),
):
    with db() as conn:
        rows = conn.execute("""
            SELECT ca.*,
                   u.name AS besitzer_name,
                   (SELECT COUNT(*) FROM community_adoption_interest i WHERE i.listing_id = ca.id) AS interest_count,
                   (SELECT COUNT(*) FROM community_adoption_interest i2
                    WHERE i2.listing_id = ca.id AND i2.user_id = ?) AS _user_interested
            FROM community_adoption ca
            JOIN users u ON u.id = ca.user_id
            WHERE ca.status = 'active'
            ORDER BY ca.created_at DESC
            LIMIT 50
        """, (user["id"],)).fetchall()

    result = []
    for row in rows:
        d = dict(row)
        d["user_interested"] = bool(d.pop("_user_interested", 0))
        if lat is not None and lon is not None and d.get("lat") and d.get("lon"):
            dist = _haversine(lat, lon, d["lat"], d["lon"])
            d["distanz_km"] = round(dist, 1)
            if dist > radius:
                continue
        else:
            d["distanz_km"] = None
        result.append(d)

    if lat is not None and lon is not None:
        result.sort(key=lambda x: x["distanz_km"] if x["distanz_km"] is not None else 9999)

    return result


# ------------------------------------------------------------------
# POST /api/adoption/community — Inserat erstellen
# ------------------------------------------------------------------
@router.post("/community", status_code=201)
async def community_create(
    name:         str            = Form(...),
    beschreibung: str            = Form(...),
    rasse:        str            = Form(""),
    alter_jahre:  Optional[float] = Form(None),
    geschlecht:   str            = Form(""),
    gruende:      str            = Form(""),
    ort:          str            = Form(""),
    plz:          str            = Form(""),
    lat:          Optional[float] = Form(None),
    lon:          Optional[float] = Form(None),
    dog_id:       Optional[int]  = Form(None),
    foto:         Optional[UploadFile] = File(None),
    user=Depends(get_current_user),
):
    foto_url = None

    if foto and foto.filename:
        MAX_SIZE = 5 * 1024 * 1024
        header = await foto.read(12)
        if len(header) < 3:
            raise HTTPException(400, "Ungültige Datei")
        is_jpeg = header[:3] == b"\xff\xd8\xff"
        is_png  = header[:4] == b"\x89PNG"
        is_webp = header[:4] == b"RIFF" and len(header) >= 12 and header[8:12] == b"WEBP"
        if not (is_jpeg or is_png or is_webp):
            raise HTTPException(400, "Nur JPEG, PNG oder WebP erlaubt")
        rest = await foto.read(MAX_SIZE)
        if len(rest) >= MAX_SIZE:
            raise HTTPException(400, "Foto zu groß (max 5 MB)")
        data = header + rest

        folder = os.path.join(MEDIA_DIR, "adoption")
        os.makedirs(folder, exist_ok=True)
        filename = f"{uuid.uuid4()}.jpg"
        filepath = os.path.join(folder, filename)
        with open(filepath, "wb") as f:
            f.write(data)
        foto_url = f"/media/adoption/{filename}"

    with db() as conn:
        cur = conn.execute("""
            INSERT INTO community_adoption
                (user_id, dog_id, name, rasse, alter_jahre, geschlecht,
                 foto_url, beschreibung, gruende, ort, plz, lat, lon)
            VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
        """, (
            user["id"], dog_id, name, rasse or None, alter_jahre,
            geschlecht or None, foto_url, beschreibung,
            gruende or None, ort or None, plz or None, lat, lon,
        ))
        new_id = cur.lastrowid
        row = conn.execute(
            "SELECT * FROM community_adoption WHERE id = ?", (new_id,)
        ).fetchone()
    return dict(row)


# ------------------------------------------------------------------
# PATCH /api/adoption/community/{id} — Status ändern (nur Besitzer)
# ------------------------------------------------------------------
class _StatusBody(BaseModel):
    status: str

@router.patch("/community/{listing_id}")
def community_update_status(
    listing_id: int,
    body: _StatusBody,
    user=Depends(get_current_user),
):
    allowed = {"active", "reserved", "vermittelt"}
    if body.status not in allowed:
        raise HTTPException(400, f"Status muss einer von {allowed} sein")
    status = body.status
    with db() as conn:
        cur = conn.execute("""
            UPDATE community_adoption
            SET status = ?, updated_at = datetime('now')
            WHERE id = ? AND user_id = ?
        """, (status, listing_id, user["id"]))
        if cur.rowcount == 0:
            raise HTTPException(404, "Inserat nicht gefunden oder kein Zugriff")
    return {"ok": True}


# ------------------------------------------------------------------
# DELETE /api/adoption/community/{id} — Soft-Delete (nur Besitzer)
# ------------------------------------------------------------------
@router.delete("/community/{listing_id}")
def community_delete(listing_id: int, user=Depends(get_current_user)):
    with db() as conn:
        cur = conn.execute("""
            UPDATE community_adoption
            SET status = 'deleted', updated_at = datetime('now')
            WHERE id = ? AND user_id = ?
        """, (listing_id, user["id"]))
        if cur.rowcount == 0:
            raise HTTPException(404, "Inserat nicht gefunden oder kein Zugriff")
    return {"ok": True}


# ------------------------------------------------------------------
# POST /api/adoption/community/{id}/interest — Interesse bekunden
# ------------------------------------------------------------------
@router.post("/community/{listing_id}/interest", status_code=201)
def community_interest(listing_id: int, body: InterestBody = None, user=Depends(get_current_user)):
    nachricht = (body.nachricht if body else None) or None
    with db() as conn:
        listing = conn.execute(
            "SELECT id, name, user_id FROM community_adoption WHERE id = ? AND status != 'deleted'",
            (listing_id,)
        ).fetchone()
        if not listing:
            raise HTTPException(404, "Inserat nicht gefunden")
        if listing["user_id"] == user["id"]:
            raise HTTPException(400, "Eigenes Inserat")
        try:
            conn.execute("""
                INSERT INTO community_adoption_interest (listing_id, user_id, nachricht)
                VALUES (?, ?, ?)
            """, (listing_id, user["id"], nachricht))
        except Exception:
            raise HTTPException(409, "Interesse bereits bekundet")

    try:
        send_push_to_user(listing["user_id"], {
            "title": "Jemand interessiert sich für deinen Hund \U0001f43e",
            "body":  f"{user['name']} möchte mehr über {listing['name']} erfahren.",
            "url":   "/#adoption",
        })
    except Exception as e:
        logger.warning(f"Push interest: {e}")

    return {"ok": True}


# ------------------------------------------------------------------
# DELETE /api/adoption/community/{id}/interest — Interesse zurückziehen
# ------------------------------------------------------------------
@router.delete("/community/{listing_id}/interest")
def community_interest_withdraw(listing_id: int, user=Depends(get_current_user)):
    with db() as conn:
        cur = conn.execute("""
            DELETE FROM community_adoption_interest
            WHERE listing_id = ? AND user_id = ?
        """, (listing_id, user["id"]))
        if cur.rowcount == 0:
            raise HTTPException(404, "Kein Interesse gefunden")
    return {"ok": True}


# ------------------------------------------------------------------
# GET /api/adoption/community/{id}/interests — Interessenten (nur Besitzer)
# ------------------------------------------------------------------
@router.get("/community/{listing_id}/interests")
def community_interests(listing_id: int, user=Depends(get_current_user)):
    with db() as conn:
        listing = conn.execute(
            "SELECT user_id FROM community_adoption WHERE id = ? AND status != 'deleted'",
            (listing_id,)
        ).fetchone()
        if not listing:
            raise HTTPException(404, "Inserat nicht gefunden")
        if listing["user_id"] != user["id"]:
            raise HTTPException(403, "Nur der Besitzer kann Interessenten sehen")
        rows = conn.execute("""
            SELECT i.id, i.nachricht, i.created_at, u.id AS user_id, u.name, u.avatar_url
            FROM community_adoption_interest i
            JOIN users u ON u.id = i.user_id
            WHERE i.listing_id = ?
            ORDER BY i.created_at ASC
        """, (listing_id,)).fetchall()
    return [dict(r) for r in rows]
