"""
Vocal Bridge CLI - Developer tools for voice agent iteration.

Install: pip install vocal-bridge
Usage: vb --help

Requirements: Python 3.9+ (no additional dependencies - uses stdlib only)
"""

__version__ = "0.1.2"

import argparse
import json
import os
import sys
import tempfile
import subprocess
import urllib.request
import urllib.error
from datetime import datetime
from pathlib import Path
from typing import Optional, Dict, Any

# ========================================
# Configuration
# ========================================

CONFIG_DIR = Path.home() / ".vocal-bridge"
DEFAULT_API_URL = "https://vocalbridgeai.com"


def get_config_dir() -> Path:
    CONFIG_DIR.mkdir(parents=True, exist_ok=True)
    return CONFIG_DIR


def load_config() -> dict:
    config_file = get_config_dir() / "config.json"
    if config_file.exists():
        try:
            with open(config_file, "r") as f:
                return json.load(f)
        except (json.JSONDecodeError, IOError):
            return {}
    return {}


def save_config(config: dict) -> None:
    config_file = get_config_dir() / "config.json"
    with open(config_file, "w") as f:
        json.dump(config, f, indent=2)
    os.chmod(config_file, 0o600)


def get_api_key() -> Optional[str]:
    env_key = os.environ.get("VOCAL_BRIDGE_API_KEY")
    if env_key:
        return env_key
    return load_config().get("api_key")


def set_api_key(api_key: str) -> None:
    config = load_config()
    config["api_key"] = api_key
    save_config(config)


def get_api_url() -> str:
    env_url = os.environ.get("VOCAL_BRIDGE_API_URL")
    if env_url:
        return env_url.rstrip("/")
    return load_config().get("api_url", DEFAULT_API_URL).rstrip("/")


def set_api_url(api_url: str) -> None:
    config = load_config()
    config["api_url"] = api_url.rstrip("/")
    save_config(config)


def clear_config() -> None:
    config_file = get_config_dir() / "config.json"
    if config_file.exists():
        config_file.unlink()


# ========================================
# API Client (using stdlib urllib)
# ========================================


class APIError(Exception):
    def __init__(self, status_code: int, detail: str):
        self.status_code = status_code
        self.detail = detail
        super().__init__(f"API error ({status_code}): {detail}")


def api_request(
    method: str,
    endpoint: str,
    api_key: Optional[str] = None,
    api_url: Optional[str] = None,
    data: Optional[dict] = None,
    params: Optional[dict] = None,
) -> Dict[str, Any]:
    """Make an API request using urllib."""
    key = api_key or get_api_key()
    url = api_url or get_api_url()

    if not key:
        raise ValueError("No API key found. Run 'vb auth login' or set VOCAL_BRIDGE_API_KEY")

    full_url = f"{url}{endpoint}"
    if params:
        query = "&".join(f"{k}={v}" for k, v in params.items() if v is not None)
        if query:
            full_url = f"{full_url}?{query}"

    headers = {
        "X-API-Key": key,
        "Content-Type": "application/json",
    }

    body = json.dumps(data).encode("utf-8") if data else None

    req = urllib.request.Request(full_url, data=body, headers=headers, method=method)

    try:
        with urllib.request.urlopen(req, timeout=30) as response:
            return json.loads(response.read().decode("utf-8"))
    except urllib.error.HTTPError as e:
        try:
            detail = json.loads(e.read().decode("utf-8")).get("detail", str(e))
        except Exception:
            detail = str(e)
        raise APIError(e.code, detail)
    except urllib.error.URLError as e:
        raise APIError(0, f"Connection failed: {e.reason}")


# ========================================
# Formatting Helpers
# ========================================


def format_datetime(dt_str: Optional[str]) -> str:
    if not dt_str:
        return "-"
    try:
        dt = datetime.fromisoformat(dt_str.replace("Z", "+00:00"))
        return dt.strftime("%Y-%m-%d %H:%M:%S")
    except Exception:
        return dt_str


def format_duration(seconds: Optional[int]) -> str:
    if seconds is None:
        return "-"
    minutes, secs = divmod(seconds, 60)
    if minutes > 0:
        return f"{minutes}m {secs}s"
    return f"{secs}s"


def print_error(message: str) -> None:
    print(f"Error: {message}", file=sys.stderr)


def print_success(message: str) -> None:
    print(f"OK: {message}")


# ========================================
# Auth Commands
# ========================================


