import asyncio
import json
import logging
import psutil
import time
from datetime import datetime, timezone
import httpx

from services import maintenance, announce, llamaswap, auftragsbuch, discover
from config import V1_UPSTREAM

log = logging.getLogger(__name__)

# Wenn aktiv, wird der Report um die angegebene Stunde (0-23) lokaler Zeit verschickt
SCHEDULE_HOUR = 8 

async def fetch_tech_news() -> str:
    """Holt die Top 3 Tech-News von HackerNews als Futter."""
    try:
        async with httpx.AsyncClient(timeout=10.0) as client:
            top_ids_resp = await client.get("https://hacker-news.firebaseio.com/v0/topstories.json")
            top_ids = top_ids_resp.json()[:3]
            news_items = []
            for item_id in top_ids:
                item_resp = await client.get(f"https://hacker-news.firebaseio.com/v0/item/{item_id}.json")
                item = item_resp.json()
                title = item.get('title', 'Ohne Titel')
                url = item.get('url', f"https://news.ycombinator.com/item?id={item_id}")
                news_items.append(f"- {title} ({url})")
            return "\n".join(news_items) if news_items else "Keine News gefunden."
    except Exception as e:
        log.warning(f"Fehler beim Abrufen der News: {e}")
        return "News konnten nicht abgerufen werden."

async def generate_and_send_report() -> bool:
    """Sammelt System-Status, fragt das lokale LLM nach einer Zusammenfassung als Lucy und sendet via Telegram."""
    log.info("Sysadmin-Report gestartet...")
    
    # 1. Daten sammeln
    ram = psutil.virtual_memory()
    ram_gb_total = round(ram.total / (1024**3), 1)
    ram_gb_used = round(ram.used / (1024**3), 1)
    cpu_percent = psutil.cpu_percent(interval=1)
    disk = psutil.disk_usage('/')
    disk_gb_free = round(disk.free / (1024**3), 1)
    
    try:
        upd = maintenance.updates()
    except Exception:
        upd = {"os": 0, "engine": False, "swap": False, "hermes": False}
        
    running_models = llamaswap.get_running_models()
    
    # News
    news_text = await fetch_tech_news()
    
    # Discover: Bessere Modelle
    disc_data = discover.safe_discover(ram_gb_total)
    rec_models = []
    if disc_data and "categories" in disc_data:
        for c in disc_data["categories"]:
            if c.get("recommended"):
                rec_models.append(c["recommended"])
    rec_str = ", ".join(rec_models[:3]) if rec_models else "Keine neuen Empfehlungen"
    
    # Auftragsbuch
    try:
        auftraege = auftragsbuch.list_all()
        offene = [a for a in auftraege if a.get("status") in ("todo", "triage")]
        auftraege_str = f"{len(offene)} offene Aufgaben, die auf dich warten." if offene else "Das Auftragsbuch ist leer (Keine offenen Aufgaben)."
    except Exception:
        auftraege_str = "Fehler beim Lesen des Auftragsbuchs."

    events = announce.list_recent(limit=15)
    event_lines = []
    for e in events:
        ts_str = datetime.fromtimestamp(e["ts"]).strftime("%d.%m. %H:%M")
        text = e.get('text', '')
        if len(text) > 100:
            text = text[:100] + "..."
        event_lines.append(f"- [{ts_str}] {e.get('subject', '')}: {text}")
        
    events_str = "\n".join(event_lines) if event_lines else "Keine besonderen Vorkommnisse."
    
    evidence = f"""
Hardware: RAM {ram_gb_used}/{ram_gb_total} GB ({ram.percent}%), CPU {cpu_percent}%, Speicher {disk_gb_free} GB frei.
Laufende Modelle: {', '.join(running_models) if running_models else 'Keine'}
Updates anstehend: OS: {upd.get('os', 0)}, Engine: {upd.get('engine', False)}, Swap: {upd.get('swap', False)}, Hermes: {upd.get('hermes', False)}
Offene Freigaben für Commander (Auftragsbuch): {auftraege_str}
Modell-Empfehlungen für diese Box: {rec_str}

Heutige Tech-News:
{news_text}

Kürzliche Ereignisse (Chronik):
{events_str}
"""

    prompt = f"""Du bist Lucy, die KI-Sysadmin der Box.
Dein Boss braucht einen gehaltvollen, aber gut formatierten Morgen-Bericht via Telegram. 

Regeln:
1. Wie geht's der Box? (Kurzer Hardware/Modelle Check)
2. News: Übersetze die Tech-News kurz auf Deutsch und übernimm IMMER die Quellen-URLs in deine Nachricht!
3. Updates: Sind welche offen?
4. Modelle & Aufgaben: Welche besseren Modelle gibt es? Wartest du auf Auftragsbestätigungen?
5. Benutze Emojis, mach es lesbar (Bulletpoints).
6. Chronik nur erwähnen, wenn es Probleme gab (sonst weglassen).
7. Antworte DIREKT mit dem Text der Nachricht (keine Einleitung).
8. SEI INFORMATIV aber nicht endlos lang.

Rohe Daten:
{evidence}
"""

    endpoint = "http://127.0.0.1:9001/v1/chat/completions" # Internes Gateway für model: auto

    # Da das Gateway (Router) auf :9010 lauscht, können wir auch das nutzen, 
    # aber Port 8080 (llama-swap engine) ist der direkteste Weg für den internen LLM-Call.
    model = "gpt-oss-120b" # Fallback, falls auto nicht geht, llama-swap routet das meist passend
    
    req_body = {
        "model": "auto",
        "messages": [
            {"role": "system", "content": "Du bist Lucy, KI-Sysadmin der Box."},
            {"role": "user", "content": prompt}
        ],
        "max_tokens": 1000,
        "temperature": 0.4
    }

    digest = ""
    try:
        async with httpx.AsyncClient(timeout=180.0) as client:
            resp = await client.post(endpoint, json=req_body)
            resp.raise_for_status()
            data = resp.json()
            digest = data["choices"][0]["message"].get("content", "").strip()
    except Exception as e:
        log.error(f"Fehler beim LLM-Aufruf für Sysadmin-Report: {e}")
        # Fallback, falls LLM nicht erreichbar ist
        digest = f"🤖 [LLM offline] Hier sind die rohen Daten:\n{evidence}"

    if digest:
        announce.notify_telegram("[🌅 Morgen-Digest]", digest)
        log.info("Sysadmin-Report via Telegram versendet.")
        return True
    return False


