"""
Torchwood Chat-Moderation – David verdient dort Geld.

- Fenster-Titel: PC-Aktivität (Chrome o.ä.)
- Chat-Mitlesen: eigener Wächter-Browser (Playwright/Edge-Profil), liest sichtbare Chat-Zeilen
"""

from __future__ import annotations

import json
import os
import re
import subprocess
import sys
import time
import webbrowser
from pathlib import Path

from jam.brain import JamBrain, JamState

ROOT = Path(__file__).resolve().parent.parent
TORCHWOOD_CONFIG = ROOT / "data" / "torchwood-moderator.json"
TORCHWOOD_CHAT_SNAPSHOT = ROOT / "data" / "torchwood-chat-snapshot.json"
TORCHWOOD_CHAT_PID = ROOT / "data" / "torchwood-chat-watcher.pid"
TORCHWOOD_WATCHER_SCRIPT = ROOT / "scripts" / "torchwood_chat_watcher.py"
TORCHWOOD_URL = "https://torchwood.app/de"

SKIP_CHAT_LINE = re.compile(
    r"(passwort|password|pin\s*:|kreditkarte|seed\s|mnemonic|private\s*key|"
    r"api[_-]?key|login\s*@|@[a-z0-9.-]+\.(com|de)\b)",
    re.I,
)

TORCHWOOD_HINT = re.compile(
    r"(torchwood|torch\s*wood|moderator|mod-panel|mod panel|chatmod)",
    re.I,
)


def _load_cfg() -> dict:
    if TORCHWOOD_CONFIG.is_file():
        try:
            return json.loads(TORCHWOOD_CONFIG.read_text(encoding="utf-8"))
        except (json.JSONDecodeError, OSError):
            pass
    return {
        "url": TORCHWOOD_URL,
        "role": "chat_moderator",
        "owner": "David",
        "watch_enabled": True,
        "chat_read_enabled": True,
    }


def _save_cfg(cfg: dict) -> None:
    TORCHWOOD_CONFIG.parent.mkdir(parents=True, exist_ok=True)
    TORCHWOOD_CONFIG.write_text(
        json.dumps(cfg, ensure_ascii=False, indent=2),
        encoding="utf-8",
    )


def is_watch_enabled(state: JamState) -> bool:
    cfg = _load_cfg()
    return bool(getattr(state, "jam_torchwood_watch", cfg.get("watch_enabled", True)))


def is_torchwood_title(title: str) -> bool:
    return bool(title and TORCHWOOD_HINT.search(title))


def classify_torchwood(title: str, exe: str = "") -> str | None:
    blob = f"{title} {exe}"
    if TORCHWOOD_HINT.search(blob):
        return "torchwood_mod"
    if "torchwood.app" in blob.lower():
        return "torchwood_mod"
    return None


def on_session_learn(state: JamState, *, seconds: float, title: str) -> None:
    """Extra-Gedächtnis wenn David auf Torchwood moderiert."""
    state.torchwood_mod_seconds = round(
        float(getattr(state, "torchwood_mod_seconds", 0) or 0) + seconds, 1
    )
    state.torchwood_mod_sessions = int(getattr(state, "torchwood_mod_sessions", 0)) + 1
    state.last_torchwood_mod_at = time.time()
    state.learn_word("torchwood")
    state.learn_word("moderator")
    state.learn_word("chatmoderation")
    state.remember(
        f"David moderiert Torchwood ({int(seconds)}s): {title[:60]}"
    )
    cfg = _load_cfg()
    log = list(cfg.get("sessions", []))
    log.append(
        {
            "at": time.time(),
            "seconds": round(seconds, 1),
            "title": title[:100],
        }
    )
    cfg["sessions"] = log[-200:]
    cfg["total_seconds"] = round(
        float(cfg.get("total_seconds", 0)) + seconds, 1
    )
    _save_cfg(cfg)


def is_chat_read_enabled(state: JamState) -> bool:
    cfg = _load_cfg()
    return bool(getattr(state, "jam_torchwood_chat_read", cfg.get("chat_read_enabled", True)))


def is_watcher_running() -> bool:
    if not TORCHWOOD_CHAT_PID.is_file():
        return False
    try:
        pid = int(TORCHWOOD_CHAT_PID.read_text(encoding="utf-8").strip())
        os.kill(pid, 0)
        return True
    except (OSError, ValueError):
        TORCHWOOD_CHAT_PID.unlink(missing_ok=True)
        return False


