"""CLI entry point for HiveMind."""
from __future__ import annotations

import argparse
import asyncio
import logging
import sys
from pathlib import Path

from hivemind.config import Config
from hivemind.node import Node


def setup_logging(verbose: bool = False, log_file: str | None = None) -> None:
    level = logging.DEBUG if verbose else logging.INFO
    handlers: list[logging.Handler] = [logging.StreamHandler()]
    if log_file:
        fh = logging.FileHandler(log_file, encoding="utf-8")
        fh.setLevel(level)
        fh.setFormatter(logging.Formatter(
            "%(asctime)s [%(levelname)s] %(name)s: %(message)s",
            datefmt="%Y-%m-%d %H:%M:%S",
        ))
        handlers.append(fh)
    logging.basicConfig(
        level=level,
        format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
        datefmt="%H:%M:%S",
        handlers=handlers,
    )

    if log_file:
        # Ungecatchte Exceptions und stderr in Logdatei schreiben
        _log_file_path = log_file

        def _excepthook(exc_type, exc_value, exc_tb):
            import traceback
            logger = logging.getLogger("hivemind")
            tb_str = "".join(traceback.format_exception(exc_type, exc_value, exc_tb))
            logger.critical("Ungecatchte Exception:\n%s", tb_str)
            # Auch auf stderr ausgeben (normales Verhalten beibehalten)
            sys.__excepthook__(exc_type, exc_value, exc_tb)

        sys.excepthook = _excepthook

        # stderr auf Logdatei tee-en (Tracebacks von C-Extensions etc. erfassen)
        class _StderrTee:
            def __init__(self, original, log_path):
                self._orig = original
                self._log_path = log_path
            def write(self, msg):
                self._orig.write(msg)
                if msg.strip():
                    try:
                        with open(self._log_path, "a", encoding="utf-8") as f:
                            f.write(msg)
                    except Exception:
                        pass
            def flush(self):
                self._orig.flush()
            def __getattr__(self, name):
                return getattr(self._orig, name)

        sys.stderr = _StderrTee(sys.__stderr__, log_file)


