"""BAN YARO — Forum (Sprint 11)"""

import os, uuid, json, logging
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File
from pydantic import BaseModel
from typing import Optional
from database import db
from auth import get_current_user, get_current_user_optional
from timeutils import safe_client_time
from ratelimit import is_duplicate_post, record_post
from content_filter import check_forum_content
from routes.push import send_push_to_user
from media_utils import convert_media, extract_video_thumb, generate_preview, preview_url_from

logger = logging.getLogger(__name__)

router = APIRouter()

MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media")
FORUM_DIR = os.path.join(MEDIA_DIR, "forum")

KATEGORIEN = ['allgemein', 'rasse', 'region', 'gesundheit', 'erziehung',
              'spaziergang', 'ausflug', 'training', 'ernaehrung', 'probleme', 'tauschboerse']


# ------------------------------------------------------------------
# Schemas
# ------------------------------------------------------------------
class ThreadCreate(BaseModel):
    kategorie:   str           = 'allgemein'
    titel:       str
    text:        str
    thread_lat:  Optional[float] = None
    thread_lon:  Optional[float] = None
    thread_ort:  Optional[str]   = None
    client_time: Optional[str]   = None

class PostCreate(BaseModel):
    text:        str
    client_time: Optional[str] = None

class ThreadPatch(BaseModel):
    is_pinned:  Optional[int] = None
    is_locked:  Optional[int] = None

class ThreadUpdate(BaseModel):
    titel:      Optional[str]   = None
    text:       Optional[str]   = None
    thread_lat: Optional[float] = None
    thread_lon: Optional[float] = None
    thread_ort: Optional[str]   = None

class PostUpdate(BaseModel):
    text: str

class LikeBody(BaseModel):
    target_type: str   # 'thread' | 'post'
    target_id:   int

class ReportBody(BaseModel):
    target_type: str
    target_id:   int
    grund:       str

class LocationBody(BaseModel):
    lat:  Optional[float] = None
    lon:  Optional[float] = None
    show: bool = False

class ResolveReport(BaseModel):
    resolved: int = 1


# ------------------------------------------------------------------
# Helpers
# ------------------------------------------------------------------
_FORUM_ALLOWED_EXT = {".jpg",".jpeg",".png",".gif",".webp",".heic",".heif",
                      ".mp4",".mov",".webm",".m4v",".pdf",".avi"}

def _save_upload(file: UploadFile, data: bytes) -> str:
    os.makedirs(FORUM_DIR, exist_ok=True)
    ext = os.path.splitext(file.filename or "")[1].lower() or ".jpg"
    if ext not in _FORUM_ALLOWED_EXT:
        raise HTTPException(415, "Dateityp nicht erlaubt.")
    data, ext = convert_media(data, file.filename or "")
    filename = f"{uuid.uuid4().hex}{ext}"
    path     = os.path.join(FORUM_DIR, filename)
    with open(path, "wb") as f:
        f.write(data)
    if ext in {".mp4", ".webm"}:
        extract_video_thumb(path)
    else:
        preview_bytes = generate_preview(data, ext)
        if preview_bytes:
            with open(os.path.splitext(path)[0] + "_preview.webp", "wb") as f:
                f.write(preview_bytes)
    return f"/media/forum/{filename}"

def _parse_foto_urls(raw) -> list:
    if not raw:
        return []
    try:
        return json.loads(raw)
    except Exception:
        return []

def _user_liked(conn, user_id: int, target_type: str, target_id: int) -> bool:
    if not user_id:
        return False
    row = conn.execute(
        "SELECT 1 FROM forum_likes WHERE user_id=? AND target_type=? AND target_id=?",
        (user_id, target_type, target_id)
    ).fetchone()
    return row is not None


