"""
npm publisher.
Publishes a Node.js PoC package using the npm CLI.
"""

from __future__ import annotations

import json
import logging
import shutil
import subprocess
from pathlib import Path

from rich.console import Console

logger = logging.getLogger(__name__)
_console = Console()

_npm_user: str | None = None   # cached after first whoami / login
_login_attempted: bool = False  # only prompt login once per session


def _npm() -> str:
    """Return the resolved npm executable (handles npm.cmd on Windows)."""
    cmd = shutil.which("npm")
    if not cmd:
        raise FileNotFoundError(
            "npm not found on PATH. Install Node.js to publish npm packages."
        )
    return cmd


def _ensure_logged_in(npm: str) -> str | None:
    """
    Return the logged-in npm username.
    If not logged in, run `npm login` interactively once, then re-check.
    Returns None if login fails or is skipped.
    """
    global _npm_user, _login_attempted

    if _npm_user:
        return _npm_user

    # Check current login status
    try:
        r = subprocess.run([npm, "whoami"], capture_output=True, text=True)
        if r.returncode == 0:
            _npm_user = r.stdout.strip()
            return _npm_user
    except Exception:
        pass

    # Not logged in — run npm login once
    if not _login_attempted:
        _login_attempted = True
        _console.print(
            "\n[yellow]You are not logged in to npm.[/yellow] "
            "Running [bold]npm login[/bold] — complete the browser auth, then press ENTER.\n"
        )
        try:
            subprocess.run([npm, "login"], check=False)
        except Exception as exc:
            logger.error(f"npm login error: {exc}")
            return None

        # Re-check after login
        try:
            r = subprocess.run([npm, "whoami"], capture_output=True, text=True)
            if r.returncode == 0:
                _npm_user = r.stdout.strip()
                _console.print(f"[green]Logged in as {_npm_user}[/green]\n")
                return _npm_user
        except Exception:
            pass

        _console.print("[red]npm login failed or was not completed.[/red]")

    return None


def _check_scope(pkg_name: str, npm_user: str) -> bool:
    """Return True if this user can publish the package (unscoped or own scope)."""
    if not pkg_name.startswith("@"):
        return True
    scope = pkg_name.split("/")[0][1:]  # "grafana" from "@grafana/sql"
    return scope.lower() == npm_user.lower()


def publish(poc_dir: Path, token: str = "", simulate: bool = False) -> bool:
    """
    Publish a Node.js PoC package to the npm registry.

    Ensures the user is logged in (runs npm login once if needed) before
    publishing.  Skips packages whose scope belongs to another org.

    Args:
        poc_dir:  Path to the PoC package directory (must contain package.json).
        token:    Unused — kept for API compatibility.
        simulate: If True, run 'npm pack' only (no publish).

    Returns:
        True on success, False on failure.
    """
    if not (poc_dir / "package.json").exists():
        logger.error(f"No package.json found in {poc_dir}")
        return False

    try:
        npm = _npm()
    except FileNotFoundError as exc:
        logger.error(str(exc))
        return False

    if simulate:
        logger.info(f"[SIMULATE] npm pack {poc_dir.name}")
        try:
            result = subprocess.run([npm, "pack"], cwd=poc_dir, capture_output=True, text=True)
            logger.info(result.stdout)
            return result.returncode == 0
        except Exception as exc:
            logger.error(f"npm pack error: {exc}")
            return False

    # Ensure logged in (runs npm login once if needed)
    npm_user = _ensure_logged_in(npm)
    if not npm_user:
        return False

    # Read package name
    try:
        pkg_name = json.loads((poc_dir / "package.json").read_text(encoding="utf-8")).get("name", poc_dir.name)
    except Exception:
        pkg_name = poc_dir.name

    # Check scope ownership
    if not _check_scope(pkg_name, npm_user):
        scope = pkg_name.split("/")[0]
        _console.print(
            f"[yellow]  ~ Skipping '{pkg_name}'[/yellow] — "
            f"scope [bold]{scope}[/bold] belongs to another org "
            f"(logged in as [bold]{npm_user}[/bold])"
        )
        return False

    logger.info(f"Publishing {pkg_name} to npm as {npm_user} …")
    try:
        result = subprocess.run(
            [npm, "publish", "--access", "public"],
            cwd=poc_dir,
        )
    except Exception as exc:
        logger.error(f"npm publish error: {exc}")
        return False

    if result.returncode != 0:
        logger.error(f"npm publish failed for {pkg_name} (exit {result.returncode})")
        return False

    logger.info(f"Successfully published {pkg_name} to npm.")
    return True
