"""RouterOS command proxy via NATS request-reply.

Sends command requests to the Go poller's CmdResponder subscription
(device.cmd.{device_id}) and returns structured RouterOS API response data.

Used by:
- Config editor API (browse menu paths, add/edit/delete entries)
- Template push service (execute rendered template commands)
"""

import json
import logging
from typing import Any

import nats
import nats.aio.client

from app.config import settings

logger = logging.getLogger(__name__)

# Module-level NATS connection (lazy initialized)
_nc: nats.aio.client.Client | None = None


async def _get_nats() -> nats.aio.client.Client:
    """Get or create a NATS connection for command proxy requests."""
    global _nc
    if _nc is None or _nc.is_closed:
        _nc = await nats.connect(settings.NATS_URL)
        logger.info("RouterOS proxy NATS connection established")
    return _nc


async def execute_command(
    device_id: str,
    command: str,
    args: list[str] | None = None,
    timeout: float = 15.0,
) -> dict[str, Any]:
    """Execute a RouterOS API command on a device via the Go poller.

    Args:
        device_id: UUID string of the target device.
        command: Full RouterOS API path, e.g. "/ip/address/print".
        args: Optional list of RouterOS API args, e.g. ["=.proplist=.id,address"].
        timeout: NATS request timeout in seconds (default 15s).

    Returns:
        {"success": bool, "data": list[dict], "error": str|None}
    """
    nc = await _get_nats()
    request = {
        "device_id": device_id,
        "command": command,
        "args": args or [],
    }

    try:
        reply = await nc.request(
            f"device.cmd.{device_id}",
            json.dumps(request).encode(),
            timeout=timeout,
        )
        return json.loads(reply.data)
    except nats.errors.TimeoutError:
        return {
            "success": False,
            "data": [],
            "error": "Device command timed out — device may be offline or unreachable",
        }
    except Exception as exc:
        logger.error("NATS request failed for device %s: %s", device_id, exc)
        return {"success": False, "data": [], "error": str(exc)}


async def browse_menu(device_id: str, path: str) -> dict[str, Any]:
    """Browse a RouterOS menu path and return all entries.

    Args:
        device_id: Device UUID string.
        path: RouterOS menu path, e.g. "/ip/address" or "/interface".

    Returns:
        {"success": bool, "data": list[dict], "error": str|None}
    """
    command = f"{path}/print"
    return await execute_command(device_id, command)


async def add_entry(
    device_id: str, path: str, properties: dict[str, str]
) -> dict[str, Any]:
    """Add a new entry to a RouterOS menu path.

    Args:
        device_id: Device UUID.
        path: Menu path, e.g. "/ip/address".
        properties: Key-value pairs for the new entry.

    Returns:
        Command response dict.
    """
    args = [f"={k}={v}" for k, v in properties.items()]
    return await execute_command(device_id, f"{path}/add", args)


async def update_entry(
    device_id: str, path: str, entry_id: str | None, properties: dict[str, str]
) -> dict[str, Any]:
    """Update an existing entry in a RouterOS menu path.

    Args:
        device_id: Device UUID.
        path: Menu path.
        entry_id: RouterOS .id value (e.g. "*1"). None for singleton paths.
        properties: Key-value pairs to update.

    Returns:
        Command response dict.
    """
    id_args = [f"=.id={entry_id}"] if entry_id else []
    args = id_args + [f"={k}={v}" for k, v in properties.items()]
    return await execute_command(device_id, f"{path}/set", args)


async def remove_entry(
    device_id: str, path: str, entry_id: str
) -> dict[str, Any]:
    """Remove an entry from a RouterOS menu path.

    Args:
        device_id: Device UUID.
        path: Menu path.
        entry_id: RouterOS .id value.

    Returns:
        Command response dict.
    """
    return await execute_command(device_id, f"{path}/remove", [f"=.id={entry_id}"])


async def execute_cli(device_id: str, cli_command: str) -> dict[str, Any]:
    """Execute an arbitrary RouterOS CLI command.

    Parses a CLI-style string like '/ping address=8.8.8.8 count=4' into the
    RouterOS API command ('/ping') and args (['=address=8.8.8.8', '=count=4']).

    Args:
        device_id: Device UUID.
        cli_command: Full CLI command string.

    Returns:
        Command response dict.
    """
    parts = cli_command.strip().split()
    command = parts[0]
    # RouterOS API args need '=' prefix: 'address=8.8.8.8' -> '=address=8.8.8.8'
    args = []
    for p in parts[1:]:
        if "=" in p and not p.startswith("="):
            args.append(f"={p}")
        else:
            args.append(p)
    return await execute_command(device_id, command, args=args if args else None)


async def close() -> None:
    """Close the NATS connection. Called on application shutdown."""
    global _nc
    if _nc and not _nc.is_closed:
        await _nc.drain()
        _nc = None
        logger.info("RouterOS proxy NATS connection closed")
