"""CLI entry point for `g8` commands — developer-facing terminal interface.

Provides the same capabilities as MCP tools but formatted for terminal output.
Installed via `pip install g8-mcp-server`, invoked as `g8`.
"""

from __future__ import annotations

import argparse
import asyncio
import json
import sys
import webbrowser

from g8_mcp_server.client import G8Client, G8ClientError, CREDENTIALS_PATH, save_credentials, remove_credentials, load_stored_credentials

_SETTINGS_URL = "https://app.graph8.com/settings/api"
_DEFAULT_API_URL = "https://be.graph8.com"


def main() -> None:
    """Entry point for the `g8` CLI command."""
    parser = argparse.ArgumentParser(prog="g8", description="graph8 developer CLI")
    subparsers = parser.add_subparsers(dest="command", help="Available commands")

    # g8 login
    login_parser = subparsers.add_parser("login", help="Authenticate with graph8")
    login_parser.add_argument("--api-key", action="store_true", help="Authenticate with an API key instead of OAuth")
    login_parser.add_argument("--url", type=str, default=None, help="Backend API URL (default: production)")

    # g8 logout
    subparsers.add_parser("logout", help="Remove stored credentials")

    # g8 whoami
    subparsers.add_parser("whoami", help="Show current authentication status")

    # g8 snippet
    snippet_parser = subparsers.add_parser("snippet", help="Get the graph8 tracking snippet")
    snippet_parser.add_argument("--framework", type=str, help="Target framework (html, react, nextjs, vue, wordpress, webflow, shopify)")
    snippet_parser.add_argument("--repo-id", type=str, default=None, help="Repository ID (optional)")
    snippet_parser.add_argument("--list", action="store_true", help="List supported frameworks")
    snippet_parser.add_argument("--format", choices=["text", "json"], default="text", help="Output format")

    # g8 form
    form_parser = subparsers.add_parser("form", help="Get a progressive form template")
    form_parser.add_argument("--framework", type=str, required=True, help="Target framework")
    form_parser.add_argument("--variant", type=str, required=True, choices=["embedded", "popup"], help="Form variant")
    form_parser.add_argument("--repo-id", type=str, default=None, help="Repository ID (optional)")
    form_parser.add_argument("--stages", type=str, default=None, help="Custom field stages (JSON string)")
    form_parser.add_argument("--format", choices=["text", "json"], default="text", help="Output format")

    args = parser.parse_args()

    if not args.command:
        parser.print_help()
        sys.exit(1)

    # login/logout/whoami are synchronous — handle before async dispatch
    if args.command == "login":
        api_url = args.url or _DEFAULT_API_URL
        if args.api_key:
            _handle_login_api_key(api_url)
        else:
            _handle_login_oauth(api_url)
        return
    if args.command == "logout":
        _handle_logout()
        return
    if args.command == "whoami":
        asyncio.run(_handle_whoami())
        return

    try:
        asyncio.run(_dispatch(args))
    except G8ClientError as e:
        print(f"Error: {e}", file=sys.stderr)
        sys.exit(1)
    except KeyboardInterrupt:
        sys.exit(0)


# ---------------------------------------------------------------------------
# Auth commands
# ---------------------------------------------------------------------------