def cmd_auth_login(args: argparse.Namespace) -> int:
    api_key = args.api_key

    if not api_key:
        try:
            api_key = input("Enter your API key: ").strip()
        except (KeyboardInterrupt, EOFError):
            print()
            return 1

    if not api_key:
        print_error("No API key provided")
        return 1

    if not api_key.startswith("vb_"):
        print_error("Invalid API key format (should start with 'vb_')")
        return 1

    if args.api_url:
        set_api_url(args.api_url)

    print("Verifying API key...")
    try:
        agent = api_request("GET", "/api/v1/agent", api_key=api_key, api_url=args.api_url)
    except APIError as e:
        if e.status_code == 401:
            print_error("Invalid API key")
        elif e.status_code == 404:
            print_error("Agent not found for this API key")
        else:
            print_error(f"API error: {e.detail}")
        return 1
    except Exception as e:
        print_error(f"Connection failed: {e}")
        return 1

    set_api_key(api_key)
    print_success(f"Authenticated as agent '{agent['name']}' ({agent['mode']} mode)")
    return 0


def cmd_auth_logout(args: argparse.Namespace) -> int:
    clear_config()
    print_success("Logged out successfully")
    return 0


def cmd_auth_status(args: argparse.Namespace) -> int:
    api_key = get_api_key()
    api_url = get_api_url()

    if not api_key:
        print("Status: Not authenticated")
        print(f"API URL: {api_url}")
        print("\nRun 'vb auth login' to authenticate")
        return 1

    print(f"API URL: {api_url}")
    print(f"API Key: {api_key[:12]}...{api_key[-4:]}")

    try:
        agent = api_request("GET", "/api/v1/agent")
        print("Status: Authenticated")
        print(f"Agent: {agent['name']} ({agent['mode']} mode)")
        print(f"Agent ID: {agent['id']}")
        if agent.get("phone_number"):
            print(f"Phone: {agent['phone_number']}")
        return 0
    except APIError as e:
        print(f"Status: Invalid ({e.detail})")
        return 1
    except Exception as e:
        print(f"Status: Unable to verify ({e})")
        return 1


# ========================================
# Agent Commands
# ========================================


def cmd_agent(args: argparse.Namespace) -> int:
    try:
        agent = api_request("GET", "/api/v1/agent")

        print(f"Agent: {agent['name']}")
        print(f"ID: {agent['id']}")
        print(f"Mode: {agent['mode']}")
        print(f"Status: {agent['deployment_status']}")
        if agent.get("phone_number"):
            print(f"Phone: {agent['phone_number']}")
        if agent.get("greeting"):
            print(f"Greeting: {agent['greeting']}")
        print(f"Created: {format_datetime(agent.get('created_at'))}")
        return 0
    except ValueError as e:
        print_error(str(e))
        return 1
    except APIError as e:
        print_error(e.detail)
        return 1
    except Exception as e:
        print_error(f"Failed to get agent info: {e}")
        return 1


# ========================================
# Logs Commands
# ========================================


def cmd_logs_list(args: argparse.Namespace) -> int:
    try:
        params = {"limit": args.limit, "offset": args.offset}
        if args.status:
            params["status"] = args.status

        result = api_request("GET", "/api/v1/logs", params=params)
        sessions = result.get("sessions", [])
        total = result.get("total", 0)

        if not sessions:
            print("No call logs found")
            return 0

        print(f"{'ID':<36}  {'Started':<19}  {'Duration':<10}  {'Status':<12}  {'Messages'}")
        print("-" * 100)

        for session in sessions:
            session_id = session.get("id", "-")
            started = format_datetime(session.get("started_at"))
            duration = format_duration(session.get("duration_seconds"))
            status = session.get("status", "-")
            messages = session.get("message_count", "-")

            status_display = status
            if status == "completed":
                status_display = f"\033[32m{status}\033[0m"
            elif status == "failed":
                status_display = f"\033[31m{status}\033[0m"
            elif status == "in_progress":
                status_display = f"\033[33m{status}\033[0m"

            print(f"{session_id}  {started}  {duration:<10}  {status_display:<21}  {messages}")

        print(f"\nShowing {len(sessions)} of {total} sessions")

        if args.json:
            print("\n--- JSON ---")
            print(json.dumps(result, indent=2))

        return 0
    except ValueError as e:
        print_error(str(e))
        return 1
    except APIError as e:
        print_error(e.detail)
        return 1
    except Exception as e:
        print_error(f"Failed to get logs: {e}")
        return 1


