"""CLI commands for the google_meet plugin.

Wires ``hermes meet <subcommand>``:
  setup       — preflight playwright, chromium, auth file, print fixes
  auth        — open a browser to sign into Google, save storage state
  join <url>  — join a Meet URL synchronously (also callable from the agent)
  status      — print current bot state
  transcript  — print the transcript
  stop        — leave the current meeting
"""

from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path
from typing import Optional

from hermes_constants import get_hermes_home

from plugins.google_meet import process_manager as pm
from plugins.google_meet.meet_bot import _is_safe_meet_url


def _auth_state_path() -> Path:
    return Path(get_hermes_home()) / "workspace" / "meetings" / "auth.json"


# ---------------------------------------------------------------------------
# argparse wiring
# ---------------------------------------------------------------------------

def register_cli(subparser: argparse.ArgumentParser) -> None:
    """Build the ``hermes meet`` argparse tree.

    Called by :func:`_register_cli_commands` at plugin load time.
    """
    subs = subparser.add_subparsers(dest="meet_command")

    subs.add_parser("setup", help="Preflight: playwright, chromium, auth")

    inst_p = subs.add_parser(
        "install",
        help="Install prerequisites (pip deps, Chromium, platform audio tools)",
    )
    inst_p.add_argument(
        "--realtime", action="store_true",
        help="Also install realtime audio tools (pulseaudio-utils on Linux, BlackHole+ffmpeg on macOS). Uses sudo/brew, prompts before invoking either.",
    )
    inst_p.add_argument(
        "--yes", "-y", action="store_true",
        help="Answer yes to all prompts (use with care; will run sudo apt-get or brew without asking).",
    )

    subs.add_parser("auth", help="Sign in to Google and save session state")

    join_p = subs.add_parser("join", help="Join a Meet URL")
    join_p.add_argument("url", help="https://meet.google.com/...")
    join_p.add_argument("--guest-name", default="Hermes Agent")
    join_p.add_argument("--duration", default=None, help="e.g. 30m, 2h, 90s")
    join_p.add_argument("--headed", action="store_true", help="show browser")
    join_p.add_argument(
        "--mode", choices=("transcribe", "realtime"), default="transcribe",
        help="transcribe (default, listen-only) or realtime (speak via OpenAI Realtime)"
    )
    join_p.add_argument(
        "--node", default=None,
        help="remote node name, or 'auto' to use the sole registered node"
    )

    subs.add_parser("status", help="Print current Meet bot state")

    tr_p = subs.add_parser("transcript", help="Print the scraped transcript")
    tr_p.add_argument("--last", type=int, default=None)

    say_p = subs.add_parser("say", help="Speak text in an active realtime meeting")
    say_p.add_argument("text", help="what to say")
    say_p.add_argument("--node", default=None)

    subs.add_parser("stop", help="Leave the current meeting")

    # v3: remote node host management.
    node_p = subs.add_parser(
        "node",
        help="Manage remote meet node hosts (run/list/approve/remove/status/ping)",
    )
    try:
        from plugins.google_meet.node.cli import register_cli as _register_node_cli
        _register_node_cli(node_p)
    except Exception as e:  # pragma: no cover — defensive
        # If the node module fails to import for any reason (optional dep
        # missing at import time etc.), leave the subparser present but
        # flag it. The argparse dispatch will surface a clear error.
        def _node_unavailable(args):
            print(f"hermes meet node: module unavailable ({e})")
            return 1
        node_p.set_defaults(func=_node_unavailable)

    subparser.set_defaults(func=meet_command)


# ---------------------------------------------------------------------------
# Dispatch
# ---------------------------------------------------------------------------

def meet_command(args: argparse.Namespace) -> int:
    sub = getattr(args, "meet_command", None)
    if not sub:
        print("usage: hermes meet {setup,auth,join,status,transcript,say,stop,node}")
        return 2
    if sub == "setup":
        return _cmd_setup()
    if sub == "install":
        return _cmd_install(
            realtime=bool(getattr(args, "realtime", False)),
            assume_yes=bool(getattr(args, "yes", False)),
        )
    if sub == "auth":
        return _cmd_auth()
    if sub == "join":
        return _cmd_join(
            url=args.url,
            guest_name=args.guest_name,
            duration=args.duration,
            headed=args.headed,
            mode=getattr(args, "mode", "transcribe"),
            node=getattr(args, "node", None),
        )
    if sub == "status":
        return _cmd_status()
    if sub == "transcript":
        return _cmd_transcript(last=args.last)
    if sub == "say":
        return _cmd_say(text=args.text, node=getattr(args, "node", None))
    if sub == "stop":
        return _cmd_stop()
    if sub == "node":
        # Dispatch was set by the node cli's register_cli; fall through to
        # whatever its subparsers wired.
        fn = getattr(args, "func", None)
        if fn is None or fn is meet_command:
            print("usage: hermes meet node {run,list,approve,remove,status,ping}")
            return 2
        return fn(args)
    print(f"unknown subcommand: {sub}")
    return 2


# ---------------------------------------------------------------------------
# Subcommand handlers
# ---------------------------------------------------------------------------

