"""
CLI state and prompt helpers for the EDA integrator.
Classifies stage/EDA state and provides colored display and action prompts.
"""

import json
import logging
import re
from enum import Enum
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from eda_integration.clients.eda.client import EDAClient

from eda_integration.utils.constants import HTTP_OK

logger = logging.getLogger(__name__)

# ANSI color codes for state display
DARK_GRAY = "\033[90m"
DARK_YELLOW = "\033[33m"
LIGHT_YELLOW = "\033[93m"
LIGHT_GREEN = "\033[92m"
RESET = "\033[0m"


class IntegratorState(Enum):
    """State of the integrator with respect to stage directory and EDA."""

    EMPTY = "empty"  # stage dir not found or empty
    PARSED = "parsed"     # stage has resources but no topology-data.json
    READY = "ready"       # stage has topology-data.json
    DONE = "done"       # namespace exists in EDA


def is_stage_empty(stage_dir: Path) -> bool:
    """Return True if the stage directory does not exist or has no contents."""
    if not stage_dir.exists():
        return True
    return not any(stage_dir.iterdir())


def get_topology_data_path(stage_dir: Path) -> Path:
    """Return path to topology-data.json (created after containerlab deploy).
    The clab-topology directory name is dynamic (e.g. clab-topology-xyz); uses the
    first directory under clab/ whose name starts with 'clab-topology'."""
    clab_dir = Path(stage_dir) / "clab"
    if clab_dir.is_dir():
        for child in sorted(clab_dir.iterdir()):
            if child.is_dir() and child.name.startswith("clab-topology"):
                return child / "topology-data.json"
    return clab_dir / "clab-topology" / "topology-data.json"


def has_topology_data(stage_dir: Path) -> bool:
    """Return True if stage has topology-data.json."""
    return get_topology_data_path(stage_dir).exists()


def namespace_exists_in_eda(eda_client: "EDAClient", namespace: str) -> bool:
    """Return True if the given namespace exists in EDA. Returns False on API/connection errors."""
    try:
        resp = eda_client.get("apps/core.eda.nokia.com/v1/namespaces")
        if resp.status != HTTP_OK:
            return False
        data = json.loads(resp.data.decode("utf-8"))
        names = [item["metadata"]["name"] for item in data.get("items", [])]
        return namespace in names
    except Exception as e:
        logger.debug("Failed to list EDA namespaces: %s", e)
        return False


def get_state(
    stage_dir: Path,
    eda_client: "EDAClient",
    namespace: str,
) -> IntegratorState:
    """
    Determine current integrator state. Checks in order: EMPTY -> PARSED -> READY -> DONE.
    """
    if is_stage_empty(stage_dir):
        return IntegratorState.EMPTY
    if not has_topology_data(stage_dir):
        return IntegratorState.PARSED
    if not namespace_exists_in_eda(eda_client, namespace):
        return IntegratorState.READY
    return IntegratorState.DONE


def format_state_display(state: IntegratorState) -> str:
    """Return a color-coded string for the current state (e.g. 'State: EMPTY')."""
    colors = {
        IntegratorState.EMPTY: DARK_GRAY,
        IntegratorState.PARSED: DARK_YELLOW,
        IntegratorState.READY: LIGHT_YELLOW,
        IntegratorState.DONE: LIGHT_GREEN,
    }
    color = colors.get(state, RESET)
    return f"{color}State: {state.name}{RESET}"


def get_state_info_message(
    state: IntegratorState,
    stage_dir: Path,
    namespace: str,
) -> str:
    """Return the uncolored informational message for the given state."""
    stage_str = str(Path(stage_dir).resolve())
    messages = {
        IntegratorState.EMPTY: (
            f"Please verify {stage_str} is the correct stage directory, or edit config.yaml and .env. Then run 'start' or 'run-parse' to run the first step (parsing the Excel file) manually."
        ),
        IntegratorState.PARSED: (
            "Stage directory contains files. Please verify parsed JSON resources are present. Then run 'run-preprocess' to deploy containerlab and update toponode addresses."
        ),
        IntegratorState.READY: (
            "Stage directory contains topology-data.json. Please verify containerlab is up and all JSON resources are present. Run 'run-upload' to generate EDA resources."
        ),
        IntegratorState.DONE: (
            "Namespace exists in EDA. Please verify CRs in EDA UI"
        ),
    }
    return messages.get(state, "")


def _strip_ansi(text: str) -> str:
    """Remove ANSI escape sequences for width calculation."""
    return re.sub(r"\033\[[\d;]*m", "", text)


def _wrap_text(text: str, width: int) -> list[str]:
    """Wrap text to fit within width; returns list of lines."""
    words = text.split()
    if not words:
        return [""]
    out: list[str] = []
    current: list[str] = []
    current_len = 0
    for word in words:
        need = len(word) + (1 if current else 0)
        if current_len + need <= width and current:
            current.append(word)
            current_len += need
        elif current_len + need > width and current:
            out.append(" ".join(current))
            current = [word]
            current_len = len(word)
        else:
            current = [word]
            current_len = len(word)
    if current:
        out.append(" ".join(current))
    return out


def format_state_box(
    state: IntegratorState,
    stage_dir: Path,
    namespace: str,
) -> str:
    """
    Return the state display and info message in a box.
    State line is color-coded; info message is uncolored (white).
    """
    state_line = format_state_display(state)
    state_plain = _strip_ansi(state_line)
    info_msg = get_state_info_message(state, stage_dir, namespace)
    info_lines = _wrap_text(info_msg, 70)
    all_plain = [state_plain] + info_lines
    pad = max(len(line) for line in all_plain)
    pad = max(pad, 50)
    width = pad + 4

    top = "┌" + "─" * (width - 2) + "┐"
    bottom = "└" + "─" * (width - 2) + "┘"

    middle_parts = [
        "│ " + state_line + " " * (pad - len(state_plain)) + " │",
    ]
    for line in info_lines:
        middle_parts.append("│ " + RESET + line + " " * (pad - len(line)) + " │")
    middle = "\n".join(middle_parts)
    return f"{top}\n{middle}\n{bottom}"


def get_action_prompt(state: IntegratorState) -> str:
    """
    Return the action hint for the main loop dialogue.
    EMPTY: prompt to start. PARSED/READY/DONE: prompt to inspect or reset.
    """
    if state == IntegratorState.EMPTY:
        return "'start' to continue"
    return "'inspect' to check current status or 'reset' to start over"