def stop_chat_watcher() -> str:
    if not is_watcher_running():
        return "Torchwood-Chat-Wächter läuft nicht."
    try:
        pid = int(TORCHWOOD_CHAT_PID.read_text(encoding="utf-8").strip())
        subprocess.run(["taskkill", "/PID", str(pid), "/F"], capture_output=True, timeout=15)
    except (OSError, ValueError):
        pass
    TORCHWOOD_CHAT_PID.unlink(missing_ok=True)
    return "Torchwood-Chat-Wächter gestoppt."


def start_chat_watcher() -> tuple[bool, str]:
    """Startet Browser mit Profil – dort Chat mitlesen (David loggt sich ein)."""
    if is_watcher_running():
        return True, "Chat-Wächter läuft schon."
    if not TORCHWOOD_WATCHER_SCRIPT.is_file():
        return False, "Skript torchwood_chat_watcher.py fehlt."
    py = ROOT / ".venv" / "Scripts" / "pythonw.exe"
    if not py.is_file():
        py = Path(sys.executable)
    try:
        subprocess.Popen(
            [str(py), str(TORCHWOOD_WATCHER_SCRIPT)],
            cwd=str(ROOT),
            creationflags=getattr(subprocess, "CREATE_NEW_PROCESS_GROUP", 0)
            | getattr(subprocess, "DETACHED_PROCESS", 0),
            close_fds=True,
        )
        time.sleep(2)
        if is_watcher_running():
            return True, "Torchwood-Chat-Wächter gestartet – im **Edge-Fenster** anmelden."
        return True, "Wächter startet… gleich Edge-Fenster mit torchwood.app/de."
    except OSError as e:
        return False, str(e)


def _ingest_new_messages(brain: JamBrain, messages: list) -> int:
    state = brain.state
    seen = set(getattr(state, "torchwood_chat_seen_hashes", None) or [])
    if not isinstance(seen, set):
        seen = set(seen) if seen else set()
    added = 0
    chat_log = list(getattr(state, "torchwood_chat_learned", []) or [])
    for item in messages[-40:]:
        if not isinstance(item, dict):
            continue
        text = (item.get("text") or "").strip()
        if len(text) < 2 or len(text) > 350:
            continue
        if SKIP_CHAT_LINE.search(text):
            continue
        key = text[:200]
        if key in seen:
            continue
        seen.add(key)
        chat_log.append({"text": text[:300], "at": item.get("at", time.time())})
        state.remember(f"Torchwood-Chat: {text[:90]}")
        for word in re.findall(r"[A-Za-zÄÖÜäöüß]{4,}", text)[:4]:
            state.learn_word(word.lower())
        added += 1
    if added:
        state.torchwood_chat_learned = chat_log[-150:]
        state.torchwood_chat_messages_seen = int(
            getattr(state, "torchwood_chat_messages_seen", 0)
        ) + added
        state.torchwood_chat_seen_hashes = list(seen)[-500:]
        state.add_xp(min(20, added * 2))
        brain.save()
    return added


def poll_chat_from_file(brain: JamBrain) -> int:
    """Liest neue Chat-Zeilen aus dem Wächter-Snapshot."""
    if not is_chat_read_enabled(brain.state):
        return 0
    if not TORCHWOOD_CHAT_SNAPSHOT.is_file():
        return 0
    try:
        data = json.loads(TORCHWOOD_CHAT_SNAPSHOT.read_text(encoding="utf-8"))
    except (json.JSONDecodeError, OSError):
        return 0
    return _ingest_new_messages(brain, list(data.get("messages", [])))


def open_torchwood(brain: JamBrain, *, new_tab: bool = True, with_chat_read: bool = True) -> str:
    cfg = _load_cfg()
    url = cfg.get("url", TORCHWOOD_URL)
    brain.state.jam_torchwood_watch = True
    brain.state.jam_torchwood_chat_read = with_chat_read
    cfg["chat_read_enabled"] = with_chat_read
    _save_cfg(cfg)

    parts: list[str] = []
    if with_chat_read and is_chat_read_enabled(brain.state):
        ok, msg = start_chat_watcher()
        parts.append(msg if ok else f"Chat-Wächter: {msg}")
        parts.append(
            "Melde dich im **Torchwood-Fenster von Jam** an (Edge-Profil) – "
            "dort lese ich den Chat mit."
        )
    else:
        try:
            webbrowser.open(url, new=2 if new_tab else 0)
            parts.append(f"Torchwood im normalen Browser: {url}")
        except OSError as e:
            return f"Torchwood lässt sich nicht öffnen: {e}"[:400]

    brain.state.remember(f"Torchwood geöffnet + Chat-Lesen: {with_chat_read}")
    brain.state.last_action = "torchwood_open_chat"
    brain.save()
    return (
        " ".join(parts)
        + " PC-Lernen zählt deine Schicht. "
        "„Torchwood letzte Nachrichten“ / „Torchwood Statistik“."
    )[:720]


