"""
Authentication module with headless API-first flow and device flow fallback.

Headless path (API-first, no browser needed):
- POST /v2/auth/signup       -> create account + bootstrap token
- POST /v2/auth/bootstrap-key -> exchange token for API key

OR for returning users:
- POST /v2/auth/login-key    -> email+password -> API key

Device flow fallback (requires browser):
- POST /public/mcp-device/start -> get user_code + session_id
- POST /public/mcp-device/exchange -> exchange for API key
"""
import os
import sys
import time
import webbrowser
import httpx
from rich.console import Console
from rich.panel import Panel
from rich.prompt import Prompt, Confirm

from config import (
    NIA_SYNC_DIR,
    CONFIG_FILE,
    load_config,
    save_config,
    clear_config,
    get_api_base_url,
)

console = Console()


def _error_detail(resp: httpx.Response, fallback: str = "Unknown error") -> str:
    try:
        return resp.json().get("detail", fallback)
    except Exception:
        return resp.text or fallback


# Polling configuration
POLL_INTERVAL_SECONDS = 2
MAX_POLL_ATTEMPTS = 150  # 5 minutes max


def is_authenticated() -> bool:
    """Check if user is authenticated (has API key stored)."""
    return bool(os.getenv("NIA_API_KEY") or load_config().get("api_key"))


def get_api_key() -> str | None:
    """Get the stored API key."""
    return os.getenv("NIA_API_KEY") or load_config().get("api_key")


def login() -> bool:
    """
    Authenticate: try headless API-first flow, fall back to device flow.
    """
    if _try_headless_login():
        return True

    console.print("[dim]Falling back to browser authentication...[/dim]")
    return _device_flow_login()


def _try_headless_login() -> bool:
    """Attempt headless login via /v2/auth/* endpoints."""
    base = get_api_base_url()

    # Probe whether the server supports API-first auth
    try:
        with httpx.Client(timeout=10) as client:
            probe = client.post(
                f"{base}/v2/auth/signup",
                json={"email": "probe@test.invalid", "password": "probe000"},
            )
            if probe.status_code == 404:
                return False
    except Exception:
        return False

    console.print()
    console.print(Panel.fit(
        "[bold]Headless authentication[/bold]\n\n"
        "Sign in or create an account without a browser.",
        title="API-First Auth",
    ))
    console.print()

    has_account = Confirm.ask("Do you already have a Nia account?", default=False)

    if has_account:
        return _headless_login_existing(base)
    return _headless_signup_new(base)


def _headless_signup_new(base: str) -> bool:
    """Create a new account via API and get an API key."""
    email = Prompt.ask("Email")
    password = Prompt.ask("Password (min 8 chars)", password=True)
    org_name = Prompt.ask("Organization name")

    if not org_name or not org_name.strip():
        console.print("[red]Organization name is required.[/red]")
        return False

    payload: dict = {
        "email": email,
        "password": password,
        "organization_name": org_name.strip(),
    }

    try:
        with httpx.Client(timeout=30) as client:
            resp = client.post(f"{base}/v2/auth/signup", json=payload)

            if resp.status_code == 422:
                console.print(f"[red]{_error_detail(resp, 'Validation error')}[/red]")
                return False
            if resp.status_code == 429:
                console.print("[red]Too many attempts. Try again later.[/red]")
                return False
            if resp.status_code >= 400:
                console.print(f"[red]Signup failed: {_error_detail(resp)}[/red]")
                return False

            data = resp.json()
            bootstrap_token = data["bootstrap_token"]

            resp2 = client.post(
                f"{base}/v2/auth/bootstrap-key",
                json={"bootstrap_token": bootstrap_token},
            )
            if resp2.status_code >= 400:
                console.print(f"[red]Key creation failed: {_error_detail(resp2)}[/red]")
                return False

            key_data = resp2.json()
            save_config({"api_key": key_data["api_key"]})
            console.print("[green]Account created and authenticated![/green]")
            return True

    except httpx.RequestError as e:
        console.print(f"[yellow]Network error: {e}[/yellow]")
        return False
    except Exception as e:
        console.print(f"[red]Error: {e}[/red]")
        return False


