"""Focus/decision state helpers and secure decision preflight."""

import re
import time

from armorer.core.prompt_parsing import _normalize_prompt, _extract_reply_value, _is_yes, _is_no


_FOCUS_TTL_S = 30 * 60
_CASUAL_MESSAGES = {
    "hi",
    "hello",
    "hey",
    "yo",
    "sup",
    "thanks",
    "thank you",
    "thx",
    "ok",
    "okay",
    "cool",
    "nice",
}


def _risk_label_for_prompt(prompt: str) -> str:
    p = str(prompt or "").lower()
    risky = [
        (r"\buninstall\b", "uninstall"),
        (r"\binstall\b", "install"),
        (r"\b(stop|shutdown)\b", "stop"),
        (r"\brestart\b", "restart"),
        (r"\brun\b", "run"),
        (r"\b(shell|execute command|run command)\b", "command execution"),
        (r"\b(docker compose down|rm -rf)\b", "destructive command"),
        (r"\b(update|rotate|change).*(token|key|secret|password)\b", "credential change"),
    ]
    for pattern, label in risky:
        if re.search(pattern, p):
            return label
    return ""


def _find_referenced_apps(prompt: str, app_names: list[str]) -> list[str]:
    p = str(prompt or "").lower()
    out: list[str] = []
    for name in app_names:
        n = str(name or "").strip()
        if not n:
            continue
        if re.search(rf"\b{re.escape(n.lower())}\b", p):
            out.append(n)
    return out


def _extract_focus_target(handler, prompt: str) -> str:
    p = str(prompt or "").lower()
    candidates = [a.name for a in handler._list_apps_with_openclaw_fallback() if str(a.name).strip()]
    candidates.extend(["openclaw", "autogpt", "clawbot"])
    for name in candidates:
        n = str(name or "").strip()
        if not n:
            continue
        if re.search(rf"\b{re.escape(n.lower())}\b", p):
            return n
    return ""


def _is_assistant_setup_config_related(prompt: str) -> bool:
    p = str(prompt or "").strip().lower()
    if not p:
        return False
    if p.startswith(("ui input:", "ui selection:", "ui confirmation:")):
        return True

    assistant_terms = (
        "assistant", "agent", "openclaw", "autogpt", "clawbot",
        "telegram", "signal", "gateway", "playbook",
    )
    topic_terms = (
        "setup", "set up", "configure", "config", "install", "onboard",
        "deploy", "run", "start", "stop", "restart", "status", "health",
        "doctor", "token", "chat id", "user id", "env", "model",
        "hardening", "security", "scan",
    )
    has_assistant = any(t in p for t in assistant_terms)
    has_topic = any(t in p for t in topic_terms)
    return has_assistant and has_topic


def _is_casual_message(prompt: str) -> bool:
    normalized = _normalize_prompt(prompt)
    if not normalized:
        return False
    if normalized in _CASUAL_MESSAGES:
        return True
    return bool(re.fullmatch(r"(hi|hello|hey|thanks|thank you|thx|ok|okay|cool|nice)[!. ]*", normalized))


def _focus_is_stale(focus: dict) -> bool:
    if not isinstance(focus, dict) or not focus:
        return False
    try:
        updated_at = float(focus.get("updated_at") or 0)
    except Exception:
        updated_at = 0.0
    if updated_at <= 0:
        return True
    return (time.time() - updated_at) > _FOCUS_TTL_S


def _pending_is_stale(pending: dict) -> bool:
    if not isinstance(pending, dict) or not pending:
        return False
    try:
        created_at = float(pending.get("created_at") or 0)
    except Exception:
        created_at = 0.0
    if created_at <= 0:
        return True
    return (time.time() - created_at) > _FOCUS_TTL_S