def last_chat_lines(state: JamState, n: int = 5) -> str:
    log = list(getattr(state, "torchwood_chat_learned", []) or [])
    if not log:
        snap = {}
        if TORCHWOOD_CHAT_SNAPSHOT.is_file():
            try:
                snap = json.loads(TORCHWOOD_CHAT_SNAPSHOT.read_text(encoding="utf-8"))
            except (json.JSONDecodeError, OSError):
                pass
        msgs = snap.get("messages", [])[-n:]
        if msgs:
            lines = [m.get("text", "")[:80] for m in msgs if isinstance(m, dict)]
            return "Letzte Chat-Zeilen (live): " + " | ".join(lines[-n:])
        return "Noch keine Chat-Zeilen – Wächter starten und auf Torchwood einloggen."
    tail = [x.get("text", "")[:80] for x in log[-n:]]
    return "Letzte Torchwood-Chat (gelernt): " + " | ".join(tail)


def stats_line(state: JamState) -> str:
    sec = float(getattr(state, "torchwood_mod_seconds", 0) or 0)
    n = int(getattr(state, "torchwood_mod_sessions", 0))
    if sec < 60:
        return f"Torchwood-Mod: {int(sec)}s ({n} Schichten)"
    return f"Torchwood-Mod: {int(sec // 60)} Min ({n} Schichten)"


def summary(state: JamState) -> str:
    sec = float(getattr(state, "torchwood_mod_seconds", 0) or 0)
    cfg = _load_cfg()
    total_cfg = float(cfg.get("total_seconds", 0))
    chat_n = int(getattr(state, "torchwood_chat_messages_seen", 0))
    watcher = "läuft" if is_watcher_running() else "aus"
    return (
        f"**Torchwood Chat-Moderator:** {stats_line(state)}. "
        f"Chat gelesen: **{chat_n}** Zeilen, Wächter **{watcher}**. "
        f"{last_chat_lines(state, 3)}"
    )[:720]


def enable_watch(brain: JamBrain) -> str:
    brain.state.jam_torchwood_watch = True
    cfg = _load_cfg()
    cfg["watch_enabled"] = True
    _save_cfg(cfg)
    brain.state.remember("Torchwood-Zuschauen: AN")
    brain.save()
    brain.state.jam_torchwood_chat_read = True
    cfg["chat_read_enabled"] = True
    _save_cfg(cfg)
    start_chat_watcher()
    return (
        "Torchwood-Zuschauen + **Chat-Mitlesen** ist **an**. "
        "Edge-Fenster: torchwood.app/de – dort einloggen."
    )


def disable_watch(brain: JamBrain) -> str:
    brain.state.jam_torchwood_watch = False
    brain.state.jam_torchwood_chat_read = False
    cfg = _load_cfg()
    cfg["watch_enabled"] = False
    cfg["chat_read_enabled"] = False
    _save_cfg(cfg)
    stop_chat_watcher()
    brain.save()
    return "Torchwood-Zuschauen und Chat-Mitlesen sind aus."


def hears_torchwood(text: str) -> bool:
    h = text.lower().replace("jam", " ")
    return any(
        w in h
        for w in (
            "torchwood",
            "torch wood",
            "chat moderator",
            "chatmoderator",
            "moderator seite",
            "mod schicht",
        )
    )


def handle_speech(brain: JamBrain, heard: str) -> str | None:
    if not hears_torchwood(heard):
        return None
    h = heard.lower()
    if any(w in h for w in ("letzte", "nachricht", "chat zeig", "was steht")):
        poll_chat_from_file(brain)
        return last_chat_lines(brain.state, 6)[:720]
    if any(w in h for w in ("statistik", "stat", "wie lange", "übersicht", "uebersicht", "stunden")):
        poll_chat_from_file(brain)
        return summary(brain.state)
    if any(w in h for w in ("aus", "stop", "nicht mehr")):
        return disable_watch(brain)
    if any(w in h for w in ("mitlesen", "chat lesen", "chat mitlesen")):
        from jam import pc_activity_learn

        pc_activity_learn.enable_learn(brain)
        return enable_watch(brain)
    if any(w in h for w in ("öffnen", "oeffnen", "open", "anmelden", "login", "start")):
        return open_torchwood(brain, with_chat_read=True)
    if any(w in h for w in ("zuschau", "lern", "merk", "beobacht")):
        from jam import pc_activity_learn

        pc_activity_learn.enable_learn(brain)
        enable_watch(brain)
        return open_torchwood(brain, with_chat_read=True)
    return open_torchwood(brain, with_chat_read=True)
