"""
Vault API — FastAPI router for credential vault operations.

Mounts into the nterm FastAPI app to expose vault unlock/lock/status/match
endpoints. The CredentialResolver instance is injected from nterm_desktop.py
at startup.

Usage in app.py:
    from nterm.vault_api import vault_router, set_vault_resolver

    app.include_router(vault_router, prefix="/api/vault")

    # Called from nterm_desktop.py after resolver is created
    set_vault_resolver(resolver)
"""

from __future__ import annotations
import logging
from typing import Optional

from fastapi import APIRouter, HTTPException
from pydantic import BaseModel

logger = logging.getLogger(__name__)

# Module-level resolver reference — set by desktop wrapper at startup
_resolver: Optional[object] = None  # CredentialResolver


def set_vault_resolver(resolver) -> None:
    """Inject the CredentialResolver instance. Called once at startup."""
    global _resolver
    _resolver = resolver
    logger.info("Vault resolver registered with API")


def get_resolver():
    """Get the resolver, raising 503 if not configured."""
    if _resolver is None:
        raise HTTPException(
            status_code=503,
            detail="Vault not configured — running in browser-only mode?"
        )
    return _resolver


# ---------------------------------------------------------------------------
# Request / Response models
# ---------------------------------------------------------------------------

class UnlockRequest(BaseModel):
    password: str


class InitRequest(BaseModel):
    password: str


class VaultStatusResponse(BaseModel):
    initialized: bool
    unlocked: bool
    credential_count: int = 0


class MatchResponse(BaseModel):
    matched: bool
    credential_name: Optional[str] = None
    username: Optional[str] = None
    auth_method: Optional[str] = None  # "password", "key_stored", "key_file"
    has_jump_host: bool = False
    jump_host: Optional[str] = None
    requires_touch: bool = False
    # Never expose passwords or key data over the API


class CredentialSummary(BaseModel):
    name: str
    username: str
    auth_type: str
    match_hosts: list[str] = []
    match_tags: list[str] = []
    is_default: bool = False
    has_jump_host: bool = False


# ---------------------------------------------------------------------------
# Router
# ---------------------------------------------------------------------------

vault_router = APIRouter(tags=["vault"])


@vault_router.get("/status", response_model=VaultStatusResponse)
async def vault_status():
    """Check vault initialization and lock state."""
    resolver = get_resolver()

    initialized = resolver.is_initialized()
    unlocked = resolver.store.is_unlocked if initialized else False
    count = 0

    if unlocked:
        try:
            count = len(resolver.list_credentials())
        except Exception:
            count = 0

    return VaultStatusResponse(
        initialized=initialized,
        unlocked=unlocked,
        credential_count=count,
    )


@vault_router.post("/init")
async def vault_init(req: InitRequest):
    """
    Initialize a new vault with a master password.
    Only works if no vault exists yet.
    """
    resolver = get_resolver()

    if resolver.is_initialized():
        raise HTTPException(status_code=409, detail="Vault already initialized")

    try:
        resolver.init_vault(req.password)
        return {"status": "initialized"}
    except Exception as e:
        logger.error(f"Vault init failed: {e}")
        raise HTTPException(status_code=500, detail="Vault initialization failed")


@vault_router.post("/unlock")
async def vault_unlock(req: UnlockRequest):
    """Unlock the vault with the master password."""
    resolver = get_resolver()

    if not resolver.is_initialized():
        raise HTTPException(status_code=404, detail="No vault found — initialize first")

    if resolver.store.is_unlocked:
        return {"status": "already_unlocked"}

    success = resolver.unlock_vault(req.password)
    if not success:
        raise HTTPException(status_code=401, detail="Invalid master password")

    count = len(resolver.list_credentials())
    logger.info(f"Vault unlocked — {count} credentials available")
    return {"status": "unlocked", "credential_count": count}


@vault_router.post("/lock")
async def vault_lock():
    """Lock the vault — clears decrypted secrets from memory."""
    resolver = get_resolver()
    resolver.lock_vault()
    logger.info("Vault locked")
    return {"status": "locked"}


@vault_router.get("/match", response_model=MatchResponse)
async def vault_match(host: str, port: int = 22):
    """
    Resolve the best credential for a hostname.

    Returns credential metadata (name, username, auth method) but
    never exposes secrets. The actual credential injection happens
    server-side when the WebSocket connect message arrives.
    """
    resolver = get_resolver()

    if not resolver.store.is_unlocked:
        raise HTTPException(status_code=423, detail="Vault is locked")

    profile = resolver.resolve_or_default(host, port=port)

    if profile is None:
        return MatchResponse(matched=False)

    # Determine auth method string from first auth config
    auth_method = None
    if profile.auth_methods:
        method = profile.auth_methods[0].method
        auth_method = method.name.lower()  # "password", "key_stored", etc.

    return MatchResponse(
        matched=True,
        credential_name=profile.name,
        username=profile.primary_username,
        auth_method=auth_method,
        has_jump_host=profile.has_jump_host,
        jump_host=profile.jump_hosts[0].hostname if profile.jump_hosts else None,
        requires_touch=profile.requires_touch,
    )


@vault_router.get("/credentials", response_model=list[CredentialSummary])
async def vault_list_credentials():
    """
    List all credentials (metadata only, no secrets).
    Used by the session tree to show vault badges.
    """
    resolver = get_resolver()

    if not resolver.store.is_unlocked:
        raise HTTPException(status_code=423, detail="Vault is locked")

    creds = resolver.list_credentials()
    summaries = []

    for cred in creds:
        # Determine auth type
        if cred.ssh_key:
            auth_type = "key"
        elif cred.password:
            auth_type = "password"
        else:
            auth_type = "none"

        summaries.append(CredentialSummary(
            name=cred.name,
            username=cred.username,
            auth_type=auth_type,
            match_hosts=cred.match_hosts or [],
            match_tags=cred.match_tags or [],
            is_default=cred.is_default,
            has_jump_host=bool(cred.jump_host),
        ))

    return summaries


@vault_router.post("/launch-manager")
async def vault_launch_manager():
    """
    Request the desktop wrapper to open the native credential manager dialog.

    This endpoint doesn't open the dialog directly — it signals the desktop
    wrapper via a callback. The actual dialog is a PyQt6 QDialog.exec() in
    the main thread.
    """
    if _launch_manager_callback is None:
        raise HTTPException(
            status_code=501,
            detail="Credential manager not available in browser-only mode"
        )

    try:
        _launch_manager_callback()
        return {"status": "launched"}
    except Exception as e:
        logger.error(f"Failed to launch credential manager: {e}")
        raise HTTPException(status_code=500, detail="Failed to launch manager")


# Callback for native dialog launch — set by desktop wrapper
_launch_manager_callback = None


def set_launch_manager_callback(callback) -> None:
    """Register callback to open native credential manager dialog."""
    global _launch_manager_callback
    _launch_manager_callback = callback