def _handle_login_oauth(api_url: str = _DEFAULT_API_URL) -> None:
    """Handle `g8 login` — OAuth browser flow with local callback server."""
    import secrets
    import threading
    from http.server import HTTPServer, BaseHTTPRequestHandler
    from urllib.parse import urlencode, urlparse, parse_qs

    _OAUTH_CALLBACK_PORT = 18920
    _APP_LOGIN_URL = "https://app.graph8.com/cli-auth"

    result: dict[str, str] = {}
    server_ready = threading.Event()

    class CallbackHandler(BaseHTTPRequestHandler):
        def do_GET(self) -> None:
            parsed = urlparse(self.path)
            params = parse_qs(parsed.query)

            api_key = params.get("api_key", [""])[0]
            email = params.get("email", [""])[0]
            error = params.get("error", [""])[0]

            if error:
                result["error"] = error
                self._respond("Authentication failed. You can close this tab.")
            elif api_key:
                result["api_key"] = api_key
                result["email"] = email
                self._respond("Authenticated! You can close this tab and return to your terminal.")
            else:
                self._respond("Missing credentials. Please try again.")
                result["error"] = "No API key in callback"

        def _respond(self, message: str) -> None:
            html = f"""<html><body style="font-family:system-ui;display:flex;justify-content:center;
            align-items:center;height:100vh;margin:0;background:#0a0a0a;color:#fff">
            <div style="text-align:center"><h2>graph8 CLI</h2><p>{message}</p></div></body></html>"""
            self.send_response(200)
            self.send_header("Content-Type", "text/html")
            self.end_headers()
            self.wfile.write(html.encode())

        def log_message(self, format: str, *args: object) -> None:
            pass  # Suppress HTTP logs

    state = secrets.token_urlsafe(32)
    callback_url = f"http://localhost:{_OAUTH_CALLBACK_PORT}/callback"

    auth_url = f"{_APP_LOGIN_URL}?" + urlencode({
        "callback_url": callback_url,
        "state": state,
    })

    print(f"Opening graph8 in your browser to sign in...\n")
    webbrowser.open(auth_url)
    print("Waiting for authentication...")

    try:
        server = HTTPServer(("127.0.0.1", _OAUTH_CALLBACK_PORT), CallbackHandler)
        server.timeout = 120
        server.handle_request()  # Handle one request then stop
        server.server_close()
    except OSError as e:
        print(f"\nError: could not start callback server on port {_OAUTH_CALLBACK_PORT}: {e}", file=sys.stderr)
        print("Try `g8 login --api-key` to authenticate with an API key instead.")
        sys.exit(1)
    except KeyboardInterrupt:
        print("\nLogin cancelled.")
        sys.exit(0)

    if "error" in result:
        print(f"\nLogin failed: {result['error']}", file=sys.stderr)
        sys.exit(1)

    api_key = result.get("api_key", "")
    email = result.get("email", "")

    if not api_key:
        print("\nLogin failed: no credentials received.", file=sys.stderr)
        sys.exit(1)

    # Validate the key
    print("Verifying...", end=" ", flush=True)
    try:
        asyncio.run(_validate_key(api_key, api_url))
    except G8ClientError:
        print("failed.")
        print("Error: received invalid credentials. Try again or use `g8 login --api-key`.", file=sys.stderr)
        sys.exit(1)
    except Exception:
        print("skipped (could not reach API).")

    save_credentials(api_key, api_url, auth_method="oauth", email=email)
    print("done.\n")
    if email:
        print(f"Logged in as {email}")
    print("Credentials saved to ~/.g8/credentials.json")
    print("You can now use `g8 snippet`, `g8 form`, and other commands.")


def _handle_login_api_key(api_url: str = _DEFAULT_API_URL) -> None:
    """Handle `g8 login --api-key` — prompt for API key, store locally."""
    print(f"Opening graph8 settings in your browser...\n  {_SETTINGS_URL}\n")
    webbrowser.open(_SETTINGS_URL)

    print("Create an API key in Settings -> API, then paste it here.\n")
    try:
        api_key = input("API key: ").strip()
    except (EOFError, KeyboardInterrupt):
        print("\nLogin cancelled.")
        sys.exit(0)

    if not api_key:
        print("Error: no API key provided.", file=sys.stderr)
        sys.exit(1)

    # Validate the key
    print("Verifying...", end=" ", flush=True)
    try:
        asyncio.run(_validate_key(api_key, api_url))
    except G8ClientError:
        print("failed.")
        print("Error: invalid API key. Check the key and try again.", file=sys.stderr)
        sys.exit(1)
    except Exception:
        print("skipped (could not reach API).")

    save_credentials(api_key, api_url, auth_method="api_key")
    print("done.\n")
    print("Authenticated successfully. Credentials saved to ~/.g8/credentials.json")
    print("You can now use `g8 snippet`, `g8 form`, and other commands.")


def _handle_logout() -> None:
    """Handle `g8 logout` — remove stored credentials."""
    if remove_credentials():
        print("Logged out. Credentials removed from ~/.g8/credentials.json")
    else:
        print("No stored credentials found.")
    print("Use `g8 login` or set G8_API_KEY to authenticate.")


async def _handle_whoami() -> None:
    """Handle `g8 whoami` — show auth status."""
    import os
    env_key = os.environ.get("G8_API_KEY", "")
    stored_key, stored_url, auth_method = load_stored_credentials()

    if not env_key and not stored_key:
        print("Not authenticated.")
        print("Run `g8 login` or set G8_API_KEY to get started.")
        return

    if env_key:
        source = "G8_API_KEY environment variable"
        key = env_key
    elif auth_method == "oauth":
        source = "OAuth (~/.g8/credentials.json)"
        key = stored_key
    else:
        source = "API key (~/.g8/credentials.json)"
        key = stored_key

    masked = f"{key[:8]}...{key[-4:]}" if len(key) > 12 else "****"

    print(f"Auth source:  {source}")
    print(f"API key:      {masked}")

    # Show email for OAuth logins
    if not env_key and auth_method == "oauth":
        try:
            data = json.loads(CREDENTIALS_PATH.read_text())
            email = data.get("email", "")
            if email:
                print(f"Logged in as: {email}")
        except (json.JSONDecodeError, OSError):
            pass

    if stored_url:
        print(f"API URL:      {stored_url}")


async def _validate_key(api_key: str, api_url: str = _DEFAULT_API_URL) -> None:
    """Make a lightweight API call to verify the key is valid."""
    import httpx

    headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
    async with httpx.AsyncClient(base_url=api_url, headers=headers, timeout=10) as client:
        resp = await client.get("/api/v1/contacts", params={"limit": 1})
        if resp.status_code == 401:
            raise G8ClientError(401, "Invalid API key")