def _cmd_setup() -> int:
    import platform as _p

    print("google_meet preflight")
    print("---------------------")

    system = _p.system()
    system_ok = system in {"Linux", "Darwin"}
    print(f"  platform       : {system}  [{'ok' if system_ok else 'unsupported'}]")

    try:
        import playwright  # noqa: F401
        pw_ok = True
        pw_msg = "installed"
    except ImportError:
        pw_ok = False
        pw_msg = "NOT installed — run: pip install playwright"
    print(f"  playwright     : {pw_msg}")

    chromium_ok = False
    chromium_msg = "unknown"
    if pw_ok:
        try:
            from playwright.sync_api import sync_playwright
            with sync_playwright() as p:
                try:
                    exe = p.chromium.executable_path
                    if exe and Path(exe).exists():
                        chromium_ok = True
                        chromium_msg = f"ok ({exe})"
                    else:
                        chromium_msg = (
                            "not installed — run: "
                            "python -m playwright install chromium"
                        )
                except Exception as e:
                    chromium_msg = f"probe failed: {e}"
        except Exception as e:
            chromium_msg = f"probe failed: {e}"
    print(f"  chromium       : {chromium_msg}")

    auth_path = _auth_state_path()
    auth_ok = auth_path.is_file()
    print(
        "  google auth    : "
        + (f"ok ({auth_path})" if auth_ok else "not saved — run: hermes meet auth")
    )

    print()
    all_ok = system_ok and pw_ok and chromium_ok
    if all_ok:
        print(
            "ready. Join a meeting:  "
            "hermes meet join https://meet.google.com/abc-defg-hij"
        )
    else:
        print("not ready yet — fix the items above.")
    return 0 if all_ok else 1


def _cmd_install(*, realtime: bool, assume_yes: bool) -> int:
    """Install the plugin's prerequisites.

    Always: pip install playwright + websockets, then
    ``python -m playwright install chromium``.

    With ``--realtime``: also install the platform audio bridge deps.
      Linux : ``sudo apt-get install -y pulseaudio-utils``
      macOS : ``brew install blackhole-2ch ffmpeg``  (+ remind the user
              to select BlackHole as the default input device manually)

    Prompts before every package-manager invocation unless ``--yes``.
    Refuses to run on Windows.
    """
    import platform as _p
    import shutil as _shutil
    import subprocess as _sp

    system = _p.system()
    if system not in {"Linux", "Darwin"}:
        print(f"google_meet install: {system} is not supported (linux/macos only)")
        return 1

    def _confirm(prompt: str) -> bool:
        if assume_yes:
            return True
        try:
            ans = input(f"{prompt} [y/N] ").strip().lower()
        except EOFError:
            return False
        return ans in {"y", "yes"}

    print("google_meet install")
    print("-------------------")

    # 1) pip deps — always safe, venv-scoped.
    pip_pkgs = ["playwright", "websockets"]
    print(f"\n[1/3] pip install: {' '.join(pip_pkgs)}")
    try:
        res = _sp.run(
            [sys.executable, "-m", "pip", "install", "--upgrade", *pip_pkgs],
            check=False,
        )
        if res.returncode != 0:
            print("  pip install failed")
            return 1
    except Exception as e:
        print(f"  pip install failed: {e}")
        return 1

    # 2) Playwright browsers — pulls chromium (~300MB first run).
    print("\n[2/3] python -m playwright install chromium")
    try:
        res = _sp.run(
            [sys.executable, "-m", "playwright", "install", "chromium"],
            check=False,
        )
        if res.returncode != 0:
            print("  playwright install failed (may already be installed)")
    except Exception as e:
        print(f"  playwright install failed: {e}")
        return 1

    # 3) Platform audio deps for realtime mode.
    if realtime:
        print("\n[3/3] realtime audio deps")
        if system == "Linux":
            if _shutil.which("paplay") and _shutil.which("pactl"):
                print("  pulseaudio-utils already installed.")
            else:
                if not _confirm(
                    "  install pulseaudio-utils? this runs `sudo apt-get install -y pulseaudio-utils`"
                ):
                    print("  skipped (you can run it manually later)")
                else:
                    cmd = ["sudo", "apt-get", "install", "-y", "pulseaudio-utils"]
                    print(f"  $ {' '.join(cmd)}")
                    res = _sp.run(cmd, check=False)
                    if res.returncode != 0:
                        print("  apt install failed — install pulseaudio-utils manually")
        elif system == "Darwin":
            have_bh = False
            try:
                out = _sp.check_output(["system_profiler", "SPAudioDataType"], text=True)
                have_bh = "BlackHole" in out
            except Exception:
                pass
            have_ffmpeg = bool(_shutil.which("ffmpeg"))
            needs = []
            if not have_bh:
                needs.append("blackhole-2ch")
            if not have_ffmpeg:
                needs.append("ffmpeg")
            if not needs:
                print("  BlackHole and ffmpeg already installed.")
            elif not _shutil.which("brew"):
                print(
                    "  missing: " + ", ".join(needs) + "\n"
                    "  install Homebrew first (https://brew.sh) or install the packages manually."
                )
            else:
                if not _confirm(f"  install via brew: {' '.join(needs)}?"):
                    print("  skipped (you can run it manually later)")
                else:
                    cmd = ["brew", "install", *needs]
                    print(f"  $ {' '.join(cmd)}")
                    res = _sp.run(cmd, check=False)
                    if res.returncode != 0:
                        print("  brew install failed — install them manually")
            print(
                "\n  NOTE: macOS does not auto-route audio. Open\n"
                "    System Settings → Sound → Input\n"
                "  and select 'BlackHole 2ch' before starting a realtime meeting.\n"
                "  hermes will not switch your default input for you."
            )
    else:
        print("\n[3/3] skipped (pass --realtime to install audio tooling too)")

    print("\ndone. verify with: hermes meet setup")
    return 0


