#!/usr/bin/env python3
"""Aria HTTP API - wird von n8n aufgerufen fuer Health/Restart/Status/Trigger"""
import os
import subprocess
import json
import threading
import time
from http.server import HTTPServer, BaseHTTPRequestHandler
from socketserver import ThreadingMixIn
from urllib.parse import urlparse, parse_qs
from prometheus_client import start_http_server, Counter, Histogram, generate_latest, CONTENT_TYPE_LATEST

# --- Prometheus Metrics ---
METRICS_PORT = 9877
api_requests_total = Counter('aria_api_requests_total', 'Total API requests', ['endpoint', 'status'])
triggers_total = Counter('aria_api_triggers_total', 'n8n trigger executions', ['trigger_name', 'result'])
request_duration = Histogram('aria_api_request_duration_seconds', 'Request duration', ['endpoint'])

PORT = 9876
SECRET = os.environ.get("ARIA_API_SECRET", "aria-default-secret")  # fallback nur lokal

TRIGGER_PROMPTS = {
    "morning_briefing": (
        "n8n Trigger: Guten Morgen! Bitte fuehre jetzt das Morning Briefing durch: "
        "1) Lies den neuesten Daily Log in aria-brain/06-Daily/ "
        "2) Checke Google Calendar fuer heutige Termine "
        "3) Pruefe Linear Issues (via MCP) - was ist offen, was ist ueberfaellig, was blockiert uns? "
        "4) Schicke Kais eine kompakte Zusammenfassung per Telegram: "
        "Termine heute | Offene/ueberfaellige Linear Issues (mit Issue-ID) | Prioritaet des Tages. "
        "Kein Roman, max 8 Zeilen. Sei direkt: wenn etwas zu lange offen ist, sag es."
    ),
    "ki_news": (
        "n8n Trigger: Zeit fuer das KI-News Briefing! Bitte recherchiere jetzt: "
        "Fokus auf Anthropic und Claude.ai - neue Modelle, Features, Pricing, API-Aenderungen, Blog-Posts. "
        "Danach kurz: Was gibt es sonst Relevantes (OpenAI, Google, Mistral) das uns als KI-Startup betrifft? "
        "Schicke Kais eine kompakte Zusammenfassung per Telegram (3-5 Punkte, kein Fliesstext). "
        "Quellen: anthropic.com/news, claude.ai, WebSearch."
    ),
    "evening_check": (
        "n8n Trigger: Vollstaendiger Abend-Systemcheck! Pruefe ALLES was automatisiert laeuft: "
        "SERVICES (systemctl status): aria-v3, aria-api, aria-chat-logger-sqlite, aria-chat-logger, aria-control, aria-health, "
        "aria-watchdog.timer, aria-git-sync.timer, aria-db-cleanup.timer, aria-vault-index.timer. "
        "N8N WORKFLOWS (letzte Execution + Status): Morning Briefing, KI-News Briefing, Abend-Check, Nightly Research, "
        "Offline-Watchdog, LinkedIn Poster, Instagram Poster, Instagram DM Webhook, alle Reminder-Workflows. "
        "VERBINDUNGEN testen: Gmail API, GMX IMAP (imap.gmx.net:993), IONOS IMAP (imap.ionos.de:993), "
        "Google Calendar API, Supabase (health check), GitHub API, n8n API, Linear API. "
        "OBSIDIAN: aria-git-sync letzter erfolgreicher Lauf? Brain-Dateien aktuell? "
        "TAGESBILANZ: Daily Log lesen, Linear Issues pruefen (offen/ueberfaellig/blockiert). "
        "Ergebnis: EINE Telegram-Nachricht mit zwei Teilen: "
        "Teil 1 — System-Status: Haken (ok) oder WARNUNG (konkret was kaputt ist, nicht nur 'Fehler'). "
        "Teil 2 — Tagesbilanz: Was lief heute, was ist offen, Prioritaet morgen. "
        "Maximal 15 Zeilen gesamt."
    ),
    "nightly_research": (
        "n8n Trigger: Nachtschicht-Recherche! Bitte pruefe jetzt: "
        "1) Gibt es neue Wettbewerber-Bewegungen (objego, hellohousing, immocloud)? "
        "2) Offene Tasks aus dem Daily Log die heute nicht erledigt wurden? "
        "3) Schreibe einen kurzen Nacht-Eintrag im Daily Log was du recherchiert hast. "
        "Kais muss dafuer NICHT aufgeweckt werden - nur dokumentieren."
    ),
}