def _focus_mode_preflight(handler, conversation_id: str, prompt: str) -> tuple[str, dict | None]:
    raw_prompt = str(prompt or "").strip()
    if not raw_prompt:
        return raw_prompt, None

    pending = handler._pending_decisions.get(conversation_id, {})
    pending_type = str(pending.get("type") or "").strip()
    response = _extract_reply_value(raw_prompt)
    resume_pending = pending.get("resume_pending")

    if pending_type == "focus_switch_confirm":
        if _pending_is_stale(pending) or _is_casual_message(raw_prompt):
            handler._clear_pending_decision(conversation_id)
            handler._clear_focus(conversation_id)
            return raw_prompt, None

    if pending_type == "focus_switch_confirm":
        if _is_yes(response):
            original = str(pending.get("original_prompt") or "")
            handler._restore_pending_decision(conversation_id, resume_pending)
            handler._clear_focus(conversation_id)
            if not original:
                return "", {"message": "Assistant setup/configuration focus cleared. Ask your next question."}
            return original or raw_prompt, None
        if _is_no(response):
            handler._restore_pending_decision(conversation_id, resume_pending)
            focus = handler._get_focus(conversation_id)
            target = str(focus.get("target") or "assistant setup")
            return "", {
                "message": (
                    f"Keeping focus on assistant setup/configuration ({target}). "
                    "Ask setup/config questions or say `switch topic`."
                )
            }
        return "", {
            "message": "Reply `yes` to switch topic or `no` to stay focused on assistant setup/configuration.",
            "ui_action": {
                "type": "confirm",
                "message": "Switch away from assistant setup/configuration mode?",
                "default": False,
            },
        }

    lowered = raw_prompt.lower()
    if lowered in {"switch topic", "exit focus", "focus off"}:
        preserved_pending = pending if pending_type and pending_type != "focus_switch_confirm" else {}
        handler._set_pending_decision(
            conversation_id,
            {
                "type": "focus_switch_confirm",
                "created_at": time.time(),
                "original_prompt": "",
                "resume_pending": preserved_pending,
            },
        )
        return "", {
            "message": "Switch away from assistant setup/configuration mode?",
            "ui_action": {
                "type": "confirm",
                "message": "Switch topic?",
                "default": False,
            },
        }

    focus = handler._get_focus(conversation_id)
    if not focus:
        if _is_assistant_setup_config_related(raw_prompt):
            target = _extract_focus_target(handler, raw_prompt)
            handler._set_focus(conversation_id, mode="assistant_setup_config", target=target)
        return raw_prompt, None

    if _focus_is_stale(focus):
        handler._clear_focus(conversation_id)
        focus = {}

    if not focus:
        if _is_assistant_setup_config_related(raw_prompt):
            target = _extract_focus_target(handler, raw_prompt)
            handler._set_focus(conversation_id, mode="assistant_setup_config", target=target)
        return raw_prompt, None

    if str(focus.get("mode") or "") != "assistant_setup_config":
        return raw_prompt, None

    if handler._allow_pending_openclaw_reply_through_focus_gate(pending, response):
        return raw_prompt, None

    if _is_casual_message(raw_prompt):
        handler._clear_focus(conversation_id)
        return raw_prompt, None

    if _is_assistant_setup_config_related(raw_prompt):
        target = _extract_focus_target(handler, raw_prompt)
        if target:
            handler._set_focus(conversation_id, mode="assistant_setup_config", target=target)
        return raw_prompt, None

    target = str(focus.get("target") or "the current assistant")
    preserved_pending = pending if pending_type and pending_type != "focus_switch_confirm" else {}
    handler._set_pending_decision(
        conversation_id,
        {
            "type": "focus_switch_confirm",
            "created_at": time.time(),
            "original_prompt": raw_prompt,
            "resume_pending": preserved_pending,
        },
    )
    return "", {
        "message": (
            f"This conversation is focused on assistant setup/configuration ({target}). "
            "Do you want to switch topic?"
        ),
        "ui_action": {
            "type": "confirm",
            "message": "Switch topic?",
            "default": False,
        },
    }


def _secure_decision_preflight(handler, conversation_id: str, prompt: str) -> tuple[str, dict | None]:
    raw_prompt = str(prompt or "").strip()
    if not raw_prompt:
        return raw_prompt, None

    confirmed_prefix = "[SECURE_DECISION_CONFIRMED]"
    if raw_prompt.startswith(confirmed_prefix):
        return raw_prompt[len(confirmed_prefix):].strip(), None

    pending = handler._pending_decisions.get(conversation_id, {})
    if isinstance(pending, dict) and pending:
        pending_type = str(pending.get("type") or "").strip()
        response = _extract_reply_value(raw_prompt)
        if pending_type == "confirm_high_risk":
            if _is_yes(response):
                original = str(pending.get("original_prompt") or "")
                handler._clear_pending_decision(conversation_id)
                return f"{confirmed_prefix}\n{original}".strip(), None
            if _is_no(response):
                handler._clear_pending_decision(conversation_id)
                return "", {"message": "Cancelled. No high-risk action was executed."}
            return "", {
                "message": "Confirmation required. Reply `yes` to proceed or `no` to cancel.",
                "ui_action": {
                    "type": "confirm",
                    "message": "Proceed with this high-risk action?",
                    "default": False,
                },
            }

        if pending_type == "select_target":
            options = [str(x) for x in pending.get("options", []) if str(x).strip()]
            if _is_no(response) or str(response or "").strip().lower() in {"cancel", "abort", "stop", "nevermind", "never mind"}:
                handler._clear_pending_decision(conversation_id)
                return "", {"message": "Cancelled. No high-risk action was executed."}
            selected = ""
            for opt in options:
                if response.lower() == opt.lower():
                    selected = opt
                    break
            if not selected and response.isdigit():
                idx = int(response) - 1
                if 0 <= idx < len(options):
                    selected = options[idx]

            if selected:
                original = str(pending.get("original_prompt") or "")
                handler._clear_pending_decision(conversation_id)
                enriched = (
                    f"{confirmed_prefix}\n{original}\n\n"
                    f"Target assistant selected by operator: {selected}"
                )
                return enriched, None
            msg = "I need a target assistant before proceeding. Reply with one of: " + ", ".join(options)
            return "", {"message": msg, "ui_action": {"type": "select", "message": "Which assistant should this apply to?", "options": options}}

    risk_label = _risk_label_for_prompt(raw_prompt)
    if not risk_label:
        return raw_prompt, None

    app_names = [a.name for a in handler._list_apps_with_openclaw_fallback() if str(a.name).strip()]
    referenced = _find_referenced_apps(raw_prompt, app_names)

    if app_names and not referenced:
        handler._set_pending_decision(
            conversation_id,
            {
                "type": "select_target",
                "created_at": time.time(),
                "original_prompt": raw_prompt,
                "options": app_names[:12],
            },
        )
        return "", {
            "message": (
                f"This looks like a high-risk `{risk_label}` request, but the target assistant is unclear. "
                "Choose the assistant before I continue."
            ),
            "ui_action": {
                "type": "select",
                "message": "Which assistant should this apply to?",
                "options": app_names[:12],
            },
        }

    handler._set_pending_decision(
        conversation_id,
        {
            "type": "confirm_high_risk",
            "created_at": time.time(),
            "original_prompt": raw_prompt,
            "risk_label": risk_label,
        },
    )
    return "", {
        "message": (
            f"High-risk `{risk_label}` action detected. Confirm to proceed or reply `no` to cancel."
        ),
        "ui_action": {
            "type": "confirm",
            "message": f"Proceed with high-risk action: {_normalize_prompt(raw_prompt)[:120]}",
            "default": False,
        },
    }