def cmd_logs_detail(args: argparse.Namespace) -> int:
    try:
        session = api_request("GET", f"/api/v1/logs/{args.session_id}")

        print(f"Session: {session.get('id')}")
        print(f"Status: {session.get('status')}")
        print(f"Started: {format_datetime(session.get('started_at'))}")
        print(f"Ended: {format_datetime(session.get('ended_at'))}")
        print(f"Duration: {format_duration(session.get('duration_seconds'))}")
        print(f"Messages: {session.get('message_count', '-')}")
        if session.get("caller_phone"):
            print(f"Caller: {session.get('caller_phone')}")
        if session.get("call_direction"):
            print(f"Direction: {session.get('call_direction')}")
        if session.get("error_message"):
            print(f"Error: {session.get('error_message')}")

        transcript_text = session.get("transcript_text")
        if transcript_text:
            print("\n--- Transcript ---")
            print(transcript_text)
        elif session.get("transcript"):
            print("\n--- Transcript ---")
            for msg in session.get("transcript", []):
                role = msg.get("role", "unknown")
                content = msg.get("content", "")
                print(f"[{role}] {content}")

        if args.json:
            print("\n--- Full JSON ---")
            print(json.dumps(session, indent=2))

        return 0
    except ValueError as e:
        print_error(str(e))
        return 1
    except APIError as e:
        print_error(e.detail)
        return 1
    except Exception as e:
        print_error(f"Failed to get log detail: {e}")
        return 1


def cmd_stats(args: argparse.Namespace) -> int:
    try:
        stats = api_request("GET", "/api/v1/logs/stats")

        print("Call Statistics")
        print("-" * 40)
        print(f"Total Sessions: {stats.get('total_sessions', 0)}")
        print(f"  Completed: {stats.get('completed_sessions', 0)}")
        print(f"  Failed: {stats.get('failed_sessions', 0)}")
        print(f"  Abandoned: {stats.get('abandoned_sessions', 0)}")
        print()
        print(f"Total Duration: {format_duration(stats.get('total_duration_seconds', 0))}")
        print(f"Avg Duration: {format_duration(int(stats.get('avg_duration_seconds', 0)))}")
        print(f"Total Messages: {stats.get('total_messages', 0)}")
        print(f"Avg Messages: {stats.get('avg_messages', 0):.1f}")

        if args.json:
            print("\n--- JSON ---")
            print(json.dumps(stats, indent=2))

        return 0
    except ValueError as e:
        print_error(str(e))
        return 1
    except APIError as e:
        print_error(e.detail)
        return 1
    except Exception as e:
        print_error(f"Failed to get stats: {e}")
        return 1


# ========================================
# Prompt Commands
# ========================================


def cmd_prompt_show(args: argparse.Namespace) -> int:
    try:
        agent = api_request("GET", "/api/v1/agent")

        prompt = agent.get("custom_prompt")
        greeting = agent.get("greeting")

        if greeting:
            print("--- Greeting ---")
            print(greeting)
            print()

        if prompt:
            print("--- System Prompt ---")
            print(prompt)
        else:
            print("No custom prompt set")

        return 0
    except ValueError as e:
        print_error(str(e))
        return 1
    except APIError as e:
        print_error(e.detail)
        return 1
    except Exception as e:
        print_error(f"Failed to get prompt: {e}")
        return 1


def cmd_prompt_set(args: argparse.Namespace) -> int:
    try:
        if args.file:
            with open(args.file, "r") as f:
                content = f.read()
        elif not sys.stdin.isatty():
            content = sys.stdin.read()
        else:
            print_error("Provide prompt via --file or pipe to stdin")
            print("Example: echo 'You are a helpful assistant.' | vb prompt set")
            print("Example: vb prompt set --file prompt.txt")
            return 1

        content = content.strip()
        if not content:
            print_error("Empty prompt")
            return 1

        data = {"greeting": content} if args.greeting else {"prompt": content}
        api_request("PATCH", "/api/v1/agent/prompt", data=data)

        field = "Greeting" if args.greeting else "Prompt"
        print_success(f"{field} updated ({len(content)} chars)")
        return 0
    except ValueError as e:
        print_error(str(e))
        return 1
    except APIError as e:
        print_error(e.detail)
        return 1
    except FileNotFoundError:
        print_error(f"File not found: {args.file}")
        return 1
    except Exception as e:
        print_error(f"Failed to update prompt: {e}")
        return 1