async def run_node(args: argparse.Namespace) -> None:
    """Start the HiveMind node."""
    import time as _time
    config = Config.load(args.config)
    base_dir = Path(args.config).parent

    # ── Crash-Detection (Lockfile-Mechanismus) ────────────────────────────────
    # Existiert die Markerdatei beim Start noch, wurde HiveMind letztes Mal
    # nicht sauber beendet (Crash, Kill-Signal etc.).
    _start_marker = base_dir / ".hivemind_started"
    _crashed = _start_marker.exists()
    # Letzten Log-Eintrag im logs/-Unterordner suchen
    _logs_dir = base_dir / "logs"
    try:
        _recent = max(_logs_dir.glob("hivemind_*.log"),
                      key=lambda p: p.stat().st_mtime, default=None) if _logs_dir.exists() else None
        _log_path = str(_recent) if _recent else str(base_dir / "hivemind.log")
    except Exception:
        _log_path = str(base_dir / "hivemind.log")
    try:
        _start_marker.write_text(str(_time.time()), encoding="utf-8")
    except Exception:
        pass
    # ─────────────────────────────────────────────────────────────────────────

    print(f"HiveMind v{__import__('hivemind').__version__}")
    print(f"  Config: {args.config}")
    print(f"  Model:  {config.model.path or '(nicht konfiguriert)'}")
    print()

    node = Node(config, base_dir=base_dir)
    # Crash-Info am Node verfügbar machen (web.py /api/crash-detected)
    node._crashed_on_startup = _crashed
    node._last_log_path = _log_path if Path(_log_path).exists() else ""

    try:
        await node.start()

        print(f"\n[OK] Node '{node.name}' bereit!")
        if config.gateway.api.enabled:
            host = config.gateway.api.host
            if host == "0.0.0.0":
                host = "127.0.0.1"
            print(f"  Dashboard: http://{host}:{config.gateway.api.port}")
        print()

        tasks = []
        server = None  # uvicorn server instance (für sauberes should_exit)

        # Shutdown-Event: wird von /stop (Web-API) gesetzt um sauber zu beenden
        _stop_event = asyncio.Event()
        node._stop_event = _stop_event

        # Determine private key path for web UI
        private_key_path = base_dir / "hivemind_update_key.private"
        if not private_key_path.exists():
            # Also check parent dirs
            for p in [base_dir.parent, Path.home() / ".openclaw" / "workspace"]:
                candidate = p / "hivemind_update_key.private"
                if candidate.exists():
                    private_key_path = candidate
                    break

        # Start Web UI + API gateway if enabled
        if config.gateway.api.enabled:
            import uvicorn
            from hivemind.ui.web import create_web_app

            app = create_web_app(node, private_key_path=private_key_path)
            uv_config = uvicorn.Config(
                app,
                host=config.gateway.api.host,
                port=config.gateway.api.port,
                log_level="warning",
            )
            server = uvicorn.Server(uv_config)
            tasks.append(asyncio.create_task(server.serve()))
            logging.getLogger("hivemind").info(
                "Web UI: http://%s:%d", config.gateway.api.host, config.gateway.api.port
            )

        # Start Telegram gateway if enabled
        if config.gateway.telegram.enabled and config.gateway.telegram.token:
            from hivemind.gateway.telegram import TelegramGateway

            tg = TelegramGateway(config.gateway.telegram.token, node)
            node._telegram_gateway = tg

            async def _tg_run(gw=tg):
                try:
                    await gw.start()
                except Exception as exc:
                    logging.getLogger("hivemind.gateway.telegram").error(
                        "Telegram gateway task failed: %s", exc, exc_info=True
                    )
                finally:
                    if getattr(node, "_telegram_gateway", None) is gw:
                        node._telegram_gateway = None

            tg_task = asyncio.create_task(_tg_run())
            node._telegram_gateway_task = tg_task
            tasks.append(tg_task)

        # Start TUI or daemon mode
        if args.daemon:
            logging.getLogger("hivemind").info("Running in daemon mode (Ctrl+C to stop)")
            # Auto-open dashboard in browser
            if config.gateway.api.enabled:
                import webbrowser
                host = config.gateway.api.host
                if host == "0.0.0.0":
                    host = "127.0.0.1"
                url = f"http://{host}:{config.gateway.api.port}"
                try:
                    webbrowser.open(url)
                except Exception:
                    pass
            try:
                if tasks:
                    _stop_waiter = asyncio.create_task(_stop_event.wait())
                    await asyncio.wait(
                        tasks + [_stop_waiter],
                        return_when=asyncio.FIRST_COMPLETED,
                    )
                    # Stop-Event oder Task beendet → sauberes Herunterfahren
                    _stop_waiter.cancel()
                    try:
                        await _stop_waiter
                    except asyncio.CancelledError:
                        pass
                    # uvicorn sauber beenden (kein cancel, sondern should_exit)
                    if server is not None:
                        server.should_exit = True
                    # Alle Tasks sauber auslaufen lassen (max. 8 Sekunden)
                    if tasks:
                        _remaining = [t for t in tasks if not t.done()]
                        if _remaining:
                            await asyncio.wait(_remaining, timeout=8.0)
                        for _t in tasks:
                            if not _t.done():
                                _t.cancel()
                                try:
                                    await _t
                                except (asyncio.CancelledError, Exception):
                                    pass
                else:
                    await _stop_event.wait()
            except asyncio.CancelledError:
                # Ctrl+C: uvicorn ebenfalls sauber beenden
                if server is not None:
                    server.should_exit = True
                    await asyncio.sleep(0.5)
        else:
            from hivemind.ui.tui import run_tui
            try:
                await run_tui(node)
            except (EOFError, KeyboardInterrupt):
                pass

        await node.stop()

    finally:
        # Sauber beendet → Markerdatei löschen.
        # Bleibt die Datei stehen, zeigt das beim nächsten Start einen Crash an.
        try:
            _start_marker.unlink(missing_ok=True)
        except Exception:
            pass


def open_dashboard(args: argparse.Namespace) -> None:
    """Open the HiveMind dashboard in the default browser."""
    import webbrowser
    config = Config.load(args.config)
    host = config.gateway.api.host
    port = config.gateway.api.port
    if host == "0.0.0.0":
        host = "127.0.0.1"
    url = f"http://{host}:{port}"
    print(f"🧠 Öffne Dashboard: {url}")
    webbrowser.open(url)


