#!/usr/bin/env python3
"""
KI-gestützte Ortsextraktion aus Volltexten.
Zweistufiger Prozess:
1. Extraktion aller Ortsangaben mit Kontext
2. Intelligente Georeferenzierung mit Kontextverständnis
"""

import argparse
import json
import os
import sqlite3
import time
from pathlib import Path

import httpx

PROJECT_ROOT = Path(__file__).resolve().parent.parent
DB_PATH = PROJECT_ROOT / "data" / "tracker_remote.db"

DASHSCOPE_URL = "https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions"
DASHSCOPE_KEY = os.environ.get("QWEN_API_KEY") or os.popen("security find-generic-password -s qwen-api -w 2>/dev/null").read().strip()

# Nominatim für Geocoding
NOMINATIM_URL = "https://nominatim.openstreetmap.org/search"
USER_AGENT = "Antragstracker-Hagen/1.0"
HAGEN_BBOX = "7.35,51.30,7.65,51.45"

EXTRACTION_PROMPT = """Extrahiere ALLE geografischen Ortsangaben aus diesem kommunalpolitischen Dokument aus Hagen.

DOKUMENT:
{volltext}

---

Gib eine Liste aller Orte zurück, die im Text erwähnt werden. Für jeden Ort:
- rohtext: Die genaue Formulierung im Text
- kontext: Der Satz oder Absatz, in dem der Ort erwähnt wird
- typ: strasse|platz|stadtteil|gebaeude|sonstiges
- geocodierbar: true/false (kann man das auf einer Karte finden?)
- geocode_query: Falls geocodierbar, der beste Suchbegriff für Nominatim (z.B. bei "Polizeiwache an der Boeler Straße" → "Boeler Straße")

JSON-Format:
{{
  "orte": [
    {{
      "rohtext": "Altenhagener Brücke",
      "kontext": "Der Abschnitt ab der Altenhagener Brücke bis zum Aldi",
      "typ": "strasse",
      "geocodierbar": true,
      "geocode_query": "Altenhagener Brücke, Hagen"
    }},
    {{
      "rohtext": "Spielplatz",
      "kontext": "Darüber hinaus befindet sich ein Spielplatz",
      "typ": "gebaeude",
      "geocodierbar": false,
      "geocode_query": null
    }}
  ]
}}

WICHTIG:
- Extrahiere ALLE Orte, auch generische
- Bei "X an der Y-Straße" ist Y-Straße der geocode_query
- Stadtteile wie "Altenhagen", "Haspe" sind geocodierbar
- Generische Begriffe wie "Schule", "Spielplatz" ohne Straßenangabe sind NICHT geocodierbar

NUR JSON, keine Erklärungen."""


GEOCODE_REFINEMENT_PROMPT = """Du bist ein Geocoding-Experte für die Stadt Hagen (NRW).

Ich habe folgende Ortsangaben aus einem kommunalpolitischen Dokument extrahiert:
{orte_json}

Der Volltext-Kontext war:
{kontext}

Nominatim hat für "{query}" folgende Ergebnisse in Hagen gefunden:
{nominatim_results}

Welches Ergebnis passt am besten zum Kontext? Antworte mit der Nummer (1, 2, 3...) oder "keins" wenn keins passt.
Nur die Nummer oder "keins", keine Erklärung."""


def get_db():
    conn = sqlite3.connect(str(DB_PATH))
    conn.row_factory = sqlite3.Row
    return conn


def call_qwen(prompt: str, model: str = "qwen-turbo-latest") -> dict | str | None:
    """Ruft Qwen API auf."""
    if not DASHSCOPE_KEY:
        return None
    
    try:
        resp = httpx.post(
            DASHSCOPE_URL,
            headers={"Authorization": f"Bearer {DASHSCOPE_KEY}", "Content-Type": "application/json"},
            json={"model": model, "messages": [{"role": "user", "content": prompt}], "temperature": 0.1},
            timeout=60
        )
        resp.raise_for_status()
        content = resp.json()["choices"][0]["message"]["content"]
        
        # JSON extrahieren wenn vorhanden
        if "```json" in content:
            content = content.split("```json")[1].split("```")[0]
        elif "```" in content:
            parts = content.split("```")
            if len(parts) >= 2:
                content = parts[1]
        
        try:
            return json.loads(content.strip())
        except json.JSONDecodeError:
            return content.strip()
            
    except Exception as e:
        print(f"    API-Fehler: {e}")
        return None


def geocode_nominatim(client: httpx.Client, query: str) -> list[dict]:
    """Sucht mit Nominatim in Hagen."""
    try:
        resp = client.get(
            NOMINATIM_URL,
            params={"q": f"{query}, Hagen, Germany", "format": "json", "limit": 3, 
                    "viewbox": HAGEN_BBOX, "bounded": 1},
            headers={"User-Agent": USER_AGENT},
            timeout=10
        )
        resp.raise_for_status()
        return resp.json()
    except Exception as e:
        print(f"    Nominatim-Fehler: {e}")
        return []