# ------------------------------------------------------------------
# GET /api/forum/threads
# ------------------------------------------------------------------
@router.get("/threads")
async def list_threads(
    kategorie: Optional[str] = None,
    search:    Optional[str] = None,
    limit:     int           = 30,
    offset:    int           = 0,
    user=Depends(get_current_user_optional),
):
    uid = user['id'] if user else None
    with db() as conn:
        q = """
            SELECT t.id, t.kategorie, t.titel,
                   SUBSTR(t.text, 1, 120) AS text_preview,
                   t.antworten, t.likes, t.views,
                   t.is_pinned, t.is_locked, t.foto_urls,
                   t.created_at, t.user_id,
                   u.name AS autor_name, u.founder_number AS autor_founder_number
            FROM forum_threads t
            LEFT JOIN users u ON u.id = t.user_id
            WHERE t.is_deleted = 0
        """
        params = []
        if kategorie and kategorie != 'alle':
            q += " AND t.kategorie = ?"
            params.append(kategorie)
        if search:
            q += " AND (t.titel LIKE ? OR t.text LIKE ?)"
            params.extend([f'%{search}%', f'%{search}%'])
        q += " ORDER BY t.is_pinned DESC, t.created_at DESC LIMIT ? OFFSET ?"
        params.extend([limit, offset])
        rows = conn.execute(q, params).fetchall()

        result = []
        for r in rows:
            t = dict(r)
            foto_list = _parse_foto_urls(t.get('foto_urls'))
            first = foto_list[0] if foto_list else None
            t['foto_preview']         = first
            t['foto_preview_url']     = preview_url_from(first)
            t['foto_urls'] = foto_list
            t['user_liked'] = _user_liked(conn, uid, 'thread', t['id']) if uid else False
            result.append(t)

    return result


# ------------------------------------------------------------------
# POST /api/forum/threads
# ------------------------------------------------------------------
def _check_post_limits(user_id: int, conn, text: str, user_created_at: str | None = None, is_thread: bool = False):
    """Cooldown, Stunden-Limit und Duplikat-Check für Forum-Posts."""
    # 30-Sekunden-Cooldown zwischen beliebigen Posts
    last = conn.execute(
        """SELECT MAX(created_at) AS last FROM (
             SELECT created_at FROM forum_threads WHERE user_id=?
             UNION ALL
             SELECT created_at FROM forum_posts WHERE user_id=?
           )""",
        (user_id, user_id),
    ).fetchone()["last"]
    if last:
        try:
            from datetime import datetime as _dt
            diff = (_dt.utcnow() - _dt.fromisoformat(last)).total_seconds()
            if diff < 30:
                raise HTTPException(429, "Bitte warte einen Moment bevor du erneut postest.")
        except (ValueError, TypeError):
            pass

    # Stunden-Limit
    if is_thread:
        count = conn.execute(
            "SELECT COUNT(*) FROM forum_threads WHERE user_id=? AND created_at > datetime('now','-1 hour')",
            (user_id,),
        ).fetchone()[0]
        if count >= 5:
            raise HTTPException(429, "Du hast in dieser Stunde bereits 5 Threads erstellt. Bitte warte etwas.")
    else:
        count = conn.execute(
            "SELECT COUNT(*) FROM forum_posts WHERE user_id=? AND created_at > datetime('now','-1 hour')",
            (user_id,),
        ).fetchone()[0]
        if count >= 20:
            raise HTTPException(429, "Du hast in dieser Stunde bereits 20 Antworten geschrieben. Bitte warte etwas.")

    # Duplikat-Check
    if is_duplicate_post(user_id, text):
        raise HTTPException(400, "Dieser Beitrag wurde bereits kürzlich gepostet.")

    # Content-Filter
    check_forum_content(text, user_created_at)


