# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""
CLI Channel

Interactive command-line interface for the agent.
"""

import logging
import os
from pathlib import Path

from ..core.agent import Agent
from ..core.providers import get_available_providers

logger = logging.getLogger(__name__)


class CLIChannel:
    """
    Command-line interface for the agent.

    Usage:
        agent = Agent()
        cli = CLIChannel(agent)
        cli.run()
    """

    def __init__(self, agent: Agent):
        self.agent = agent
        self.user_id = "cli_user"
        self.channel = "cli"

    def print_header(self):
        """Print welcome header."""
        status = self.agent.get_status()

        router_info = ""
        if status.get("model_router") == "active":
            router_info = " | Router: active"

        print("=" * 60)
        print(f"  {status['name']}")
        print(f"  Model: {status['provider']}{router_info}")
        print(
            f"  Memory: {status['memory_entries']} entries | "
            f"Skills: {status['skills_loaded']} | "
            f"Tools: {status['tools_available']}"
        )
        print("=" * 60)
        print()
        print("Commands:")
        print("  /quit, /exit     - Exit")
        print("  /clear           - Clear conversation")
        print("  /status          - System status")
        print("  /model <name>    - Switch model")
        print("  /models          - List available models")
        print("  /connect <svc>   - Connect provider (anthropic, openai, ollama)")
        print("  /remember <k> <v>- Store memory")
        print("  /recall <query>  - Search memory")
        print("  /skills          - List skills")
        print("  /tools           - List tools")
        print()

    def handle_command(self, command: str) -> bool:
        """
        Handle a slash command.

        Returns True if command was handled, False otherwise.
        """
        parts = command[1:].split(maxsplit=1)
        cmd = parts[0].lower()
        args = parts[1] if len(parts) > 1 else ""

        if cmd in ["quit", "exit", "q"]:
            print("Goodbye!")
            return None  # Signal to exit

        elif cmd == "clear":
            self.agent.clear_history(self.user_id, self.channel)
            print("✓ Conversation cleared")
            return True

        elif cmd == "trust":
            from ..core.security import TrustLevel

            session = self.agent.sessions.get_or_create_session(self.user_id, self.channel)
            levels = list(TrustLevel)
            for lvl in levels:
                marker = (
                    "➡️ "
                    if session.trust_level == lvl
                    else ("✅ " if levels.index(session.trust_level) > levels.index(lvl) else "   ")
                )
                print(f"  {marker}{lvl.value.upper()}")
            if session.trust_level == TrustLevel.STRANGER:
                progress = f"{session.positive_interactions}/10 to KNOWN"
            elif session.trust_level == TrustLevel.KNOWN:
                progress = f"{session.positive_interactions}/50 to TRUSTED"
            else:
                progress = "Maximum level"
            print(f"  Score: {session.trust_score:.1f}  Progress: {progress}")
            return True

        elif cmd == "budget":
            session = self.agent.sessions.get_or_create_session(self.user_id, self.channel)
            pct = (
                min(100, session.spent_today / session.daily_budget * 100)
                if session.daily_budget > 0
                else 0
            )
            bar = f"[{'█' * int(pct / 10)}{'░' * (10 - int(pct / 10))}]"
            print(f"  Daily Limit:  ${session.daily_budget:.2f}")
            print(f"  {bar} {pct:.1f}%")
            print(f"  Spent:        ${session.spent_today:.4f}")
            print(f"  Remaining:    ${session.remaining_budget:.4f}")
            return True

        elif cmd == "caps":
            session = self.agent.sessions.get_or_create_session(self.user_id, self.channel)
            caps = sorted([c.value for c in session.capabilities])
            if caps:
                for c in caps:
                    print(f"  ✅ {c}")
            else:
                print("  (no capabilities yet)")
            return True

        elif cmd == "status":
            from ..core.tools import get_tool_registry

            tools = get_tool_registry()
            info = tools.execute("get_system_info", {})

            status = self.agent.get_status()
            print("\n📊 Status")
            print(f"Model: {status['provider']}")
            print(f"Security: {status['security_mode']}")
            print(f"Router: {status.get('model_router', 'inactive')}")
            print(f"Memory: {status['memory_entries']} entries")
            print(f"Skills: {status['skills_loaded']}")
            print(
                f"Tools: {status['tools_available']} available ({status.get('tools_registered', '?')} registered)"
            )
            print(f"Tasks: {status['scheduled_tasks']}")
            print(f"\n{info}")
            return True

        elif cmd == "model":
            if not args:
                print(f"Current: {self.agent.provider.name}")
                print("Usage: /model <provider>")
                return True

            result = self.agent.switch_provider(args)
            print(f"✓ {result}")
            return True

        elif cmd == "models":
            providers = get_available_providers()
            print(f"Current: {self.agent.provider.name}")
            print(f"Available: {', '.join(sorted(set(providers)))}")
            return True

        elif cmd == "connect":
            self._handle_connect(args)
            return True

        elif cmd == "remember":
            parts = args.split(maxsplit=1)
            if len(parts) < 2:
                print("Usage: /remember <key> <value>")
                return True

            key, value = parts
            self.agent.remember(key, value)
            print(f"✓ Remembered: {key}")
            return True

        elif cmd == "recall":
            if not args:
                print("Usage: /recall <query>")
                return True

            results = self.agent.recall(args)
            if not results:
                print(f"No memories matching '{args}'")
            else:
                for entry in results[:10]:
                    print(f"  • {entry.key}: {entry.value} [{entry.category}]")
            return True

        elif cmd == "skills":
            skills = self.agent.skills.list_skills()
            if not skills:
                print("No skills loaded")
            else:
                for s in skills:
                    status = "✓" if s["enabled"] else "✗"
                    tools = ", ".join(s["tools"]) if s["tools"] else "no tools"
                    print(f"  {status} {s['name']}: {tools}")
            return True

        elif cmd == "tools":
            tools = self.agent.tools.get_all()
            categories = {}
            for t in tools:
                if t.category not in categories:
                    categories[t.category] = []
                categories[t.category].append(t.name)

            for cat, names in sorted(categories.items()):
                print(f"  [{cat}]")
                for name in sorted(names):
                    print(f"    • {name}")
            return True

        elif cmd == "help":
            self.print_header()
            print("  /trust       - Trust level info")
            print("  /budget      - Spending status")
            print("  /caps        - Your capabilities")
            print("  /status      - Full system status")
            print("  /model [x]   - Show/switch provider")
            print("  /remember    - Store a memory")
            print("  /recall      - Search memories")
            print("  /connect     - Connect services")
            print("  /clear       - Clear history")
            print("  /quit        - Exit")
            return True

        else:
            print(f"Unknown command: /{cmd}")
            print("Type /help for available commands")
            return True

    def _handle_connect(self, args: str):
        """Handle /connect command — configure providers, services, and credentials."""
        parts = args.strip().split(maxsplit=1)
        service = parts[0].lower() if parts else ""
        rest = parts[1].strip() if len(parts) > 1 else ""

        if not service:
            print("Usage:")
            print("  /connect status                — Show all connection status")
            print()
            print("  LLM Providers:")
            print("  /connect anthropic <api-key>   — Set Anthropic key and switch")
            print("  /connect openai <api-key>      — Set OpenAI key and switch")
            print("  /connect ollama [model]        — Switch to local Ollama model")
            print()
            print("  Email:")
            print("  /connect email                 — Interactive email setup")
            print("  /connect email <addr> <pass>   — Set email credentials")
            print()
            print("  SMS (Twilio):")
            print("  /connect sms                   — Interactive Twilio setup")
            print("  /connect sms <sid> <token> <phone>")
            print()
            print("  Telegram:")
            print("  /connect telegram <bot-token>  — Set Telegram bot token")
            print("  /connect telegram owner <id>   — Set owner Telegram ID")
            print()
            print("  Google:")
            print("  /connect google                — Google OAuth status/setup")
            print()
            print("  Matrix:")
            print("  /connect matrix <homeserver> <user> <token>  — Set Matrix credentials")
            print("  /connect matrix owner <id>     — Set owner Matrix ID")
            print()
            print("  Nextcloud:")
            print("  /connect nextcloud <url> <user> <token>      — Set Nextcloud credentials")
            print()
            print("  Voice & Transcription:")
            print("  /connect voice <hf-token>      — Set HuggingFace token")
            print()
            print("  Webhooks:")
            print("  /connect webhook <secret>      — Set webhook secret")
            return

        # ── Status ────────────────────────────────────────────
        if service == "status":
            self._connect_status()
            return

        # ── LLM Providers ────────────────────────────────────
        if service in ("anthropic", "claude"):
            if not rest:
                print("Usage: /connect anthropic sk-ant-xxxxx")
                return
            if not rest.startswith("sk-ant-"):
                print("⚠ Anthropic keys start with 'sk-ant-'")
                return
            os.environ["ANTHROPIC_API_KEY"] = rest
            self._persist_env_key("ANTHROPIC_API_KEY", rest)
            try:
                result = self.agent.switch_provider("anthropic")
                print(f"✓ {result}")
                print("  Key saved to ~/.familiar/.env")
            except Exception as e:
                print(f"⚠ Failed to switch: {e}")
            return

        if service in ("openai", "gpt"):
            if not rest:
                print("Usage: /connect openai sk-xxxxx")
                return
            if not rest.startswith("sk-"):
                print("⚠ OpenAI keys start with 'sk-'")
                return
            os.environ["OPENAI_API_KEY"] = rest
            self._persist_env_key("OPENAI_API_KEY", rest)
            try:
                result = self.agent.switch_provider("openai")
                print(f"✓ {result}")
                print("  Key saved to ~/.familiar/.env")
            except Exception as e:
                print(f"⚠ Failed to switch: {e}")
            return

        if service in ("ollama", "llama"):
            model = rest or "llama3.2"
            try:
                self.agent.switch_provider("ollama")
                self.agent.config.llm.ollama_model = model
                print(f"✓ Switched to Ollama: {model}")
            except Exception as e:
                print(f"⚠ Failed: {e}")
            return

        # ── Email (IMAP/SMTP) ────────────────────────────────
        if service == "email":
            if rest:
                email_parts = rest.split(maxsplit=1)
                if len(email_parts) == 2:
                    addr, password = email_parts
                    os.environ["EMAIL_ADDRESS"] = addr
                    os.environ["EMAIL_PASSWORD"] = password
                    self._persist_env_key("EMAIL_ADDRESS", addr)
                    self._persist_env_key("EMAIL_PASSWORD", password)

                    # Auto-detect IMAP/SMTP
                    domain = addr.split("@")[-1] if "@" in addr else ""
                    imap, smtp, imap_port, smtp_port = self._guess_mail_servers(domain)
                    if imap:
                        os.environ["EMAIL_IMAP_SERVER"] = imap
                        os.environ["EMAIL_SMTP_SERVER"] = smtp
                        os.environ["EMAIL_IMAP_PORT"] = str(imap_port)
                        os.environ["EMAIL_SMTP_PORT"] = str(smtp_port)
                        self._persist_env_key("EMAIL_IMAP_SERVER", imap)
                        self._persist_env_key("EMAIL_SMTP_SERVER", smtp)
                        self._persist_env_key("EMAIL_IMAP_PORT", str(imap_port))
                        self._persist_env_key("EMAIL_SMTP_PORT", str(smtp_port))

                    print(f"✓ Email configured: {addr}")
                    if imap:
                        print(f"  IMAP: {imap}:{imap_port}  SMTP: {smtp}:{smtp_port}")
                    if domain.lower() in ("proton.me", "protonmail.com", "pm.me"):
                        print("  Note: Proton Mail Bridge must be running on localhost.")
                    print("  Saved to ~/.familiar/.env")
                    return
                else:
                    print("Usage: /connect email user@example.com app-password")
                    return

            # Interactive
            addr = input("  Email address: ").strip()
            if not addr:
                return
            password = input("  App password: ").strip()
            if not password:
                return

            domain = addr.split("@")[-1] if "@" in addr else ""
            imap, smtp, imap_port, smtp_port = self._guess_mail_servers(domain)

            if imap:
                print(f"  Detected: IMAP={imap}:{imap_port}, SMTP={smtp}:{smtp_port}")
                if domain.lower() in ("proton.me", "protonmail.com", "pm.me"):
                    print("  Note: Proton Mail Bridge must be running on localhost.")
                custom = input("  Use these? [Y/n] ").strip().lower()
                if custom == "n":
                    imap = input("  IMAP server: ").strip()
                    smtp = input("  SMTP server: ").strip()
                    imap_port = int(input("  IMAP port [993]: ").strip() or "993")
                    smtp_port = int(input("  SMTP port [587]: ").strip() or "587")
            else:
                imap = input("  IMAP server: ").strip()
                smtp = input("  SMTP server: ").strip()
                imap_port = int(input("  IMAP port [993]: ").strip() or "993")
                smtp_port = int(input("  SMTP port [587]: ").strip() or "587")

            os.environ["EMAIL_ADDRESS"] = addr
            os.environ["EMAIL_PASSWORD"] = password
            self._persist_env_key("EMAIL_ADDRESS", addr)
            self._persist_env_key("EMAIL_PASSWORD", password)
            if imap:
                os.environ["EMAIL_IMAP_SERVER"] = imap
                os.environ["EMAIL_SMTP_SERVER"] = smtp
                os.environ["EMAIL_IMAP_PORT"] = str(imap_port)
                os.environ["EMAIL_SMTP_PORT"] = str(smtp_port)
                self._persist_env_key("EMAIL_IMAP_SERVER", imap)
                self._persist_env_key("EMAIL_SMTP_SERVER", smtp)
                self._persist_env_key("EMAIL_IMAP_PORT", str(imap_port))
                self._persist_env_key("EMAIL_SMTP_PORT", str(smtp_port))

            print(f"✓ Email configured: {addr}")
            print("  Saved to ~/.familiar/.env")
            return

        # ── SMS (Twilio) ─────────────────────────────────────
        if service in ("sms", "twilio"):
            if rest:
                sms_parts = rest.split()
                if len(sms_parts) == 3:
                    sid, token, phone = sms_parts
                    os.environ["TWILIO_ACCOUNT_SID"] = sid
                    os.environ["TWILIO_AUTH_TOKEN"] = token
                    os.environ["TWILIO_PHONE_NUMBER"] = phone
                    self._persist_env_key("TWILIO_ACCOUNT_SID", sid)
                    self._persist_env_key("TWILIO_AUTH_TOKEN", token)
                    self._persist_env_key("TWILIO_PHONE_NUMBER", phone)
                    print(f"✓ Twilio SMS configured: {phone}")
                    print("  Saved to ~/.familiar/.env")
                    return
                else:
                    print("Usage: /connect sms <account-sid> <auth-token> <phone-number>")
                    return

            # Interactive
            print("  Twilio SMS Setup")
            print("  Get credentials: https://console.twilio.com")
            sid = input("  Account SID: ").strip()
            if not sid:
                return
            token = input("  Auth Token: ").strip()
            if not token:
                return
            phone = input("  Phone Number (+1...): ").strip()
            if not phone:
                return

            os.environ["TWILIO_ACCOUNT_SID"] = sid
            os.environ["TWILIO_AUTH_TOKEN"] = token
            os.environ["TWILIO_PHONE_NUMBER"] = phone
            self._persist_env_key("TWILIO_ACCOUNT_SID", sid)
            self._persist_env_key("TWILIO_AUTH_TOKEN", token)
            self._persist_env_key("TWILIO_PHONE_NUMBER", phone)
            print(f"✓ Twilio configured: {phone}")
            print("  Saved to ~/.familiar/.env")
            return

        # ── Telegram ──────────────────────────────────────────
        if service == "telegram":
            sub_parts = rest.split(maxsplit=1)
            sub = sub_parts[0].lower() if sub_parts else ""
            val = sub_parts[1].strip() if len(sub_parts) > 1 else ""

            if sub == "owner" and val:
                os.environ["OWNER_TELEGRAM_ID"] = val
                self._persist_env_key("OWNER_TELEGRAM_ID", val)
                print(f"✓ Owner Telegram ID set: {val}")
                print("  Saved to ~/.familiar/.env")
                return

            token = rest.strip()
            if not token:
                print("Usage:")
                print("  /connect telegram <bot-token>   — Set bot token")
                print("  /connect telegram owner <id>    — Set owner ID")
                print(
                    f"  Current token: {'✓ set' if os.environ.get('TELEGRAM_BOT_TOKEN') else '✗ not set'}"
                )
                print(f"  Owner ID:      {os.environ.get('OWNER_TELEGRAM_ID', '✗ not set')}")
                return

            os.environ["TELEGRAM_BOT_TOKEN"] = token
            self._persist_env_key("TELEGRAM_BOT_TOKEN", token)
            print("✓ Telegram bot token set")
            print("  Saved to ~/.familiar/.env")
            print("  Restart to activate Telegram channel")
            return

        # ── Google OAuth ──────────────────────────────────────
        if service == "google":
            creds_path = Path.home() / ".familiar" / "data" / "google_credentials.json"
            token_cal = Path.home() / ".familiar" / "data" / "google_token_calendar.json"
            token_drive = Path.home() / ".familiar" / "data" / "google_token_drive.json"

            print("  Google Workspace Status:")
            print(f"    Credentials: {'✓' if creds_path.exists() else '✗'} {creds_path}")
            print(
                f"    Calendar:    {'✓ authorized' if token_cal.exists() else '✗ not authorized'}"
            )
            print(
                f"    Drive:       {'✓ authorized' if token_drive.exists() else '✗ not authorized'}"
            )
            print()
            if not creds_path.exists():
                print("  Setup:")
                print("    1. Go to https://console.cloud.google.com")
                print("    2. Create OAuth 2.0 credentials (Desktop app)")
                print("    3. Download as 'google_credentials.json'")
                print(f"    4. Place at: {creds_path}")
                print("    5. Run /connect google again")
            else:
                print("  Credentials found. Use Telegram /connect google auth")
                print("  to complete OAuth flow (requires browser).")
            return

        # ── Voice / HuggingFace ───────────────────────────────
        if service in ("voice", "hf", "huggingface"):
            token = rest.strip()
            if not token:
                print("Usage: /connect voice <huggingface-token>")
                print("  Get token: https://huggingface.co/settings/tokens")
                print(f"  Current: {'✓ set' if os.environ.get('HF_TOKEN') else '✗ not set'}")
                return

            os.environ["HF_TOKEN"] = token
            os.environ["HUGGINGFACE_TOKEN"] = token
            self._persist_env_key("HF_TOKEN", token)
            self._persist_env_key("HUGGINGFACE_TOKEN", token)
            print("✓ HuggingFace token set")
            print("  Saved to ~/.familiar/.env")
            return

        # ── Discord ───────────────────────────────────────────
        if service == "discord":
            token = rest.strip()
            if not token:
                print("Usage: /connect discord <bot-token>")
                print(
                    f"  Current: {'✓ set' if os.environ.get('DISCORD_BOT_TOKEN') else '✗ not set'}"
                )
                return

            os.environ["DISCORD_BOT_TOKEN"] = token
            self._persist_env_key("DISCORD_BOT_TOKEN", token)
            print("✓ Discord bot token set")
            print("  Saved to ~/.familiar/.env")
            print("  Restart to activate Discord channel")
            return

        # ── Signal ────────────────────────────────────────────
        if service == "signal":
            phone = rest.strip()
            if not phone:
                print("Usage: /connect signal <phone-number>")
                print("  Requires signal-cli installed")
                print(f"  Current: {os.environ.get('SIGNAL_PHONE', '✗ not set')}")
                return

            os.environ["SIGNAL_PHONE"] = phone
            self._persist_env_key("SIGNAL_PHONE", phone)
            print(f"✓ Signal phone set: {phone}")
            print("  Saved to ~/.familiar/.env")
            return

        # ── Webhooks ──────────────────────────────────────────
        if service == "webhook":
            secret = rest.strip()
            if not secret:
                print("Usage: /connect webhook <secret>")
                print(
                    f"  Current: {'✓ set' if os.environ.get('FAMILIAR_WEBHOOK_SECRET') else '✗ not set'}"
                )
                return

            os.environ["FAMILIAR_WEBHOOK_SECRET"] = secret
            self._persist_env_key("FAMILIAR_WEBHOOK_SECRET", secret)
            print("✓ Webhook secret set")
            print("  Saved to ~/.familiar/.env")
            return

        # ── Teams ─────────────────────────────────────────────
        if service == "teams":
            if not rest:
                print("Usage: /connect teams <app-id> <app-password> <tenant-id>")
                print(f"  Current: {'✓ set' if os.environ.get('TEAMS_APP_ID') else '✗ not set'}")
                return

            teams_parts = rest.split()
            if len(teams_parts) == 3:
                app_id, app_pass, tenant = teams_parts
                os.environ["TEAMS_APP_ID"] = app_id
                os.environ["TEAMS_APP_PASSWORD"] = app_pass
                os.environ["TEAMS_TENANT_ID"] = tenant
                self._persist_env_key("TEAMS_APP_ID", app_id)
                self._persist_env_key("TEAMS_APP_PASSWORD", app_pass)
                self._persist_env_key("TEAMS_TENANT_ID", tenant)
                print("✓ Teams configured")
                print("  Saved to ~/.familiar/.env")
            else:
                print("Usage: /connect teams <app-id> <app-password> <tenant-id>")
            return

        # ── Matrix ────────────────────────────────────────────
        if service == "matrix":
            if rest.startswith("owner"):
                owner_id = rest.split(maxsplit=1)[1].strip() if " " in rest else ""
                if not owner_id:
                    print("Usage: /connect matrix owner @you:matrix.org")
                    print(f"  Current: {os.environ.get('OWNER_MATRIX_ID', '✗ not set')}")
                    return
                os.environ["OWNER_MATRIX_ID"] = owner_id
                self._persist_env_key("OWNER_MATRIX_ID", owner_id)
                print(f"✓ Matrix owner set: {owner_id}")
                print("  Saved to ~/.familiar/.env")
                return

            matrix_parts = rest.split()
            if len(matrix_parts) < 3:
                print("Usage: /connect matrix <homeserver-url> <user-id> <access-token>")
                print("  Example: /connect matrix https://matrix.org @bot:matrix.org syt_xxx")
                print()
                print(f"  Homeserver: {os.environ.get('MATRIX_HOMESERVER', '✗ not set')}")
                print(f"  User: {os.environ.get('MATRIX_USER', '✗ not set')}")
                print(
                    f"  Token: {'✓ set' if os.environ.get('MATRIX_ACCESS_TOKEN') else '✗ not set'}"
                )
                return

            homeserver, user_id, token = matrix_parts[0], matrix_parts[1], matrix_parts[2]
            os.environ["MATRIX_HOMESERVER"] = homeserver
            os.environ["MATRIX_USER"] = user_id
            os.environ["MATRIX_ACCESS_TOKEN"] = token
            self._persist_env_key("MATRIX_HOMESERVER", homeserver)
            self._persist_env_key("MATRIX_USER", user_id)
            self._persist_env_key("MATRIX_ACCESS_TOKEN", token)
            print(f"✓ Matrix configured: {user_id} on {homeserver}")
            print("  Saved to ~/.familiar/.env")
            print("  Restart to activate Matrix channel")
            return

        # ── Nextcloud ────────────────────────────────────────
        if service == "nextcloud":
            nc_parts = rest.split()
            if len(nc_parts) < 3:
                print("Usage: /connect nextcloud <server-url> <username> <app-token>")
                print("  Example: /connect nextcloud https://cloud.example.org alice xxxx-xxxx")
                print("  Generate an app password in Nextcloud → Settings → Security")
                print()
                print(f"  Server: {os.environ.get('NEXTCLOUD_URL', '✗ not set')}")
                print(f"  User: {os.environ.get('NEXTCLOUD_USER', '✗ not set')}")
                print(f"  Token: {'✓ set' if os.environ.get('NEXTCLOUD_TOKEN') else '✗ not set'}")
                return

            url, user, token = nc_parts[0], nc_parts[1], nc_parts[2]
            os.environ["NEXTCLOUD_URL"] = url
            os.environ["NEXTCLOUD_USER"] = user
            os.environ["NEXTCLOUD_TOKEN"] = token
            self._persist_env_key("NEXTCLOUD_URL", url)
            self._persist_env_key("NEXTCLOUD_USER", user)
            self._persist_env_key("NEXTCLOUD_TOKEN", token)
            print(f"✓ Nextcloud configured: {user} on {url}")
            print("  Saved to ~/.familiar/.env")
            print("  Calendar, contacts, and files tools are now active")
            return

        print(f"Unknown service: {service}")
        print("Type /connect for available services")

    def _connect_status(self):
        """Show status of all configurable services."""
        print(f"\n  Provider: {self.agent.provider.name}")
        print()

        services = [
            (
                "LLM Providers",
                [
                    ("Anthropic", "ANTHROPIC_API_KEY", "sk-ant-..."),
                    ("OpenAI", "OPENAI_API_KEY", "sk-..."),
                ],
            ),
            (
                "Channels",
                [
                    ("Telegram", "TELEGRAM_BOT_TOKEN", None),
                    ("  Owner ID", "OWNER_TELEGRAM_ID", None),
                    ("Discord", "DISCORD_BOT_TOKEN", None),
                    ("Signal", "SIGNAL_PHONE", None),
                    ("Matrix", "MATRIX_HOMESERVER", None),
                    ("  User", "MATRIX_USER", None),
                    ("  Owner ID", "OWNER_MATRIX_ID", None),
                    ("Teams", "TEAMS_APP_ID", None),
                ],
            ),
            (
                "Nextcloud (CalDAV/WebDAV)",
                [
                    ("Server", "NEXTCLOUD_URL", None),
                    ("User", "NEXTCLOUD_USER", None),
                    ("Token", "NEXTCLOUD_TOKEN", "****"),
                ],
            ),
            (
                "Email",
                [
                    ("Address", "EMAIL_ADDRESS", None),
                    ("Password", "EMAIL_PASSWORD", "****"),
                    ("IMAP", "EMAIL_IMAP_SERVER", None),
                    ("SMTP", "EMAIL_SMTP_SERVER", None),
                ],
            ),
            (
                "SMS (Twilio)",
                [
                    ("Account SID", "TWILIO_ACCOUNT_SID", None),
                    ("Auth Token", "TWILIO_AUTH_TOKEN", "****"),
                    ("Phone", "TWILIO_PHONE_NUMBER", None),
                ],
            ),
            (
                "Voice & AI",
                [
                    ("HuggingFace", "HF_TOKEN", "hf_..."),
                ],
            ),
            (
                "Security",
                [
                    ("Webhook Secret", "FAMILIAR_WEBHOOK_SECRET", "****"),
                ],
            ),
        ]

        for section, entries in services:
            print(f"  {section}:")
            for label, env_var, mask in entries:
                val = os.environ.get(env_var, "")
                if val:
                    if mask == "****":
                        display = "✓ ****" + val[-4:] if len(val) > 4 else "✓ set"
                    elif mask:
                        display = f"✓ {mask}"
                    else:
                        display = f"✓ {val}"
                else:
                    display = "✗ not set"
                print(f"    {label:16s} {display}")
            print()

        # Ollama special handling
        print("  Ollama:")
        try:
            import subprocess

            result = subprocess.run(["ollama", "list"], capture_output=True, timeout=5, text=True)
            if result.returncode == 0:
                models = [
                    line.split()[0]
                    for line in result.stdout.strip().split("\n")[1:]
                    if line.strip()
                ]
                print(f"    Status:          ✓ running ({len(models)} models)")
                for m in models[:8]:
                    active = (
                        " ← active"
                        if m == getattr(self.agent.config.llm, "ollama_model", "")
                        else ""
                    )
                    print(f"      {m}{active}")
            else:
                print("    Status:          ✗ not responding")
        except FileNotFoundError:
            print("    Status:          ✗ not installed")
        except Exception:
            print("    Status:          ✗ error")

        # Google special handling
        creds_path = Path.home() / ".familiar" / "data" / "google_credentials.json"
        token_cal = Path.home() / ".familiar" / "data" / "google_token_calendar.json"
        token_drive = Path.home() / ".familiar" / "data" / "google_token_drive.json"
        print()
        print("  Google Workspace:")
        print(f"    Credentials:     {'✓ found' if creds_path.exists() else '✗ not found'}")
        print(
            f"    Calendar:        {'✓ authorized' if token_cal.exists() else '✗ not authorized'}"
        )
        print(
            f"    Drive:           {'✓ authorized' if token_drive.exists() else '✗ not authorized'}"
        )
        print()

    @staticmethod
    def _guess_mail_servers(domain: str):
        """Guess IMAP/SMTP servers and ports from email domain.

        Returns (imap_server, smtp_server, imap_port, smtp_port).
        Proton domains use Bridge ports (1143/1025); all others use 993/587.
        """
        known = {
            "gmail.com": ("imap.gmail.com", "smtp.gmail.com", 993, 587),
            "googlemail.com": ("imap.gmail.com", "smtp.gmail.com", 993, 587),
            "outlook.com": ("outlook.office365.com", "smtp.office365.com", 993, 587),
            "hotmail.com": ("outlook.office365.com", "smtp.office365.com", 993, 587),
            "live.com": ("outlook.office365.com", "smtp.office365.com", 993, 587),
            "yahoo.com": ("imap.mail.yahoo.com", "smtp.mail.yahoo.com", 993, 587),
            "icloud.com": ("imap.mail.me.com", "smtp.mail.me.com", 993, 587),
            "me.com": ("imap.mail.me.com", "smtp.mail.me.com", 993, 587),
            "proton.me": ("127.0.0.1", "127.0.0.1", 1143, 1025),
            "protonmail.com": ("127.0.0.1", "127.0.0.1", 1143, 1025),
            "pm.me": ("127.0.0.1", "127.0.0.1", 1143, 1025),
            "aol.com": ("imap.aol.com", "smtp.aol.com", 993, 587),
            "zoho.com": ("imap.zoho.com", "smtp.zoho.com", 993, 587),
            "fastmail.com": ("imap.fastmail.com", "smtp.fastmail.com", 993, 587),
        }
        return known.get(domain.lower(), ("", "", 993, 587))

    def _persist_env_key(self, key_name: str, key_value: str):
        """Save an API key to ~/.familiar/.env"""
        env_path = Path.home() / ".familiar" / ".env"
        env_path.parent.mkdir(parents=True, exist_ok=True)

        # Read existing content
        lines = []
        if env_path.exists():
            lines = env_path.read_text().splitlines()

        # Replace or append
        found = False
        for i, line in enumerate(lines):
            if line.startswith(f"{key_name}="):
                lines[i] = f"{key_name}={key_value}"
                found = True
                break
        if not found:
            lines.append(f"{key_name}={key_value}")

        env_path.write_text("\n".join(lines) + "\n")
        env_path.chmod(0o600)

    def run(self):
        """Run the interactive CLI."""
        self.print_header()

        # CLI = physical access = owner trust
        # (Multi-user channels like Telegram start at STRANGER)
        from ..core.security import TrustLevel

        session = self.agent.sessions.get_or_create_session(self.user_id, self.channel)
        if session.trust_level != TrustLevel.OWNER:
            session.set_trust_level(TrustLevel.OWNER)
            self.agent.sessions.save_session(session)

        # Start scheduler
        self.agent.start_scheduler()

        # Wire scheduler delivery → terminal
        if self.agent.scheduler:

            def _deliver_to_cli(message, channel, chat_id):
                print(f"\n📬 [Scheduled] {message}\n")

            self.agent.scheduler.set_delivery_callback(_deliver_to_cli)

        while True:
            try:
                # Get input
                user_input = input("You: ").strip()

                if not user_input:
                    continue

                # Check for commands
                if user_input.startswith("/"):
                    result = self.handle_command(user_input)
                    if result is None:  # Exit signal
                        break
                    print()
                    continue

                # Process with agent
                print("Agent: ", end="", flush=True)
                response = self.agent.chat(user_input, user_id=self.user_id, channel=self.channel)

                # Phase 2: intercept confirmation sentinel
                from familiar.core.confirmations import (
                    CALENDAR_MENU_PREFIX,
                    EMAIL_MENU_PREFIX,
                    SENTINEL_PREFIX,
                )

                if isinstance(response, str) and response.startswith(SENTINEL_PREFIX):
                    token = response[len(SENTINEL_PREFIX) :]
                    session = self.agent.sessions.get_or_create_session(self.user_id, self.channel)
                    pending = session.pending_confirmations.get(token)
                    if pending:
                        preview = pending.get("preview", "This action is ready to execute.")
                        risk = pending.get("risk", "medium")
                        flag = "⚠️  " if risk == "high" else ""
                        print(flag + preview)
                        print()
                        try:
                            answer = input("Confirm? [y/N] ").strip().lower()
                        except (EOFError, KeyboardInterrupt):
                            answer = "n"
                        if answer in ("y", "yes"):
                            tool_input = dict(pending["tool_input"])
                            tool_input["_confirmed"] = True
                            del session.pending_confirmations[token]
                            self.agent.sessions.save_session(session)
                            try:
                                result = self.agent.tools.execute(
                                    pending["tool_name"],
                                    tool_input,
                                    context={
                                        "session": session,
                                        "session_manager": self.agent.sessions,
                                        "agent": self.agent,
                                        "user_id": self.user_id,
                                        "channel": self.channel,
                                    },
                                )
                                try:
                                    from familiar.core.agent import _capture_context_from_result

                                    _capture_context_from_result(
                                        pending["tool_name"],
                                        pending.get("tool_input", {}),
                                        result,
                                        session,
                                        self.agent.sessions,
                                    )
                                except Exception:
                                    pass
                                print(result or "✅ Done.")
                            except Exception as e:
                                print(f"❌ Action failed: {e}")
                        else:
                            session.pending_confirmations.pop(token, None)
                            self.agent.sessions.save_session(session)
                            print("Cancelled.")
                    else:
                        print("⚠️  Confirmation expired. Please try again.")
                elif isinstance(response, str) and response.startswith(EMAIL_MENU_PREFIX):
                    session = self.agent.sessions.get_or_create_session(self.user_id, self.channel)
                    email_menu = session.session_context.get("email_menu", {})
                    summaries = email_menu.get("summaries", [])
                    results = email_menu.get("results", {})
                    if summaries:
                        print("\n📧 Email Accounts:\n")
                        for i, (key, label, summary) in enumerate(summaries, 1):
                            emoji = "📭" if summary == "0 unread" else "📬"
                            print(f"  {i}. {emoji} {label}: {summary}")
                        print(f"\n  Enter number to view, or press Enter for all:")
                        try:
                            choice = input("> ").strip()
                            if choice and choice.isdigit():
                                idx = int(choice) - 1
                                if 0 <= idx < len(summaries):
                                    print(results.get(summaries[idx][0], "No data"))
                                else:
                                    print("Invalid selection.")
                            else:
                                print("\n\n".join(results.values()))
                        except (EOFError, KeyboardInterrupt):
                            pass
                    else:
                        print("⚠️ No email accounts found.")
                elif isinstance(response, str) and response.startswith(CALENDAR_MENU_PREFIX):
                    session = self.agent.sessions.get_or_create_session(self.user_id, self.channel)
                    cal_menu = session.session_context.get("calendar_menu", {})
                    summaries = cal_menu.get("summaries", [])
                    results = cal_menu.get("results", {})
                    if summaries:
                        print("\n📅 Calendar Accounts:\n")
                        for i, (key, label, summary) in enumerate(summaries, 1):
                            emoji = "📭" if summary == "0 events" else "📆"
                            print(f"  {i}. {emoji} {label}: {summary}")
                        print(f"\n  Enter number to view, or press Enter for all:")
                        try:
                            choice = input("> ").strip()
                            if choice and choice.isdigit():
                                idx = int(choice) - 1
                                if 0 <= idx < len(summaries):
                                    print(results.get(summaries[idx][0], "No data"))
                                else:
                                    print("Invalid selection.")
                            else:
                                print("\n\n".join(results.values()))
                        except (EOFError, KeyboardInterrupt):
                            pass
                    else:
                        print("⚠️ No calendar accounts found.")
                else:
                    print(response)
                print()

            except KeyboardInterrupt:
                print("\nGoodbye!")
                break

            except EOFError:
                print("\nGoodbye!")
                break

            except Exception as e:
                print(f"\n❌ Error: {e}\n")

        # Cleanup
        self.agent.stop_scheduler()


def run_cli(agent: Agent = None):
    """Convenience function to run CLI."""
    if agent is None:
        from ..core.agent import create_agent

        agent = create_agent()

    cli = CLIChannel(agent)
    cli.run()