def _cmd_auth() -> int:
    """Open a headed Chromium, let the user sign in, save storage_state."""
    try:
        from playwright.sync_api import sync_playwright
    except ImportError:
        print(
            "playwright is not installed. run:\n"
            "  pip install playwright && python -m playwright install chromium"
        )
        return 1

    path = _auth_state_path()
    path.parent.mkdir(parents=True, exist_ok=True)

    print(f"opening Chromium — sign in to Google, then return here and press Enter.")
    print(f"saving storage state to: {path}")
    try:
        with sync_playwright() as pw:
            browser = pw.chromium.launch(headless=False)
            context = browser.new_context()
            page = context.new_page()
            page.goto("https://accounts.google.com/", wait_until="domcontentloaded")
            try:
                input("press Enter after you've signed in ... ")
            except EOFError:
                pass
            context.storage_state(path=str(path))
            browser.close()
    except Exception as e:
        print(f"auth failed: {e}")
        return 1
    print("saved. you can now run: hermes meet join <url>")
    return 0


def _cmd_join(
    url: str,
    *,
    guest_name: str,
    duration: Optional[str],
    headed: bool,
    mode: str = "transcribe",
    node: Optional[str] = None,
) -> int:
    if not _is_safe_meet_url(url):
        print(f"refusing: not a meet.google.com URL: {url}")
        return 2
    if node:
        # Remote: go through NodeClient.
        try:
            from plugins.google_meet.node.registry import NodeRegistry
            from plugins.google_meet.node.client import NodeClient
        except ImportError as e:
            print(f"node module unavailable: {e}")
            return 1
        reg = NodeRegistry()
        entry = reg.resolve(node if node != "auto" else None)
        if entry is None:
            print(f"no registered node matches {node!r}")
            return 1
        client = NodeClient(url=entry["url"], token=entry["token"])
        try:
            res = client.start_bot(
                url=url, guest_name=guest_name, duration=duration,
                headed=headed, mode=mode,
            )
        except Exception as e:
            print(f"remote start_bot failed: {e}")
            return 1
        print(json.dumps({"node": entry.get("name"), **res}, indent=2))
        return 0 if res.get("ok") else 1

    auth = _auth_state_path()
    res = pm.start(
        url=url,
        headed=headed,
        guest_name=guest_name,
        duration=duration,
        auth_state=str(auth) if auth.is_file() else None,
        mode=mode,
    )
    print(json.dumps(res, indent=2))
    return 0 if res.get("ok") else 1


def _cmd_say(text: str, node: Optional[str] = None) -> int:
    if not (text or "").strip():
        print("refusing: empty text")
        return 2
    if node:
        try:
            from plugins.google_meet.node.registry import NodeRegistry
            from plugins.google_meet.node.client import NodeClient
        except ImportError as e:
            print(f"node module unavailable: {e}")
            return 1
        reg = NodeRegistry()
        entry = reg.resolve(node if node != "auto" else None)
        if entry is None:
            print(f"no registered node matches {node!r}")
            return 1
        client = NodeClient(url=entry["url"], token=entry["token"])
        try:
            res = client.say(text)
        except Exception as e:
            print(f"remote say failed: {e}")
            return 1
        print(json.dumps({"node": entry.get("name"), **res}, indent=2))
        return 0 if res.get("ok") else 1

    res = pm.enqueue_say(text)
    print(json.dumps(res, indent=2))
    return 0 if res.get("ok") else 1


def _cmd_status() -> int:
    res = pm.status()
    print(json.dumps(res, indent=2))
    return 0 if res.get("ok") else 1


def _cmd_transcript(last: Optional[int]) -> int:
    res = pm.transcript(last=last)
    if not res.get("ok"):
        print(json.dumps(res, indent=2))
        return 1
    for ln in res.get("lines", []):
        print(ln)
    return 0


def _cmd_stop() -> int:
    res = pm.stop(reason="hermes meet stop")
    print(json.dumps(res, indent=2))
    return 0 if res.get("ok") else 1


if __name__ == "__main__":  # pragma: no cover
    parser = argparse.ArgumentParser(prog="hermes meet")
    register_cli(parser)
    ns = parser.parse_args()
    sys.exit(meet_command(ns))