def _headless_login_existing(base: str) -> bool:
    """Login with existing credentials via API."""
    email = Prompt.ask("Email")
    password = Prompt.ask("Password", password=True)

    try:
        with httpx.Client(timeout=30) as client:
            resp = client.post(
                f"{base}/v2/auth/login-key",
                json={"email": email, "password": password},
            )

            if resp.status_code == 401:
                console.print("[red]Invalid email or password.[/red]")
                return False
            if resp.status_code == 429:
                console.print("[red]Too many attempts. Try again later.[/red]")
                return False
            if resp.status_code >= 400:
                console.print(f"[red]Login failed: {_error_detail(resp)}[/red]")
                return False

            key_data = resp.json()
            save_config({"api_key": key_data["api_key"]})
            console.print("[green]Authentication successful![/green]")
            return True

    except httpx.RequestError as e:
        console.print(f"[yellow]Network error: {e}[/yellow]")
        return False
    except Exception as e:
        console.print(f"[red]Error: {e}[/red]")
        return False


def _device_flow_login() -> bool:
    """Original device flow: browser-based authentication."""
    try:
        console.print("Starting authentication...")

        with httpx.Client(timeout=30) as client:
            response = client.post(f"{get_api_base_url()}/public/mcp-device/start")
            response.raise_for_status()

            data = response.json()
            user_code = data["user_code"]
            authorization_session_id = data["authorization_session_id"]
            verification_url = data["verification_url"]

        console.print()
        console.print(Panel.fit(
            f"[bold cyan]Your code: {user_code}[/bold cyan]\n\n"
            "1. A browser window will open\n"
            "2. Sign in to your Nia account\n"
            "3. The code will be pre-filled\n"
            "4. Complete the setup, then return here",
            title="Authentication Code",
        ))
        console.print()

        webbrowser.open(verification_url)
        console.print(f"[dim]Browser opened to: {verification_url}[/dim]")
        console.print()
        console.print("Waiting for authentication...")

        api_key = _poll_for_api_key(authorization_session_id, user_code)

        if not api_key:
            return False

        save_config({"api_key": api_key})
        return True

    except httpx.HTTPStatusError as e:
        console.print(f"[red]HTTP error: {e.response.status_code}[/red]")
        return False
    except Exception as e:
        console.print(f"[red]Error during login: {e}[/red]")
        return False


def _poll_for_api_key(session_id: str, user_code: str) -> str | None:
    """Poll the exchange endpoint until authentication completes."""
    with httpx.Client(timeout=30) as client:
        with console.status("[dim]Waiting for browser authentication...[/dim]") as status:
            for attempt in range(MAX_POLL_ATTEMPTS):
                try:
                    response = client.post(
                        f"{get_api_base_url()}/public/mcp-device/exchange",
                        json={
                            "authorization_session_id": session_id,
                            "user_code": user_code,
                        }
                    )

                    if response.status_code == 200:
                        data = response.json()
                        status.stop()
                        console.print("[green]Authentication successful![/green]")
                        return data.get("api_key")

                    elif response.status_code == 400:
                        detail = _error_detail(response, "")
                        if "not yet authorized" in detail.lower() or "complete the setup" in detail.lower():
                            # Still waiting for user to complete in browser
                            time.sleep(POLL_INTERVAL_SECONDS)
                            continue
                        else:
                            status.stop()
                            console.print(f"[red]Error: {detail}[/red]")
                            return None

                    elif response.status_code == 410:
                        status.stop()
                        console.print("[red]Session expired. Please try again.[/red]")
                        return None

                    elif response.status_code == 409:
                        status.stop()
                        console.print("[red]Session already used. Please try again.[/red]")
                        return None

                    elif response.status_code == 404:
                        status.stop()
                        console.print("[red]Invalid session. Please try again.[/red]")
                        return None

                    else:
                        status.stop()
                        console.print(f"[red]Unexpected error: {response.status_code}[/red]")
                        return None

                except httpx.RequestError as e:
                    console.print(f"[yellow]Network error, retrying... ({e})[/yellow]")
                    time.sleep(POLL_INTERVAL_SECONDS)
                    continue

    console.print("[red]Timeout waiting for authentication. Please try again.[/red]")
    return None


def logout():
    """Clear stored credentials."""
    clear_config()
    console.print("[green]Credentials cleared.[/green]")