def cmd_prompt_edit(args: argparse.Namespace) -> int:
    try:
        agent = api_request("GET", "/api/v1/agent")

        if args.greeting:
            content = agent.get("greeting", "")
            field_name = "greeting"
        else:
            content = agent.get("custom_prompt", "")
            field_name = "prompt"

        editor = os.environ.get("EDITOR", os.environ.get("VISUAL", "vi"))

        with tempfile.NamedTemporaryFile(mode="w", suffix=f".{field_name}.txt", delete=False) as f:
            f.write(content or "")
            temp_path = f.name

        try:
            result = subprocess.run([editor, temp_path])
            if result.returncode != 0:
                print_error("Editor exited with error")
                return 1

            with open(temp_path, "r") as f:
                new_content = f.read().strip()

            if new_content == (content or "").strip():
                print("No changes made")
                return 0

            data = {"greeting": new_content} if args.greeting else {"prompt": new_content}
            api_request("PATCH", "/api/v1/agent/prompt", data=data)

            print_success(f"{field_name.capitalize()} updated ({len(new_content)} chars)")
            return 0
        finally:
            try:
                os.unlink(temp_path)
            except Exception:
                pass

    except ValueError as e:
        print_error(str(e))
        return 1
    except APIError as e:
        print_error(e.detail)
        return 1
    except Exception as e:
        print_error(f"Failed to edit prompt: {e}")
        return 1


# ========================================
# Main Entry Point
# ========================================


def create_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="vb",
        description="Vocal Bridge CLI - Developer tools for voice agent iteration",
    )
    parser.add_argument("--version", "-V", action="version", version=f"%(prog)s {__version__}")

    subparsers = parser.add_subparsers(dest="command", help="Commands")

    # Auth commands
    auth_parser = subparsers.add_parser("auth", help="Authentication commands")
    auth_subparsers = auth_parser.add_subparsers(dest="auth_command")

    auth_login = auth_subparsers.add_parser("login", help="Authenticate with API key")
    auth_login.add_argument("api_key", nargs="?", help="API key (or prompt if not provided)")
    auth_login.add_argument("--api-url", help="Custom API URL")

    auth_subparsers.add_parser("logout", help="Clear stored credentials")
    auth_subparsers.add_parser("status", help="Check authentication status")

    # Agent command
    subparsers.add_parser("agent", help="Show agent info")

    # Logs commands
    logs_parser = subparsers.add_parser("logs", help="View call logs")
    logs_parser.add_argument("session_id", nargs="?", help="Session ID for details")
    logs_parser.add_argument("-n", "--limit", type=int, default=20, help="Max logs to show")
    logs_parser.add_argument("--offset", type=int, default=0, help="Skip N logs")
    logs_parser.add_argument("--status", choices=["completed", "failed", "abandoned", "in_progress"], help="Filter by status")
    logs_parser.add_argument("--json", action="store_true", help="Output as JSON")

    # Stats command
    stats_parser = subparsers.add_parser("stats", help="Show call statistics")
    stats_parser.add_argument("--json", action="store_true", help="Output as JSON")

    # Prompt commands
    prompt_parser = subparsers.add_parser("prompt", help="Manage agent prompt")
    prompt_subparsers = prompt_parser.add_subparsers(dest="prompt_command")

    prompt_subparsers.add_parser("show", help="Show current prompt")

    prompt_set = prompt_subparsers.add_parser("set", help="Set prompt from stdin or file")
    prompt_set.add_argument("-f", "--file", help="Read prompt from file")
    prompt_set.add_argument("--greeting", action="store_true", help="Update greeting instead of prompt")

    prompt_edit = prompt_subparsers.add_parser("edit", help="Edit prompt in $EDITOR")
    prompt_edit.add_argument("--greeting", action="store_true", help="Edit greeting instead of prompt")

    return parser


def main() -> int:
    parser = create_parser()
    args = parser.parse_args()

    if not args.command:
        parser.print_help()
        return 0

    if args.command == "auth":
        if args.auth_command == "login":
            return cmd_auth_login(args)
        elif args.auth_command == "logout":
            return cmd_auth_logout(args)
        elif args.auth_command == "status":
            return cmd_auth_status(args)
        else:
            parser.parse_args(["auth", "-h"])
            return 0

    elif args.command == "agent":
        return cmd_agent(args)

    elif args.command == "logs":
        if args.session_id:
            return cmd_logs_detail(args)
        else:
            return cmd_logs_list(args)

    elif args.command == "stats":
        return cmd_stats(args)

    elif args.command == "prompt":
        if args.prompt_command == "show":
            return cmd_prompt_show(args)
        elif args.prompt_command == "set":
            return cmd_prompt_set(args)
        elif args.prompt_command == "edit":
            return cmd_prompt_edit(args)
        else:
            parser.parse_args(["prompt", "-h"])
            return 0

    else:
        parser.print_help()
        return 0


if __name__ == "__main__":
    sys.exit(main())