async def _dispatch(args: argparse.Namespace) -> None:
    """Route to the appropriate command handler."""
    if args.command == "snippet":
        await _handle_snippet(args)
    elif args.command == "form":
        await _handle_form(args)


async def _handle_snippet(args: argparse.Namespace) -> None:
    """Handle the `g8 snippet` command."""
    from g8_mcp_server.tools.snippet import SUPPORTED_FRAMEWORKS, build_snippet_result

    if args.list:
        print("Supported frameworks:")
        for fid, name in SUPPORTED_FRAMEWORKS.items():
            print(f"  {fid:<12} {name}")
        return

    if not args.framework:
        print("Error: --framework is required (or use --list to see options)", file=sys.stderr)
        sys.exit(1)

    if args.framework not in SUPPORTED_FRAMEWORKS:
        print(f"Error: unsupported framework '{args.framework}'. Use --list to see options.", file=sys.stderr)
        sys.exit(1)

    client = G8Client(require_key=True)
    snippet_data = await _fetch_snippet_data(client, args.repo_id)
    write_key = snippet_data.get("write_key", "")
    tracking_host = snippet_data.get("tracking_host", "https://t.graph8.com")

    result = build_snippet_result(args.framework, write_key, tracking_host)

    if args.format == "json":
        print(json.dumps(result, indent=2))
    else:
        _print_snippet_text(result)


async def _handle_form(args: argparse.Namespace) -> None:
    """Handle the `g8 form` command."""
    from g8_mcp_server.tools.forms import SUPPORTED_FRAMEWORKS, build_form_result

    if args.framework not in SUPPORTED_FRAMEWORKS:
        print(f"Error: unsupported framework '{args.framework}'.", file=sys.stderr)
        sys.exit(1)

    client = G8Client(require_key=True)
    snippet_data = await _fetch_snippet_data(client, args.repo_id)
    write_key = snippet_data.get("write_key", "")
    tracking_host = snippet_data.get("tracking_host", "https://t.graph8.com")

    field_stages = None
    if args.stages:
        try:
            field_stages = json.loads(args.stages)
        except json.JSONDecodeError:
            print("Error: --stages must be valid JSON", file=sys.stderr)
            sys.exit(1)

    result = build_form_result(args.framework, args.variant, write_key, tracking_host, field_stages)

    if args.format == "json":
        print(json.dumps(result, indent=2))
    else:
        _print_form_text(result)


async def _fetch_snippet_data(client: G8Client, repo_id: str | None) -> dict:
    """Fetch write key and tracking host from the backend."""
    if repo_id:
        resp = await client.get(f"/repos/{repo_id}/snippet")
    else:
        resp = await client.get("/snippet")

    data = resp.get("data", resp)
    return data


def _print_snippet_text(result: dict) -> None:
    """Print snippet result in human-readable terminal format."""
    print(f"\n{'='*60}")
    print(f"graph8 Tracking Snippet — {result['framework']}")
    print(f"{'='*60}\n")

    print("Setup Steps:")
    for i, step in enumerate(result.get("setup_steps", []), 1):
        print(f"  {i}. {step}")

    print(f"\nCode:\n{'—'*60}")
    print(result.get("snippet_code", ""))
    print(f"{'—'*60}")

    methods = result.get("tracking_methods", {})
    if isinstance(methods, dict):
        print("\nTracking Methods:")
        for name, example in methods.items():
            print(f"  {name}: {example}")
    else:
        print(f"\nTracking Methods:\n{methods}")

    print(f"\nPrivacy Options:\n{result.get('privacy_options', '')}")

    verification = result.get("verification", [])
    if isinstance(verification, list):
        print("\nVerification:")
        for i, step in enumerate(verification, 1):
            print(f"  {i}. {step}")
    else:
        print(f"\nVerification:\n{verification}")
    print()


def _print_form_text(result: dict) -> None:
    """Print form template result in human-readable terminal format."""
    variant = result.get("variant", "embedded")
    print(f"\n{'='*60}")
    print(f"graph8 Progressive Form ({variant}) — {result['framework']}")
    print(f"{'='*60}\n")

    print("Prerequisite:")
    print(f"  {result.get('prerequisite', '')}\n")

    print(f"Code:\n{'—'*60}")
    print(result.get("template_code", ""))
    print(f"{'—'*60}")

    print("\nField Stages:")
    for stage in result.get("field_stages", []):
        fields = ", ".join(f["name"] for f in stage.get("fields", []))
        print(f"  Stage {stage['stage']}: {fields}")

    print(f"\nData Flow:\n{result.get('data_flow', '')}")
    print(f"\nCustomization:\n{result.get('customization_guide', '')}")
    print()