BUSY_LOCK = "/root/.claude/aria_busy.lock"
BUSY_TIMEOUT = 3600  # seconds — lock older than this is stale

TELEGRAM_BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", "")
TELEGRAM_OWNER_CHAT_ID = os.environ.get("TELEGRAM_OWNER_CHAT_ID", os.environ.get("TELEGRAM_CHAT_ID",""))
QUEUE_RETRY_INTERVAL = 30  # seconds between retries
QUEUE_MAX_WAIT = 1800  # seconds (30 min) max wait before giving up


def is_busy():
    """Returns True if Aria has an active trigger lock."""
    if not os.path.exists(BUSY_LOCK):
        return False
    age = time.time() - os.path.getmtime(BUSY_LOCK)
    if age > BUSY_TIMEOUT:
        os.remove(BUSY_LOCK)
        return False
    return True


def is_claude_busy(session="aria"):
    """Returns True if Claude Code in the tmux session is currently processing.
    Detected via 'esc to interrupt' in the visible pane."""
    result = subprocess.run(
        ["tmux", "capture-pane", "-t", session, "-p"],
        capture_output=True, text=True
    )
    if result.returncode != 0:
        return False
    return "esc to interrupt" in result.stdout


def notify_owner(text):
    """Direct Telegram message to Kais (out-of-band, for queue errors)."""
    if not TELEGRAM_BOT_TOKEN:
        return
    try:
        import urllib.request, urllib.parse
        data = urllib.parse.urlencode({
            "chat_id": TELEGRAM_OWNER_CHAT_ID,
            "text": text,
        }).encode()
        urllib.request.urlopen(
            f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage",
            data=data, timeout=10
        )
    except Exception:
        pass


def _inject_prompt(prompt, session="aria"):
    """Low-level: clear input buffer then send prompt + Enter."""
    subprocess.run(["tmux", "send-keys", "-t", session, "", "C-u"], capture_output=True)
    subprocess.run(["tmux", "send-keys", "-t", session, prompt, "Enter"])


def _queue_worker(trigger_type, prompt, session="aria"):
    """Background worker: retry until Claude is idle or timeout."""
    start = time.time()
    while time.time() - start < QUEUE_MAX_WAIT:
        time.sleep(QUEUE_RETRY_INTERVAL)
        if subprocess.run(["tmux", "has-session", "-t", session], capture_output=True).returncode != 0:
            triggers_total.labels(trigger_name=trigger_type, result='offline').inc()
            notify_owner(f"Trigger {trigger_type} verworfen: Aria tmux offline.")
            return
        if not is_claude_busy(session):
            _inject_prompt(prompt, session)
            open(BUSY_LOCK, "w").close()
            triggers_total.labels(trigger_name=trigger_type, result='queued_ok').inc()
            return
    triggers_total.labels(trigger_name=trigger_type, result='queue_timeout').inc()
    notify_owner(
        f"Trigger {trigger_type} nach {QUEUE_MAX_WAIT//60} Min nicht zugestellt — "
        f"Claude war durchgehend busy. Schau bitte selbst rein."
    )


def send_tmux_prompt(prompt, trigger_type, session="aria"):
    """Send prompt to tmux if idle, otherwise queue for retry in background.
    Returns (result_str) one of: 'ok', 'queued', 'offline'."""
    if subprocess.run(["tmux", "has-session", "-t", session], capture_output=True).returncode != 0:
        return "offline"
    if is_claude_busy(session):
        threading.Thread(
            target=_queue_worker, args=(trigger_type, prompt, session), daemon=True
        ).start()
        return "queued"
    _inject_prompt(prompt, session)
    open(BUSY_LOCK, "w").close()
    return "ok"


class AriaHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        parsed = urlparse(self.path)
        params = parse_qs(parsed.query)
        path = parsed.path

        if path == "/metrics":
            data = generate_latest()
            self.send_response(200)
            self.send_header("Content-Type", CONTENT_TYPE_LATEST)
            self.end_headers()
            self.wfile.write(data)
            return

        token_header = self.headers.get("X-Auth-Token", "")
        token_param = params.get("token", [""])[0]
        if token_header != SECRET and token_param != SECRET:
            api_requests_total.labels(endpoint=path, status='401').inc()
            self.send(401, "unauthorized")
            return

        start = time.time()

        if path == "/health":
            result = subprocess.run(["tmux", "has-session", "-t", "aria"], capture_output=True)
            status = "online" if result.returncode == 0 else "offline"
            api_requests_total.labels(endpoint='/health', status='200').inc()
            self.send(200, status)

        elif path == "/restart":
            reason = params.get("reason", ["API"])[0][:50]
            subprocess.Popen(
                ["bash", "/root/aria/scripts/aria-restart.sh", "--reason", reason],
                stdout=open("/var/log/aria-watchdog.log", "a"),
                stderr=subprocess.STDOUT
            )
            api_requests_total.labels(endpoint='/restart', status='200').inc()
            self.send(200, "restart triggered")

        elif path == "/status":
            result = subprocess.run(["tmux", "has-session", "-t", "aria"], capture_output=True)
            status = "online" if result.returncode == 0 else "offline"
            api_requests_total.labels(endpoint='/status', status='200').inc()
            self.send(200, status)

        elif path == "/trigger":
            trigger_type = params.get("type", [""])[0]
            if trigger_type not in TRIGGER_PROMPTS:
                api_requests_total.labels(endpoint='/trigger', status='400').inc()
                self.send(400, f"unknown trigger type: {trigger_type}. valid: {', '.join(TRIGGER_PROMPTS.keys())}")
                return
            prompt = TRIGGER_PROMPTS[trigger_type]
            result = send_tmux_prompt(prompt, trigger_type)
            if result == "ok":
                triggers_total.labels(trigger_name=trigger_type, result='ok').inc()
                api_requests_total.labels(endpoint='/trigger', status='200').inc()
                self.send(200, f"triggered: {trigger_type}")
            elif result == "queued":
                triggers_total.labels(trigger_name=trigger_type, result='queued').inc()
                api_requests_total.labels(endpoint='/trigger', status='202').inc()
                self.send(202, f"queued: {trigger_type} — aria busy, will retry")
            else:
                triggers_total.labels(trigger_name=trigger_type, result='offline').inc()
                api_requests_total.labels(endpoint='/trigger', status='503').inc()
                self.send(503, "aria offline")

        elif path == "/backup":
            import datetime, shutil
            date = datetime.datetime.now().strftime("%Y-%m-%d")
            try:
                subprocess.run(
                    ["docker", "exec", "aria-n8n", "sh", "-c",
                     f"mkdir -p /home/node/.n8n/backups/{date} && n8n export:workflow --backup --output=/home/node/.n8n/backups/{date}/"],
                    check=True, capture_output=True, timeout=60
                )
                src = f"/var/lib/docker/volumes/n8n_n8n_data/_data/backups/{date}"
                dst = f"/root/aria/n8n/backups/{date}"
                if os.path.isdir(src):
                    if os.path.isdir(dst):
                        shutil.rmtree(dst)
                    shutil.copytree(src, dst)
                api_requests_total.labels(endpoint='/backup', status='200').inc()
                self.send(200, f"backup ok: {date}")
            except subprocess.CalledProcessError as e:
                api_requests_total.labels(endpoint='/backup', status='500').inc()
                self.send(500, f"backup failed: {e.stderr.decode()[:200] if e.stderr else 'unknown'}")
            except Exception as e:
                api_requests_total.labels(endpoint='/backup', status='500').inc()
                self.send(500, f"backup error: {e}")

        else:
            api_requests_total.labels(endpoint=path, status='404').inc()
            self.send(404, "unknown endpoint")

        request_duration.labels(endpoint=path).observe(time.time() - start)

    def send(self, code, msg):
        self.send_response(code)
        self.send_header("Content-Type", "text/plain")
        self.end_headers()
        self.wfile.write(msg.encode())

    def log_message(self, format, *args):
        pass  # Kein Logging-Spam


class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    daemon_threads = True

if __name__ == "__main__":
    start_http_server(METRICS_PORT, addr="127.0.0.1")
    print(f"Prometheus metrics auf 127.0.0.1:{METRICS_PORT}")
    # Nur Docker-Bridge - n8n kann zugreifen, Internet nicht
    import socket
    BIND_HOST = os.environ.get("ARIA_API_BIND", "")
    if not BIND_HOST:
        for candidate in ("172.18.0.1", "172.17.0.1", "127.0.0.1"):
            try:
                s = socket.socket(); s.bind((candidate, 0)); s.close()
                BIND_HOST = candidate; break
            except OSError:
                continue
    server = ThreadedHTTPServer((BIND_HOST, PORT), AriaHandler)
    print(f"Aria API laeuft auf {BIND_HOST}:{PORT}")
    server.serve_forever()