async def report_loop() -> None:
    """Täglicher Background-Task für den Sysadmin-Report um SCHEDULE_HOUR Uhr."""
    log.info("Sysadmin-Report Background-Loop gestartet.")
    while True:
        try:
            now = datetime.now()
            # Finde die Zeit bis zum nächsten SCHEDULE_HOUR:00
            target_hour = SCHEDULE_HOUR
            if now.hour >= target_hour:
                # Nächster Tag
                seconds_until = ((24 - now.hour - 1) * 3600) + ((60 - now.minute - 1) * 60) + (60 - now.second) + (target_hour * 3600)
            else:
                # Selber Tag
                seconds_until = ((target_hour - now.hour - 1) * 3600) + ((60 - now.minute - 1) * 60) + (60 - now.second)
                
            log.info(f"Sysadmin-Report: Warte {seconds_until} Sekunden bis zum nächsten Report.")
            await asyncio.sleep(seconds_until)
            
            await generate_and_send_report()
            
            # Warte kurz, um nicht sofort wieder zu triggern, falls es extrem schnell ging
            await asyncio.sleep(60) 
        except asyncio.CancelledError:
            break
        except Exception as e:
            log.error(f"Unerwarteter Fehler im Sysadmin-Report-Loop: {e}", exc_info=True)
            await asyncio.sleep(300)