@router.post("/threads", status_code=201)
async def create_thread(data: ThreadCreate, user=Depends(get_current_user)):
    if not user.get("email_verified"):
        raise HTTPException(403, "Bitte bestätige zuerst deine E-Mail-Adresse um im Forum zu schreiben.")
    if not data.titel.strip():
        raise HTTPException(400, "Titel darf nicht leer sein.")
    if not data.text.strip():
        raise HTTPException(400, "Text darf nicht leer sein.")
    if len(data.text.strip()) < 20:
        raise HTTPException(400, "Text muss mindestens 20 Zeichen lang sein.")
    if data.kategorie not in KATEGORIEN:
        raise HTTPException(400, "Ungültige Kategorie.")
    with db() as conn:
        _check_post_limits(user["id"], conn, data.text.strip(), user.get("created_at"), is_thread=True)
        ct = safe_client_time(data.client_time)
        cur = conn.execute(
            """INSERT INTO forum_threads (user_id, kategorie, titel, text, thread_lat, thread_lon, thread_ort, created_at)
               VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
            (user['id'], data.kategorie, data.titel.strip(), data.text.strip(),
             data.thread_lat, data.thread_lon, data.thread_ort, ct)
        )
        row = conn.execute(
            """SELECT t.*, u.name AS autor_name, u.founder_number AS autor_founder_number
               FROM forum_threads t
               LEFT JOIN users u ON u.id = t.user_id
               WHERE t.id = ?""",
            (cur.lastrowid,)
        ).fetchone()
    t = dict(row)
    t['foto_urls'] = _parse_foto_urls(t.get('foto_urls'))
    t['user_liked'] = False
    record_post(user["id"], data.text.strip())
    return t


# ------------------------------------------------------------------
# GET /api/forum/threads/{id}
# ------------------------------------------------------------------
@router.get("/threads/{thread_id}")
async def get_thread(thread_id: int, user=Depends(get_current_user_optional)):
    uid = user['id'] if user else None
    with db() as conn:
        thread = conn.execute(
            """SELECT t.*, u.name AS autor_name, u.founder_number AS autor_founder_number
               FROM forum_threads t
               LEFT JOIN users u ON u.id = t.user_id
               WHERE t.id = ? AND t.is_deleted = 0""",
            (thread_id,)
        ).fetchone()
        if not thread:
            raise HTTPException(404, "Thread nicht gefunden.")

        # Increment views
        conn.execute(
            "UPDATE forum_threads SET views = views + 1 WHERE id = ?", (thread_id,)
        )

        posts = conn.execute(
            """SELECT p.*, u.name AS autor_name, u.founder_number AS autor_founder_number
               FROM forum_posts p
               LEFT JOIN users u ON u.id = p.user_id
               WHERE p.thread_id = ?
               ORDER BY p.created_at ASC""",
            (thread_id,)
        ).fetchall()

        result = dict(thread)
        result['foto_urls'] = _parse_foto_urls(result.get('foto_urls'))
        result['user_liked'] = _user_liked(conn, uid, 'thread', thread_id) if uid else False

        result['posts'] = []
        for p in posts:
            pd = dict(p)
            if pd.get('is_deleted'):
                result['posts'].append({
                    'id':         pd['id'],
                    'thread_id':  pd['thread_id'],
                    'is_deleted': 1,
                    'created_at': pd['created_at'],
                })
            else:
                pd['foto_urls'] = _parse_foto_urls(pd.get('foto_urls'))
                pd['user_liked'] = _user_liked(conn, uid, 'post', pd['id']) if uid else False
                result['posts'].append(pd)

    return result


# ------------------------------------------------------------------
# DELETE /api/forum/threads/{id}
# ------------------------------------------------------------------
@router.delete("/threads/{thread_id}", status_code=204)
async def delete_thread(thread_id: int, user=Depends(get_current_user)):
    with db() as conn:
        thread = conn.execute(
            "SELECT * FROM forum_threads WHERE id = ?", (thread_id,)
        ).fetchone()
        if not thread:
            raise HTTPException(404, "Thread nicht gefunden.")
        if thread['user_id'] != user['id'] and not user.get('is_moderator'):
            raise HTTPException(403, "Keine Berechtigung.")
        conn.execute(
            "UPDATE forum_threads SET is_deleted = 1 WHERE id = ?", (thread_id,)
        )


# ------------------------------------------------------------------
# PATCH /api/forum/threads/{id} — Moderator: pin/lock
# ------------------------------------------------------------------
@router.patch("/threads/{thread_id}")
async def patch_thread(thread_id: int, data: ThreadPatch, user=Depends(get_current_user)):
    if not user.get('is_moderator'):
        raise HTTPException(403, "Nur Moderatoren können Threads bearbeiten.")
    with db() as conn:
        thread = conn.execute(
            "SELECT * FROM forum_threads WHERE id = ?", (thread_id,)
        ).fetchone()
        if not thread:
            raise HTTPException(404, "Thread nicht gefunden.")

        updates = data.model_dump(exclude_none=True)
        if updates:
            cols = ', '.join(f"{k} = ?" for k in updates)
            conn.execute(
                f"UPDATE forum_threads SET {cols} WHERE id = ?",
                [*updates.values(), thread_id]
            )
        row = conn.execute(
            """SELECT t.*, u.name AS autor_name, u.founder_number AS autor_founder_number
               FROM forum_threads t
               LEFT JOIN users u ON u.id = t.user_id
               WHERE t.id = ?""",
            (thread_id,)
        ).fetchone()
    t = dict(row)
    t['foto_urls'] = _parse_foto_urls(t.get('foto_urls'))
    return t


# ------------------------------------------------------------------
# POST /api/forum/threads/{id}/posts
# ------------------------------------------------------------------
@router.post("/threads/{thread_id}/posts", status_code=201)
async def create_post(thread_id: int, data: PostCreate, user=Depends(get_current_user)):
    if not user.get("email_verified"):
        raise HTTPException(403, "Bitte bestätige zuerst deine E-Mail-Adresse um im Forum zu schreiben.")
    if not data.text.strip():
        raise HTTPException(400, "Text darf nicht leer sein.")
    with db() as conn:
        thread = conn.execute(
            "SELECT id, is_locked, is_deleted FROM forum_threads WHERE id = ?",
            (thread_id,)
        ).fetchone()
        if not thread:
            raise HTTPException(404, "Thread nicht gefunden.")
        if thread['is_locked']:
            raise HTTPException(403, "Dieser Thread ist gesperrt.")
        if thread['is_deleted']:
            raise HTTPException(404, "Thread nicht gefunden.")

        _check_post_limits(user["id"], conn, data.text.strip(), user.get("created_at"), is_thread=False)

        ct = safe_client_time(data.client_time)
        cur = conn.execute(
            "INSERT INTO forum_posts (thread_id, user_id, text, created_at) VALUES (?, ?, ?, ?)",
            (thread_id, user['id'], data.text.strip(), ct)
        )
        conn.execute(
            "UPDATE forum_threads SET antworten = antworten + 1 WHERE id = ?",
            (thread_id,)
        )
        row = conn.execute(
            """SELECT p.*, u.name AS autor_name, u.founder_number AS autor_founder_number
               FROM forum_posts p
               LEFT JOIN users u ON u.id = p.user_id
               WHERE p.id = ?""",
            (cur.lastrowid,)
        ).fetchone()
        # Thread-Owner ermitteln für Push-Notification
        owner_row = conn.execute(
            "SELECT user_id FROM forum_threads WHERE id = ?", (thread_id,)
        ).fetchone()
        owner_id = owner_row['user_id'] if owner_row else None

    pd = dict(row)
    pd['foto_urls'] = []
    pd['user_liked'] = False
    record_post(user["id"], data.text.strip())

    # Push-Notification an Thread-Owner (nicht an sich selbst)
    if owner_id and owner_id != user['id']:
        try:
            commenter_name = pd.get('autor_name') or 'Jemand'
            send_push_to_user(owner_id, {
                "type":  "forum_reply",
                "title": "Neue Antwort auf deinen Beitrag",
                "body":  f"{commenter_name} hat auf deinen Beitrag geantwortet",
                "tag":   f"forum-{thread_id}",
                "data":  {"page": "forum", "id": thread_id},
            })
        except Exception:
            logger.exception("Push-Notification für Forum-Reply fehlgeschlagen (nicht kritisch)")

    return pd


# ------------------------------------------------------------------
# PATCH /api/forum/threads/{id}/content — Besitzer: titel/text
# ------------------------------------------------------------------
@router.patch("/threads/{thread_id}/content")
async def update_thread_content(thread_id: int, data: ThreadUpdate, user=Depends(get_current_user)):
    with db() as conn:
        t = conn.execute("SELECT user_id FROM forum_threads WHERE id=?", (thread_id,)).fetchone()
        if not t: raise HTTPException(404, "Thread nicht gefunden.")
        if t["user_id"] != user["id"]: raise HTTPException(403, "Nicht dein Beitrag.")
        updates, vals = [], []
        if data.titel is not None and data.titel.strip():
            updates.append("titel=?"); vals.append(data.titel.strip())
        if data.text is not None:
            updates.append("text=?"); vals.append(data.text.strip())
        # Location immer überschreiben (auch mit None zum Löschen)
        updates += ["thread_lat=?", "thread_lon=?", "thread_ort=?"]
        vals    += [data.thread_lat, data.thread_lon,
                    data.thread_ort.strip() if data.thread_ort else None]
        conn.execute(f"UPDATE forum_threads SET {','.join(updates)} WHERE id=?", (*vals, thread_id))
    return {"ok": True}


# ------------------------------------------------------------------
# PATCH /api/forum/posts/{id} — Besitzer: text
# ------------------------------------------------------------------
@router.patch("/posts/{post_id}")
async def update_post(post_id: int, data: PostUpdate, user=Depends(get_current_user)):
    with db() as conn:
        p = conn.execute("SELECT user_id FROM forum_posts WHERE id=?", (post_id,)).fetchone()
        if not p: raise HTTPException(404, "Beitrag nicht gefunden.")
        if p["user_id"] != user["id"]: raise HTTPException(403, "Nicht dein Beitrag.")
        conn.execute("UPDATE forum_posts SET text=? WHERE id=?", (data.text.strip(), post_id))
    return {"ok": True}


# ------------------------------------------------------------------
# DELETE /api/forum/posts/{id}
# ------------------------------------------------------------------
@router.delete("/posts/{post_id}", status_code=204)
async def delete_post(post_id: int, user=Depends(get_current_user)):
    with db() as conn:
        post = conn.execute(
            "SELECT * FROM forum_posts WHERE id = ?", (post_id,)
        ).fetchone()
        if not post:
            raise HTTPException(404, "Beitrag nicht gefunden.")
        if post['user_id'] != user['id'] and not user.get('is_moderator'):
            raise HTTPException(403, "Keine Berechtigung.")
        conn.execute(
            "UPDATE forum_posts SET is_deleted = 1 WHERE id = ?", (post_id,)
        )
        # Antworten-Zähler nur verringern wenn eigener soft-delete (nicht Moderator)
        conn.execute(
            "UPDATE forum_threads SET antworten = MAX(0, antworten - 1) WHERE id = ?",
            (post['thread_id'],)
        )


# ------------------------------------------------------------------
# POST /api/forum/threads/{id}/fotos
# ------------------------------------------------------------------
@router.post("/threads/{thread_id}/fotos")
async def upload_thread_foto(
    thread_id: int,
    file: UploadFile = File(...),
    user=Depends(get_current_user),
):
    with db() as conn:
        thread = conn.execute(
            "SELECT * FROM forum_threads WHERE id = ? AND is_deleted = 0",
            (thread_id,)
        ).fetchone()
        if not thread:
            raise HTTPException(404, "Thread nicht gefunden.")
        if thread['user_id'] != user['id'] and not user.get('is_moderator'):
            raise HTTPException(403, "Keine Berechtigung.")

        existing = _parse_foto_urls(thread['foto_urls'])
        if len(existing) >= 5:
            raise HTTPException(400, "Maximal 5 Fotos pro Thread.")

        data = await file.read()
        url  = _save_upload(file, data)
        existing.append(url)
        conn.execute(
            "UPDATE forum_threads SET foto_urls = ? WHERE id = ?",
            (json.dumps(existing), thread_id)
        )
    return {"foto_url": url, "foto_urls": existing}


# ------------------------------------------------------------------
# POST /api/forum/posts/{id}/fotos
# ------------------------------------------------------------------
@router.post("/posts/{post_id}/fotos")
async def upload_post_foto(
    post_id: int,
    file: UploadFile = File(...),
    user=Depends(get_current_user),
):
    with db() as conn:
        post = conn.execute(
            "SELECT * FROM forum_posts WHERE id = ? AND is_deleted = 0",
            (post_id,)
        ).fetchone()
        if not post:
            raise HTTPException(404, "Beitrag nicht gefunden.")
        if post['user_id'] != user['id'] and not user.get('is_moderator'):
            raise HTTPException(403, "Keine Berechtigung.")

        existing = _parse_foto_urls(post['foto_urls'])
        if len(existing) >= 5:
            raise HTTPException(400, "Maximal 5 Fotos pro Beitrag.")

        data = await file.read()
        url  = _save_upload(file, data)
        existing.append(url)
        conn.execute(
            "UPDATE forum_posts SET foto_urls = ? WHERE id = ?",
            (json.dumps(existing), post_id)
        )
    return {"foto_url": url, "foto_urls": existing}


# ------------------------------------------------------------------
# POST /api/forum/like — Toggle
# ------------------------------------------------------------------
_LIKE_TABLE = {'thread': 'forum_threads', 'post': 'forum_posts'}

@router.post("/like")
async def toggle_like(data: LikeBody, user=Depends(get_current_user)):
    if data.target_type not in _LIKE_TABLE:
        raise HTTPException(400, "Ungültiger Typ.")

    table = _LIKE_TABLE[data.target_type]
    with db() as conn:
        existing = conn.execute(
            "SELECT 1 FROM forum_likes WHERE user_id=? AND target_type=? AND target_id=?",
            (user['id'], data.target_type, data.target_id)
        ).fetchone()

        if existing:
            conn.execute(
                "DELETE FROM forum_likes WHERE user_id=? AND target_type=? AND target_id=?",
                (user['id'], data.target_type, data.target_id)
            )
            conn.execute(
                f"UPDATE {table} SET likes = MAX(0, likes - 1) WHERE id = ?",
                (data.target_id,)
            )
            liked = False
        else:
            conn.execute(
                "INSERT OR IGNORE INTO forum_likes (user_id, target_type, target_id) VALUES (?,?,?)",
                (user['id'], data.target_type, data.target_id)
            )
            conn.execute(
                f"UPDATE {table} SET likes = likes + 1 WHERE id = ?",
                (data.target_id,)
            )
            liked = True

        count_row = conn.execute(
            f"SELECT likes FROM {table} WHERE id = ?", (data.target_id,)
        ).fetchone()
        count = count_row['likes'] if count_row else 0

    return {"liked": liked, "count": count}


# ------------------------------------------------------------------
# POST /api/forum/report
# ------------------------------------------------------------------
@router.post("/report", status_code=201)
async def report_content(data: ReportBody, user=Depends(get_current_user)):
    if data.target_type not in ('thread', 'post'):
        raise HTTPException(400, "Ungültiger Typ.")
    if not data.grund.strip():
        raise HTTPException(400, "Grund darf nicht leer sein.")
    with db() as conn:
        conn.execute(
            """INSERT INTO forum_reports (user_id, target_type, target_id, grund)
               VALUES (?, ?, ?, ?)""",
            (user['id'], data.target_type, data.target_id, data.grund.strip())
        )
    return {"ok": True}


# ------------------------------------------------------------------
# GET /api/forum/reports — Moderator
# ------------------------------------------------------------------
@router.get("/reports")
async def list_reports(user=Depends(get_current_user)):
    if not user.get('is_moderator'):
        raise HTTPException(403, "Nur Moderatoren.")
    with db() as conn:
        rows = conn.execute(
            """SELECT r.*, u.name AS melder_name
               FROM forum_reports r
               LEFT JOIN users u ON u.id = r.user_id
               WHERE r.resolved = 0
               ORDER BY r.created_at DESC"""
        ).fetchall()
    return [dict(r) for r in rows]


# ------------------------------------------------------------------
# PATCH /api/forum/reports/{id} — Moderator: resolve
# ------------------------------------------------------------------
@router.patch("/reports/{report_id}")
async def resolve_report(report_id: int, data: ResolveReport, user=Depends(get_current_user)):
    if not user.get('is_moderator'):
        raise HTTPException(403, "Nur Moderatoren.")
    with db() as conn:
        conn.execute(
            "UPDATE forum_reports SET resolved = ? WHERE id = ?",
            (data.resolved, report_id)
        )
    return {"ok": True}


# ------------------------------------------------------------------
# GET /api/forum/members/map
# ------------------------------------------------------------------
@router.get("/members/map")
async def members_map(user=Depends(get_current_user)):
    with db() as conn:
        rows = conn.execute(
            """SELECT SUBSTR(name, 1, INSTR(name || ' ', ' ') - 1) AS vorname,
                      forum_lat AS lat, forum_lon AS lon
               FROM users
               WHERE forum_show_location = 1
               AND   forum_lat IS NOT NULL
               AND   forum_lon IS NOT NULL"""
        ).fetchall()
    return [{'vorname': r['vorname'] or '?', 'lat': round(r['lat'], 3), 'lon': round(r['lon'], 3)}
            for r in rows]


# ------------------------------------------------------------------
# PATCH /api/forum/members/location
# ------------------------------------------------------------------
@router.patch("/members/location")
async def set_member_location(data: LocationBody, user=Depends(get_current_user)):
    if data.show and data.lat is not None and data.lon is not None:
        with db() as conn:
            conn.execute(
                """UPDATE users SET forum_lat=?, forum_lon=?, forum_show_location=1
                   WHERE id=?""",
                (round(data.lat, 4), round(data.lon, 4), user['id'])
            )
        return {"ok": True, "lat": data.lat, "lon": data.lon}
    else:
        with db() as conn:
            conn.execute(
                "UPDATE users SET forum_show_location=0 WHERE id=?",
                (user['id'],)
            )
        return {"ok": True, "show": False}


# ------------------------------------------------------------------
# GET /api/forum/search
# ------------------------------------------------------------------
@router.get("/search")
async def search_forum(q: Optional[str] = None, limit: int = 20):
    if not q or len(q.strip()) < 2:
        return []
    term = f'%{q.strip()}%'
    with db() as conn:
        rows = conn.execute(
            """SELECT t.id, t.kategorie, t.titel, t.antworten, t.created_at,
                      u.name AS autor_name,
                      SUBSTR(t.text, 1, 200) AS text_preview
               FROM forum_threads t
               LEFT JOIN users u ON u.id = t.user_id
               WHERE t.is_deleted = 0
               AND   (t.titel LIKE ? OR t.text LIKE ?)
               ORDER BY t.created_at DESC
               LIMIT ?""",
            (term, term, limit)
        ).fetchall()
    return [dict(r) for r in rows]