def process_vorlage(conn: sqlite3.Connection, client: httpx.Client, vorlage: dict) -> int:
    """Extrahiert und geocodiert Orte aus einer Vorlage."""
    vid = vorlage['id']
    akz = vorlage['aktenzeichen'] or f"#{vid}"
    volltext = vorlage['volltext_clean']
    
    if not volltext or len(volltext) < 100:
        return 0
    
    # Volltext kürzen
    volltext_short = volltext[:6000] if len(volltext) > 6000 else volltext
    
    # Schritt 1: KI-Extraktion
    prompt = EXTRACTION_PROMPT.format(volltext=volltext_short)
    result = call_qwen(prompt)
    
    if not result or not isinstance(result, dict) or 'orte' not in result:
        print(f"  {akz}: Keine Orte extrahiert")
        return 0
    
    orte = result['orte']
    print(f"  {akz}: {len(orte)} Orte gefunden")
    
    # Schritt 2: Geocoding für geocodierbare Orte
    success = 0
    for ort in orte:
        rohtext = ort.get('rohtext', '')
        kontext = ort.get('kontext', '')
        typ = ort.get('typ', 'sonstiges')
        geocodierbar = ort.get('geocodierbar', False)
        geocode_query = ort.get('geocode_query')
        
        if not rohtext:
            continue
        
        # Prüfen ob schon existiert
        existing = conn.execute(
            "SELECT id FROM orte WHERE name = ? OR rohtext = ?",
            (rohtext, rohtext)
        ).fetchone()
        
        if existing:
            # Nur Verknüpfung erstellen
            conn.execute("""
                INSERT OR IGNORE INTO vorlagen_orte (vorlage_id, ort_id, kontext)
                VALUES (?, ?, ?)
            """, (vid, existing['id'], kontext[:500]))
            conn.execute("UPDATE orte SET vorlage_count = vorlage_count + 1 WHERE id = ?", (existing['id'],))
            conn.commit()
            continue
        
        # Neuen Ort anlegen
        lat, lon = None, None
        status = 'skipped'
        
        if geocodierbar and geocode_query:
            time.sleep(1.1)  # Nominatim Rate Limit
            results = geocode_nominatim(client, geocode_query)
            
            if results:
                # Ersten Treffer nehmen (könnte mit KI verfeinert werden)
                lat = float(results[0]['lat'])
                lon = float(results[0]['lon'])
                status = 'success'
                print(f"    ✓ {rohtext} → ({lat:.4f}, {lon:.4f})")
            else:
                status = 'failed'
                print(f"    ✗ {rohtext} (nicht gefunden)")
        else:
            print(f"    ⊘ {rohtext} (nicht geocodierbar)")
        
        cursor = conn.execute("""
            INSERT INTO orte (name, typ, lat, lon, rohtext, kontext_satz, geocode_status, vorlage_count)
            VALUES (?, ?, ?, ?, ?, ?, ?, 1)
        """, (geocode_query or rohtext, typ, lat, lon, rohtext, kontext[:500], status))
        
        ort_id = cursor.lastrowid
        conn.execute("""
            INSERT OR IGNORE INTO vorlagen_orte (vorlage_id, ort_id, kontext)
            VALUES (?, ?, ?)
        """, (vid, ort_id, kontext[:500]))
        conn.commit()
        
        if lat:
            success += 1
    
    return success


def main():
    parser = argparse.ArgumentParser(description="KI-gestützte Ortsextraktion")
    parser.add_argument("--limit", type=int, default=10, help="Max. Anzahl Vorlagen")
    parser.add_argument("--vorlage", type=int, help="Einzelne Vorlage-ID")
    args = parser.parse_args()
    
    print(f"=== KI-Ortsextraktion ===\n")
    
    conn = get_db()
    client = httpx.Client()
    
    if args.vorlage:
        query = "SELECT id, aktenzeichen, volltext_clean FROM vorlagen WHERE id = ?"
        params = [args.vorlage]
    else:
        # Vorlagen mit Volltext die noch nicht verarbeitet wurden
        query = """
            SELECT v.id, v.aktenzeichen, v.volltext_clean
            FROM vorlagen v
            WHERE v.volltext_clean IS NOT NULL
              AND v.id NOT IN (SELECT DISTINCT vorlage_id FROM vorlagen_orte)
            ORDER BY v.datum_eingang DESC
            LIMIT ?
        """
        params = [args.limit]
    
    vorlagen = conn.execute(query, params).fetchall()
    print(f"Verarbeite {len(vorlagen)} Vorlagen\n")
    
    total_success = 0
    for v in vorlagen:
        total_success += process_vorlage(conn, client, dict(v))
    
    client.close()
    
    # Stats
    total_orte = conn.execute("SELECT COUNT(*) FROM orte").fetchone()[0]
    geocoded = conn.execute("SELECT COUNT(*) FROM orte WHERE lat IS NOT NULL").fetchone()[0]
    conn.close()
    
    print(f"\n=== Fertig ===")
    print(f"Orte gesamt: {total_orte}")
    print(f"Geocodiert: {geocoded}")
    print(f"Diese Runde: {total_success} neue geocodiert")


if __name__ == "__main__":
    main()