def main():
    parser = argparse.ArgumentParser(
        prog="hivemind",
        description="🧠 HiveMind — Dezentrale P2P-AI",
    )
    sub = parser.add_subparsers(dest="command")

    # Default run (no subcommand)
    parser.add_argument(
        "-c", "--config",
        default="config.yaml",
        help="Path to config file (default: config.yaml)",
    )
    parser.add_argument(
        "-v", "--verbose",
        action="store_true",
        help="Enable debug logging",
    )
    parser.add_argument(
        "-d", "--daemon",
        action="store_true",
        help="Run in daemon mode (no TUI, API/Telegram only)",
    )

    # dashboard subcommand
    dash_parser = sub.add_parser("dashboard", help="Dashboard im Browser öffnen")
    dash_parser.add_argument("-c", "--config", default="config.yaml")

    # import-hf subcommand
    hf_parser = sub.add_parser("import-hf", help="HuggingFace Dataset in RAG importieren")
    hf_parser.add_argument("url", help="HuggingFace Dataset URL oder repo-id")
    hf_parser.add_argument("-c", "--config", default="config.yaml")
    hf_parser.add_argument("--max-rows", type=int, default=5000, help="Max Rows (default 5000)")
    hf_parser.add_argument("--split", default="train", help="Dataset Split (default train)")
    hf_parser.add_argument("--fields", nargs="*", help="Text-Felder (auto-detect wenn leer)")

    args = parser.parse_args()
    # Resolve config to absolute path — also check script's own directory
    config_path = Path(args.config)
    if not config_path.exists():
        # Try relative to the hivemind package directory
        pkg_dir = Path(__file__).resolve().parent.parent
        alt = pkg_dir / args.config
        if alt.exists():
            config_path = alt
    args.config = str(config_path.resolve())
    config_dir = Path(args.config).parent
    if args.command != "dashboard":
        from datetime import datetime as _dt
        _logs_dir = config_dir / "logs"
        try:
            _logs_dir.mkdir(exist_ok=True)
            _ts = _dt.now().strftime("%Y%m%d_%H%M%S")
            log_file = str(_logs_dir / f"hivemind_{_ts}.log")
            Path(log_file).touch()
            # Maximal 20 Logfiles behalten — älteste löschen
            _all_logs = sorted(_logs_dir.glob("hivemind_*.log"),
                               key=lambda p: p.stat().st_mtime)
            while len(_all_logs) > 20:
                try:
                    _all_logs.pop(0).unlink()
                except OSError:
                    break
        except (PermissionError, OSError):
            log_file = None
    else:
        log_file = None
    setup_logging(getattr(args, "verbose", False), log_file=log_file)

    # Starthinweis in Logdatei
    if log_file:
        logging.getLogger("hivemind").info(
            "HiveMind gestartet (command=%s, config=%s)",
            args.command or "run",
            getattr(args, "config", "-"),
        )

    if args.command == "dashboard":
        open_dashboard(args)
        return

    if args.command == "import-hf":
        from hivemind.rag import RAGStore
        config = Config.load(args.config)
        base_dir = Path(args.config).parent
        rag = RAGStore(data_dir=base_dir / config.rag.data_dir, chunk_size=config.rag.chunk_size)
        print(f"Importiere HuggingFace Dataset: {args.url}")
        print(f"  Split: {args.split} | Max Rows: {args.max_rows}")
        result = rag.import_huggingface(args.url, args.max_rows, args.fields, args.split)
        if "error" in result:
            print(f"  FEHLER: {result['error']}")
            sys.exit(1)
        print(f"  Rows importiert: {result['rows_imported']} / {result['rows_total']}")
        print(f"  Text-Felder: {', '.join(result['text_fields'])}")
        print(f"  Chunks: {result['chunks']}")
        print(f"  Groesse: {result['size_mb']} MB")
        print(f"  Dokument: {result['document']}")
        print("Fertig!")
        return

    try:
        asyncio.run(run_node(args))
    except KeyboardInterrupt:
        print("\nHiveMind stopped.")


if __name__ == "__main__":
    main()
