from __future__ import annotations

import re
import secrets
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any
from urllib.parse import urlparse

from iuve_connect_agents.shared.feature_flags import avatar_execution_plan_enabled
from iuve_connect_agents.shared.url_normalizer import extract_normalized_url_from_text


from .avatar_local_plan_fallback import resolve_local_open_type_plan
from .i18n import detect_language, human_command_step_label, human_plan_step_status
from .avatar_intent_router import (
    AvatarRequestType,
    CompoundIntentResult,
    classify_avatar_request_type,
    detect_compound_intent,
    extract_app_query_from_text,
    extract_cursor_question_text,
    is_browser_keyboard_type_intent,
    is_chatgpt_browser_intent,
    is_cursor_keyboard_type_intent,
    is_generic_app_launch_intent,
    is_meta_capability_question,
    should_defer_to_planner,
    wants_browser_open_and_type,
    wants_cursor_launch_and_type,
    wants_local_cursor_launch,
    wants_screen_verification,
)


@dataclass(frozen=True)
class AvatarSuggestedAction:
    """Structured command draft produced from NL intent (console-local router)."""

    action_id: str
    command_type: str
    title_key: str
    description_key: str
    required_capability: str
    risk_level: str
    payload: dict[str, Any] = field(default_factory=dict)
    scope: dict[str, Any] = field(default_factory=dict)
    required_ui_capability: str | None = None


@dataclass(frozen=True)
class AvatarChatResult:
    response_key: str
    required_capability: str | None = None
    advisory_only: bool = True
    execution_triggered: bool = False
    suggested_action: AvatarSuggestedAction | None = None
    block_reason_key: str | None = None
    execution_plan: dict[str, Any] | None = None
    plan_preview: bool = False
    is_compound: bool = False
    plan_fetch_error: str | None = None
    request_type: AvatarRequestType = "device_task"
    response_params: dict[str, Any] | None = None
    plan_resume: bool = False
    resume_step_index: int = 0


def _llm_unavailable_chat_result(
    *,
    request_type: AvatarRequestType = "device_task",
) -> AvatarChatResult:
    try:
        from iuve_connect_agents.shared.avatar_llm_cloud import cloud_llm_unavailable_reason
        from iuve_connect_agents.shared.feature_flags import (
            avatar_local_ollama_fallback_enabled,
            avatar_local_ollama_reachable,
        )

        reason = cloud_llm_unavailable_reason()
        if reason in {"not_connected", "network"}:
            from iuve_connect_agents.shared.avatar_llm_cloud import cloud_llm_credentials_available

            if reason == "not_connected" and cloud_llm_credentials_available():
                return AvatarChatResult(
                    "avatar.chat.response.llm_unavailable",
                    None,
                    advisory_only=True,
                    request_type=request_type,
                )
            return AvatarChatResult(
                "avatar.chat.response.llm_unavailable_not_connected",
                None,
                advisory_only=True,
                request_type=request_type,
            )
        if reason in {"tariff", "cloud_down"}:
            return AvatarChatResult(
                "avatar.chat.response.llm_unavailable_cloud",
                None,
                advisory_only=True,
                request_type=request_type,
            )
        if (
            avatar_local_ollama_fallback_enabled()
            and avatar_local_ollama_reachable()
            and reason != "ok"
        ):
            return AvatarChatResult(
                "avatar.chat.response.llm_unavailable",
                None,
                advisory_only=True,
                request_type=request_type,
            )
    except Exception:
        pass
    return AvatarChatResult(
        "avatar.chat.response.llm_unavailable",
        None,
        advisory_only=True,
        request_type=request_type,
    )


def _permission_block_result(
    ui_cap: str,
    *,
    ui_caps: dict[str, bool],
    plan_blocked_ui_keys: set[str] | frozenset[str] | None = None,
    suggested_action: AvatarSuggestedAction | None = None,
    execution_plan: dict[str, Any] | None = None,
    plan_preview: bool = False,
    is_compound: bool = False,
    request_type: AvatarRequestType = "device_task",
) -> AvatarChatResult:
    blocked = plan_blocked_ui_keys or set()
    if ui_cap in blocked:
        return AvatarChatResult(
            "avatar.chat.response.permission_blocked_plan",
            ui_cap,
            advisory_only=True,
            suggested_action=suggested_action,
            block_reason_key="blocked_by_plan",
            execution_plan=execution_plan,
            plan_preview=plan_preview,
            is_compound=is_compound,
            request_type=request_type,
        )
    if not ui_caps.get(ui_cap, False):
        return AvatarChatResult(
            "avatar.chat.response.permission_required",
            ui_cap,
            advisory_only=True,
            suggested_action=suggested_action,
            block_reason_key="capability_not_granted",
            execution_plan=execution_plan,
            plan_preview=plan_preview,
            is_compound=is_compound,
            request_type=request_type,
        )
    raise ValueError("capability_granted")


_URL_RE = re.compile(r"https?://[^\s<>\"']+", re.IGNORECASE)

_DOCUMENT_TOKENS = (
    "ворд",
    "word",
    "документ",
    "письм",
    "letter",
    "libreoffice",
    "writer",
    "открыть документ",
    "напиши",
    "создай документ",
)
_BROWSER_TOKENS = (
    "открой сайт",
    "open site",
    "open url",
    "browser",
    "браузер",
    "открой url",
    "перейди на",
)
_UPDATE_TOKENS = (
    "обнови агента",
    "update agent",
    "upgrade agent",
    "agent update",
    "обновление агента",
    "новая версия агента",
    "новую версию агента",
    "проверь обновлен",
    "проверь, есть ли новая версия",
    "есть ли новая версия",
    "есть ли обновлен",
    "актуальн верси",
    "check for update",
    "check update",
)
_DIAGNOSTIC_TOKENS = ("diagnostic", "status", "memory", "cpu", "log", "диагност", "памят", "процессор")
_DIAGNOSTIC_SHORT_TOKEN_RE = re.compile(r"\b(ram|cpu|log)\b", re.IGNORECASE)
_FOLDER_TOKENS = (
    "папк",
    "folder",
    "workspace",
    "iuveworkspace",
    "рабоч",
    "директор",
    "каталог",
)
_MEMORY_QUESTION_TOKENS = (
    "сколько",
    "how much",
    "how many",
    "свободн",
    "free memory",
    "free ram",
    "оператив",
)
_CAPABILITY_RULES: tuple[tuple[tuple[str, ...], str], ...] = (
    (("mouse", "click", "мыш", "курсор мыш"), "mouse_control"),
    (("keyboard", "type", "keypress", "key", "клавиат"), "keyboard_control"),
    (("root", "sudo", "privileged"), "root_operations"),
)


def default_workspace_path() -> str:
    home = Path.home()
    workspace = home / "IuVeWorkspace"
    workspace.mkdir(parents=True, exist_ok=True)
    return str(workspace)


def is_folder_intent(text: str) -> bool:
    query = str(text or "").strip().lower()
    if not query:
        return False
    return any(token in query for token in _FOLDER_TOKENS)


def format_memory_diagnostics_message(memory_summary: dict[str, Any]) -> str:
    total_kb = int(memory_summary.get("mem_total_kb") or 0)
    available_kb = int(memory_summary.get("mem_available_kb") or 0)
    if total_kb <= 0:
        return ""
    free_gb = available_kb / (1024 * 1024)
    total_gb = total_kb / (1024 * 1024)
    return f"Свободно {free_gb:.1f} GB из {total_gb:.1f} GB RAM"


def format_diagnostics_evidence_message(evidence: dict[str, Any]) -> str:
    """Format CPU/RAM/disk table from get_diagnostics evidence."""
    table = str(evidence.get("metrics_table") or "").strip()
    if table:
        return table
    memory_summary = dict(evidence.get("memory_summary") or {})
    cpu_summary = dict(evidence.get("cpu_summary") or {})
    disk_summary = dict(evidence.get("disk_summary") or {})
    from ..active.command_executor import CommandExecutor

    return CommandExecutor._format_metrics_table(
        cpu_summary=cpu_summary,
        memory_summary=memory_summary,
        disk_summary=disk_summary,
    )


def human_step_label(command_type: str, *, lang: str | None = None) -> str:
    code = lang or detect_language()
    return human_command_step_label(code, command_type)


def human_step_status(status: str, *, lang: str | None = None) -> str:
    code = lang or detect_language()
    return human_plan_step_status(code, status)


def keyboard_type_success_message_key(evidence: dict[str, Any]) -> str:
    web_status = str(evidence.get("web_post_status") or evidence.get("draft_state") or "").strip().lower()
    if evidence.get("web_post_operator") or evidence.get("composer_mode"):
        status_key = {
            "draft_inserted": "avatar.chat.response.web_post_draft_inserted",
            "draft_verified": "avatar.chat.response.web_post_draft_verified",
            "composer_found": "avatar.chat.response.web_post_composer_found",
            "publish_confirmation_required": "avatar.chat.response.web_post_publish_confirmation",
            "publish_clicked": "avatar.chat.response.web_post_publish_clicked",
            "publish_verified": "avatar.chat.response.web_post_publish_verified",
            "publish_failed": "avatar.chat.response.web_post_publish_failed",
        }.get(web_status, "avatar.chat.response.web_post_draft_inserted")
        return status_key
    target = str(evidence.get("target_app") or "").strip().lower()
    submitted = bool(evidence.get("submitted") or evidence.get("submit_after"))
    if target == "libreoffice":
        return "avatar.chat.response.execution_ok_keyboard_libreoffice"
    if target == "browser":
        if submitted:
            return "avatar.chat.response.execution_ok_keyboard_browser_sent"
        return "avatar.chat.response.execution_ok_keyboard_browser"
    return "avatar.chat.response.execution_ok_keyboard_cursor"


def _ai_metadata(*, summary: str, action: str) -> dict[str, str]:
    return {
        "source_agent": "iuve_avatar_chat",
        "model_source": "local_intent_router_v0.1.24.6",
        "reasoning_summary": summary,
        "intended_action": action,
        "user_consent_state": "consented_via_local_avatar",
    }


def _extract_url(text: str) -> str | None:
    match = _URL_RE.search(text)
    if match:
        return match.group(0).rstrip(".,;)")
    return None


def _match_tokens(text: str, tokens: tuple[str, ...]) -> bool:
    return any(token in text for token in tokens)


def classify_capability_requirement(text: str) -> str | None:
    query = str(text or "").strip().lower()
    if not query:
        return None
    for tokens, capability in _CAPABILITY_RULES:
        if any(token in query for token in tokens):
            return capability
    return None


def is_user_correction_text(text: str) -> bool:
    """True when user message is a correction/rejection suitable for revise_plan."""
    try:
        from app.services.orchestra.conversation_intelligence import analyze_conversation

        signal = analyze_conversation(str(text or ""))
        return bool(signal.correction or signal.rejection)
    except Exception:
        lowered = str(text or "").strip().lower()
        return any(
            token in lowered
            for token in (
                "не так",
                "неправильн",
                "исправ",
                "correct",
                "instead",
                "wrong",
                "нет,",
            )
        )


def build_orchestra_policy_hints_for_text(
    text: str,
    *,
    prior_user_text: str | None = None,
) -> dict[str, Any]:
    """Policy hints for orchestra decision when user sends correction/feedback."""
    hints: dict[str, Any] = {}
    if is_user_correction_text(text):
        hints["user_correction"] = True
        hints["revise_plan_request"] = True
        if prior_user_text:
            hints["prior_user_text"] = str(prior_user_text)
    return hints


def attach_orchestra_recovery_to_plan(
    plan: dict[str, Any] | None,
    *,
    orchestra_decision: dict[str, Any] | None = None,
    recovery_hints: dict[str, Any] | None = None,
) -> dict[str, Any] | None:
    """Persist orchestra recovery hints on execution plan planner_trace."""
    if not isinstance(plan, dict) or not plan:
        return plan
    hints = dict(recovery_hints or {})
    if not hints and isinstance(orchestra_decision, dict):
        payload_hints = orchestra_decision.get("recovery_hints")
        if isinstance(payload_hints, dict):
            hints = dict(payload_hints)
        contract = orchestra_decision.get("execution_contract")
        if not hints and isinstance(contract, dict) and isinstance(contract.get("recovery_hints"), dict):
            hints = dict(contract["recovery_hints"])
    if not hints:
        return plan
    trace = dict(plan.get("planner_trace") or {})
    trace["recovery_hints"] = hints
    if isinstance(orchestra_decision, dict):
        contract = orchestra_decision.get("execution_contract")
        if isinstance(contract, dict):
            trace["orchestra_execution_contract"] = dict(contract)
    merged = dict(plan)
    merged["planner_trace"] = trace
    return merged


def expand_avatar_conversation_context(text: str, *, prior_user_text: str | None = None) -> str:
    """Expand short follow-ups using the prior user utterance (inventory, document, goal memory)."""
    from ..active.device_capability_scanner import expand_device_inventory_follow_up
    from ..active.goal_memory import goal_memory_store

    raw = str(text or "").strip()
    if not raw:
        return raw

    # Wrong-target correction: DM/message → public post (before generic memory expand)
    try:
        from ..active.web_post_correction import build_web_post_correction

        mem = goal_memory_store().snapshot()
        correction = build_web_post_correction(
            raw,
            prior_user_text=prior_user_text,
            memory_site=str(mem.current_target_site or mem.active_site or mem.last_browser_url or ""),
            memory_goal=str(mem.current_goal or ""),
        )
        if correction is not None:
            return correction.recovery_prompt
    except Exception:
        pass

    store = goal_memory_store()
    store.ingest_prompt(raw, conversation_context=str(prior_user_text or ""))
    expanded = store.expand_follow_up(raw)
    if expanded != raw:
        return expanded
    expanded = expand_device_inventory_follow_up(raw, prior_user_text=prior_user_text)
    if expanded != raw:
        return expanded
    prior = str(prior_user_text or "").strip().lower()
    if not prior:
        return raw
    lower = raw.lower()
    try:
        from ..active.libreoffice_document_intent import is_document_article_failure_follow_up

        if is_document_article_failure_follow_up(raw):
            return f"{prior} — повторить: написать статью в LibreOffice Writer"
    except ImportError:
        pass
    if any(token in prior for token in _DOCUMENT_TOKENS) and any(
        token in lower for token in ("напиш", "теперь", "стать", "write", "article")
    ):
        if "стать" in lower or "article" in lower:
            topic_match = re.search(r"(?:об|about|про)\s+([^,.!?]+)", lower)
            topic = topic_match.group(1).strip() if topic_match else "теме"
            return f"напиши статью {topic} в документе"
        return f"{raw} в документе"
    return raw


def is_diagnostics_query(text: str) -> bool:
    query = str(text or "").strip().lower()
    if not query:
        return False
    from .avatar_intent_router import (
        is_active_window_analysis_intent,
        is_folder_analysis_intent,
        is_full_system_metrics_query,
        is_plan_only_intent,
        is_web_research_intent,
    )

    if is_active_window_analysis_intent(text):
        return False
    if is_folder_analysis_intent(text):
        return False
    if is_web_research_intent(text):
        return False
    if is_full_system_metrics_query(text):
        return False
    if is_plan_only_intent(text):
        return False
    if detect_compound_intent(text).is_compound:
        return False
    if is_generic_app_launch_intent(text):
        return False
    if any(token in query for token in ("корзин", "trash", "очист", "empty trash")):
        return False
    if any(token in query for token in _MEMORY_QUESTION_TOKENS) and any(
        token in query for token in ("памят", "memory", "ram", "оператив", "озу")
    ):
        return True
    if _DIAGNOSTIC_SHORT_TOKEN_RE.search(query):
        return True
    return any(
        token in query
        for token in _DIAGNOSTIC_TOKENS
        if token not in {"ram", "cpu", "log"}
    )


_MATH_EXPRESSION_RE = re.compile(r"\d\s*[\+\-\*/×÷]\s*\d")
_SIMPLE_MATH_RE = re.compile(
    r"^\s*(\d+(?:\.\d+)?)\s*([+\-*/×÷])\s*(\d+(?:\.\d+)?)\s*\??\s*$"
)


def _eval_local_math(a: float, op: str, b: float) -> float | None:
    if op in {"+", "＋"}:
        return a + b
    if op in {"-", "−"}:
        return a - b
    if op in {"*", "×"}:
        return a * b
    if op in {"/", "÷"}:
        if b == 0:
            return None
        return a / b
    return None


def try_local_math_answer(text: str) -> str | None:
    """Offline-capable simple arithmetic (DEF-006)."""
    match = _SIMPLE_MATH_RE.match(str(text or "").strip())
    if not match:
        return None
    result = _eval_local_math(float(match.group(1)), match.group(2), float(match.group(3)))
    if result is None:
        return None
    if result == int(result):
        return str(int(result))
    return str(result)


def _local_math_chat_result(
    answer: str,
    *,
    request_type: AvatarRequestType = "device_task",
) -> AvatarChatResult:
    return AvatarChatResult(
        "avatar.chat.response.llm_advisory",
        None,
        advisory_only=True,
        request_type=request_type,
        response_params={"answer": answer},
    )
_GENERAL_CHAT_TRIGGERS = (
    "любой вопрос",
    "произвольн",
    "сколько будет",
    "сколько равно",
    "what is",
    "who is",
    "when was",
    "where is",
    "расскаж",
    "объясни",
    "поясни",
    "explain",
    "tell me",
)
_GENERAL_QUESTION_PREFIXES = (
    "какая ",
    "какой ",
    "какие ",
    "какого ",
    "какую ",
    "когда ",
    "где ",
    "кто ",
    "why ",
    "how ",
    "what ",
    "which ",
)


def is_general_chat_query(text: str) -> bool:
    """True for conversational Q&A that should use LLM advisory, not device routers."""
    lower = str(text or "").strip().lower()
    if not lower:
        return False

    if classify_avatar_request_type(text) in {"capabilities_faq", "meta_external"}:
        return False
    try:
        from ..active.device_status_query_intent import is_device_status_query_intent
        from ..active.memory_query_intent import is_memory_query_intent

        if is_device_status_query_intent(text) or is_memory_query_intent(text):
            return False
    except ImportError:
        pass
    if detect_compound_intent(text).is_compound:
        return False
    if wants_screen_verification(text):
        return False
    if is_generic_app_launch_intent(text):
        return False
    if wants_local_cursor_launch(text) or is_chatgpt_browser_intent(text):
        return False
    if is_browser_keyboard_type_intent(text) or is_cursor_keyboard_type_intent(text):
        return False
    if classify_capability_requirement(text):
        return False

    from ..active.device_capability_scanner import is_device_inventory_question

    if is_device_inventory_question(text):
        return False
    if is_diagnostics_query(text):
        return False

    if "?" in text:
        return True
    if _MATH_EXPRESSION_RE.search(lower):
        return True
    if any(token in lower for token in _GENERAL_CHAT_TRIGGERS):
        return True
    if any(lower.startswith(prefix) for prefix in _GENERAL_QUESTION_PREFIXES):
        return True
    if re.search(r"\bзадай\b.*\bвопрос", lower):
        return True
    return False


def route_folder_intent(text: str) -> AvatarSuggestedAction | None:
    if not is_folder_intent(text):
        return None
    workspace = default_workspace_path()
    payload = {
        "required_capability": "agent.workspace.file.read",
        "absolute_path": workspace,
        "capability_risk_level": "low",
        **_ai_metadata(
            summary=f"Open workspace folder in file manager: {workspace}",
            action="Open IuVe workspace folder",
        ),
    }
    return AvatarSuggestedAction(
        action_id=secrets.token_hex(4),
        command_type="open_folder",
        title_key="avatar.action.open_folder.title",
        description_key="avatar.action.open_folder.body",
        required_capability="agent.workspace.file.read",
        risk_level="low",
        payload=payload,
        scope={"workspace_paths": [str(Path.home())]},
        required_ui_capability="file_access",
    )


def route_application_intent(text: str) -> AvatarSuggestedAction | None:
    if is_chatgpt_browser_intent(text):
        return None
    if wants_local_cursor_launch(text) and not wants_cursor_launch_and_type(text):
        payload = {
            "required_capability": "agent.desktop.app",
            "application": "cursor",
            "capability_risk_level": "medium",
            **_ai_metadata(
                summary="Launch Cursor IDE locally on this device",
                action="Launch Cursor application",
            ),
        }
        return AvatarSuggestedAction(
            action_id=secrets.token_hex(4),
            command_type="application_launch",
            title_key="avatar.action.application_launch.title",
            description_key="avatar.action.application_launch.body",
            required_capability="agent.desktop.app",
            risk_level="medium",
            payload=payload,
            scope={},
            required_ui_capability="application_control",
        )

    if not is_generic_app_launch_intent(text):
        return None

    from ..active.device_capability_scanner import load_profile, normalize_app_query_aliases, resolve_app_launch_from_text

    resolved = resolve_app_launch_from_text(text, load_profile())
    if resolved is None:
        query = normalize_app_query_aliases(extract_app_query_from_text(text) or text.strip())
        return AvatarSuggestedAction(
            action_id=secrets.token_hex(4),
            command_type="application_launch",
            title_key="avatar.action.application_launch.title",
            description_key="avatar.action.application_not_found.body",
            required_capability="agent.desktop.app",
            risk_level="medium",
            payload={
                "required_capability": "agent.desktop.app",
                "application": query,
                "application_query": query,
                "capability_risk_level": "medium",
                **_ai_metadata(
                    summary=f"Launch installed application matching: {query}",
                    action="Launch local desktop application",
                ),
            },
            scope={},
            required_ui_capability="application_control",
        )

    matched, candidates = resolved
    app_name = str(matched.get("name") or extract_app_query_from_text(text) or "application")
    app_slug = normalize_app_query_aliases(
        str(matched.get("slug") or app_name).strip().lower()
    )
    payload = {
        "required_capability": "agent.desktop.app",
        "application": app_slug,
        "application_name": app_name,
        "application_query": extract_app_query_from_text(text) or app_name,
        "launch_candidates": [list(item) for item in candidates],
        "capability_risk_level": "medium",
        **_ai_metadata(
            summary=f"Launch {app_name} locally on this device",
            action=f"Launch {app_name}",
        ),
    }
    return AvatarSuggestedAction(
        action_id=secrets.token_hex(4),
        command_type="application_launch",
        title_key="avatar.action.application_launch.title",
        description_key="avatar.action.application_launch_named.body",
        required_capability="agent.desktop.app",
        risk_level="medium",
        payload=payload,
        scope={},
        required_ui_capability="application_control",
    )


def route_keyboard_type_intent(text: str) -> AvatarSuggestedAction | None:
    if not is_cursor_keyboard_type_intent(text):
        return None
    if wants_cursor_launch_and_type(text):
        return None
    question = extract_cursor_question_text(text)
    payload = {
        "required_capability": "agent.desktop.input",
        "text": question,
        "target_app": "cursor",
        "window_name": "Cursor",
        "capability_risk_level": "high",
        "rollback_hint": "Delete typed text manually in Cursor chat if unintended",
        "rollback_instruction": (
            "Focus Cursor chat, select the typed text, and delete it to undo this action"
        ),
        **_ai_metadata(
            summary=f"Type question into Cursor IDE chat: {question[:120]}",
            action="Type text into active Cursor window",
        ),
    }
    return AvatarSuggestedAction(
        action_id=secrets.token_hex(4),
        command_type="keyboard_type_text",
        title_key="avatar.action.keyboard_type.title",
        description_key="avatar.action.keyboard_type.body",
        required_capability="agent.desktop.input",
        risk_level="high",
        payload=payload,
        scope={},
        required_ui_capability="keyboard_control",
    )


def _parse_mouse_delta(text: str) -> tuple[int, int]:
    query = str(text or "").strip().lower()
    amount = 80 if any(token in query for token in ("немного", "slightly", "чуть")) else 120
    dx = dy = 0
    if any(token in query for token in ("вправо", "right")):
        dx = amount
    elif any(token in query for token in ("влево", "left")):
        dx = -amount
    if any(token in query for token in ("вверх", "up")):
        dy = -amount
    elif any(token in query for token in ("вниз", "down")):
        dy = amount
    if dx == 0 and dy == 0 and any(token in query for token in ("мыш", "mouse", "курсор")):
        dx = amount
    return dx, dy


def route_mouse_intent(text: str) -> AvatarSuggestedAction | None:
    if classify_capability_requirement(text) != "mouse_control":
        return None
    dx, dy = _parse_mouse_delta(text)
    payload = {
        "required_capability": "agent.desktop.input",
        "dx": dx,
        "dy": dy,
        "capability_risk_level": "high",
        "rollback_hint": f"Move cursor back by dx={-dx}, dy={-dy}",
        "rollback_instruction": (
            "Re-run mouse_move_relative with inverted dx/dy to restore the cursor position"
        ),
        **_ai_metadata(
            summary=f"Move mouse cursor relatively by dx={dx}, dy={dy}",
            action="Relative mouse move",
        ),
    }
    return AvatarSuggestedAction(
        action_id=secrets.token_hex(4),
        command_type="mouse_move_relative",
        title_key="avatar.action.mouse_move.title",
        description_key="avatar.action.mouse_move.body",
        required_capability="agent.desktop.input",
        risk_level="high",
        payload=payload,
        scope={},
        required_ui_capability="mouse_control",
    )


def route_device_status_intent(text: str) -> AvatarSuggestedAction | None:
    from ..active.device_status_query_intent import is_device_status_query_intent

    if not is_device_status_query_intent(text):
        return None
    payload = {
        "required_capability": "agent.runtime.read",
        "capability_risk_level": "low",
        **_ai_metadata(
            summary="Read Desktop Agent runtime status, heartbeat, and connection state",
            action="Get agent status",
        ),
    }
    return AvatarSuggestedAction(
        action_id=secrets.token_hex(4),
        command_type="get_status",
        title_key="avatar.action.status.title",
        description_key="avatar.action.status.body",
        required_capability="agent.runtime.read",
        risk_level="low",
        payload=payload,
        scope={},
        required_ui_capability=None,
    )


def route_diagnostics_intent(text: str) -> AvatarSuggestedAction | None:
    if not is_diagnostics_query(text):
        from .avatar_intent_router import is_full_system_metrics_query

        if not is_full_system_metrics_query(text):
            return None
    from .avatar_intent_router import is_full_system_metrics_query

    full_table = is_full_system_metrics_query(text)
    payload = {
        "required_capability": "agent.diagnostics.read",
        "capability_risk_level": "low",
        "include_memory_summary": True,
        "include_cpu": full_table,
        "include_disk": full_table,
        "format_table": full_table,
        **_ai_metadata(
            summary="Collect local device diagnostics including runtime and memory snapshot",
            action="Read device diagnostics",
        ),
    }
    return AvatarSuggestedAction(
        action_id=secrets.token_hex(4),
        command_type="get_diagnostics",
        title_key="avatar.action.diagnostics.title",
        description_key="avatar.action.diagnostics.body",
        required_capability="agent.diagnostics.read",
        risk_level="low",
        payload=payload,
        scope={},
        required_ui_capability=None,
    )


def route_empty_trash_intent(text: str) -> AvatarSuggestedAction | None:
    from .avatar_local_plan_fallback import is_empty_trash_intent

    if not is_empty_trash_intent(text):
        return None
    payload = {
        "required_capability": "agent.workspace.file.write",
        "capability_risk_level": "medium",
        **_ai_metadata(
            summary="Empty the system recycle bin / trash on this device",
            action="Empty trash / recycle bin",
        ),
    }
    return AvatarSuggestedAction(
        action_id=secrets.token_hex(4),
        command_type="empty_trash",
        title_key="avatar.step.empty_trash",
        description_key="avatar.chat.response.execution_ok_trash",
        required_capability="agent.workspace.file.write",
        risk_level="medium",
        payload=payload,
        scope={},
        required_ui_capability="file_access",
    )


def route_document_intent(text: str) -> AvatarSuggestedAction | None:
    from .avatar_intent_router import has_document_write_intent

    query = str(text or "").strip().lower()
    if is_folder_intent(text):
        return None
    if is_cursor_keyboard_type_intent(text):
        return None
    if is_browser_keyboard_type_intent(text):
        return None
    if not has_document_write_intent(text):
        return None

    workspace = str(Path.home())
    relative_path = "IuVeWorkspace/avatar-letter.odt"
    content = (
        "Уважаемый получатель,\n\n"
        "Это черновик письма, созданный IuVe Avatar.\n"
        "Отредактируйте текст в LibreOffice Writer.\n\n"
        "С уважением,\n"
    )
    payload = {
        "required_capability": "agent.desktop.app",
        "relative_path": relative_path,
        "content": content,
        "editor": "libreoffice",
        "mode": "edit",
        "capability_risk_level": "medium",
        **_ai_metadata(
            summary="Open LibreOffice Writer with a new letter draft in workspace",
            action="Create and open document for editing in workspace",
        ),
    }
    return AvatarSuggestedAction(
        action_id=secrets.token_hex(4),
        command_type="open_document_edit",
        title_key="avatar.action.document_edit.title",
        description_key="avatar.action.document_edit.body",
        required_capability="agent.desktop.app",
        risk_level="medium",
        payload=payload,
        scope={"workspace_paths": [workspace]},
        required_ui_capability="application_control",
    )


def route_browser_intent(text: str) -> AvatarSuggestedAction | None:
    from ..active.device_capability_scanner import is_device_browser_inventory_question

    if is_device_browser_inventory_question(text):
        return None
    query = str(text or "").strip().lower()
    if is_meta_capability_question(text):
        return None
    if wants_browser_open_and_type(text):
        return None
    url = extract_normalized_url_from_text(text) or _extract_url(text)
    if url is None and not _match_tokens(query, _BROWSER_TOKENS):
        return None
    if url is None:
        url = "https://www.iuve.eu/connect"

    parsed = urlparse(url)
    if parsed.scheme not in {"http", "https"}:
        return None

    host = (parsed.hostname or "").lower()
    is_local = host in {"localhost", "127.0.0.1", "::1"}
    risk = "low" if is_local else "medium"

    payload = {
        "required_capability": "agent.browser.open_url",
        "url": url,
        "new_window": True,
        "browser": "default",
        "capability_risk_level": risk,
        **_ai_metadata(
            summary=f"Open URL in default browser: {url}",
            action=f"Browser open {url}",
        ),
    }
    return AvatarSuggestedAction(
        action_id=secrets.token_hex(4),
        command_type="browser_open_url",
        title_key="avatar.action.browser_open.title",
        description_key="avatar.action.browser_open.body",
        required_capability="agent.browser.open_url",
        risk_level=risk,
        payload=payload,
        scope={},
        required_ui_capability="network_control",
    )


def route_update_intent(text: str) -> AvatarSuggestedAction | None:
    query = str(text or "").strip().lower()
    if not _match_tokens(query, _UPDATE_TOKENS):
        return None

    payload = {
        "required_capability": "agent.update.check",
        "capability_risk_level": "low",
        **_ai_metadata(
            summary="Check for signed agent update on stable channel",
            action="Check agent update availability",
        ),
    }
    return AvatarSuggestedAction(
        action_id=secrets.token_hex(4),
        command_type="check_update",
        title_key="avatar.action.check_update.title",
        description_key="avatar.action.check_update.body",
        required_capability="agent.update.check",
        risk_level="low",
        payload=payload,
        scope={},
        required_ui_capability=None,
    )


def route_avatar_intent(text: str, *, defer_compound: bool = False) -> AvatarSuggestedAction | None:
    if defer_compound or should_defer_to_planner(text):
        return None
    from .avatar_intent_router import is_active_window_analysis_intent

    if is_active_window_analysis_intent(text):
        return None
    for router in (
        route_mouse_intent,
        route_keyboard_type_intent,
        route_folder_intent,
        route_empty_trash_intent,
        route_device_status_intent,
        route_diagnostics_intent,
        route_application_intent,
        route_document_intent,
        route_browser_intent,
        route_update_intent,
    ):
        action = router(text)
        if action is not None:
            return action
    return None


def safe_format_template(template: str, **values: object) -> str:
    """Format i18n templates without failing on braces in user-supplied values."""
    result = str(template)
    for key, value in values.items():
        result = result.replace("{" + key + "}", str(value if value is not None else ""))
    return result


def format_plan_preview_steps(plan: dict[str, Any], *, lang: str | None = None) -> str:
    steps = plan.get("steps") or []
    code = lang or detect_language()
    lines: list[str] = []
    for index, step in enumerate(steps, start=1):
        if not isinstance(step, dict):
            continue
        command_type = str(step.get("command_type") or "")
        if command_type == "plan_step":
            description = str((step.get("payload") or {}).get("description") or "").strip()
            lines.append(f"{index}. {description or human_step_label(command_type, lang=code)}")
            continue
        label = human_step_label(command_type, lang=code)
        lines.append(f"{index}. {label}")
    return "\n".join(lines)


def route_avatar_intent_with_cloud(
    text: str,
    *,
    cloud_suggestions: list[dict[str, Any]] | None = None,
    defer_compound: bool = False,
) -> AvatarSuggestedAction | None:
    """Prefer cloud heartbeat suggestions, then local NL router."""
    if defer_compound or should_defer_to_planner(text):
        return None
    if cloud_suggestions:
        matched = _match_cloud_suggestion(text, cloud_suggestions)
        if matched is not None:
            return matched
    return route_avatar_intent(text, defer_compound=defer_compound)


def _match_cloud_suggestion(text: str, suggestions: list[dict[str, Any]]) -> AvatarSuggestedAction | None:
    query = str(text or "").strip().lower()
    if not query:
        return None
    for item in suggestions:
        if not isinstance(item, dict):
            continue
        command_type = str(item.get("command_type") or "").strip().lower()
        if not command_type:
            continue
        title = str(item.get("title") or "").lower()
        reasoning = str(item.get("reasoning_summary") or "").lower()
        if title and title in query:
            return _cloud_item_to_action(item)
        if reasoning and any(token in reasoning for token in query.split()[:5] if len(token) > 3):
            return _cloud_item_to_action(item)
        if command_type == "browser_open_url" and any(t in query for t in ("http", "site", "сайт", "url", "браузер")):
            return _cloud_item_to_action(item)
        if command_type == "open_document_edit" and any(t in query for t in _DOCUMENT_TOKENS):
            return _cloud_item_to_action(item)
    return None


def _cloud_item_to_action(item: dict[str, Any]) -> AvatarSuggestedAction:
    command_type = str(item.get("command_type") or "").strip().lower()
    payload = dict(item.get("payload") or {})
    scope = dict(payload.pop("scope", {}) if "scope" in payload else (item.get("scope") or {}))
    risk = str(item.get("risk_level") or payload.get("capability_risk_level") or "medium").lower()
    cap_map = {
        "browser_open_url": ("agent.browser.open_url", "network_control"),
        "open_url": ("agent.browser.open_url", "network_control"),
        "open_document_edit": ("agent.desktop.app", "application_control"),
        "write_file_in_workspace": ("agent.workspace.file.write", "file_access"),
        "read_file_in_workspace": ("agent.workspace.file.read", "file_access"),
        "empty_trash": ("agent.workspace.file.write", "file_access"),
        "check_update": ("agent.update.check", None),
    }
    required_cap, ui_cap = cap_map.get(command_type, ("agent.status.read", None))
    payload.setdefault("required_capability", required_cap)
    payload.setdefault("user_consent_state", "consented_via_connect_flow")
    payload.setdefault("source_agent", str(item.get("source_agent") or "iuve_cloud_planner"))
    payload.setdefault("model_source", str(item.get("model_source") or "planner_rules_v0.1.21"))
    payload.setdefault(
        "reasoning_summary",
        str(item.get("reasoning_summary") or item.get("title") or "Cloud planner suggestion"),
    )
    return AvatarSuggestedAction(
        action_id=str(item.get("suggestion_id") or secrets.token_hex(4)),
        command_type=command_type,
        title_key="avatar.action.cloud_suggestion.title",
        description_key="avatar.action.cloud_suggestion.body",
        required_capability=required_cap,
        risk_level=risk,
        payload=payload,
        scope=scope,
        required_ui_capability=ui_cap,
    )


def _plan_preview_result(
    *,
    plan: dict[str, Any],
    trust_state: str,
    capabilities_enabled: int,
    ui_caps: dict[str, bool],
    request_type: AvatarRequestType,
    plan_fetch_error: str | None = None,
    plan_blocked_ui_keys: set[str] | frozenset[str] | None = None,
) -> AvatarChatResult:
    from ..active.goal_memory import goal_memory_store

    goal_memory_store().record_from_plan(plan)
    trust = str(trust_state or "").strip().lower()
    if trust != "trusted":
        return AvatarChatResult(
            "avatar.chat.response.trust_required",
            None,
            advisory_only=True,
            block_reason_key="trust_not_trusted",
            execution_plan=plan,
            plan_preview=True,
            is_compound=True,
            plan_fetch_error=plan_fetch_error,
            request_type=request_type,
        )
    required_caps: list[str] = []
    for step in plan.get("steps") or []:
        if not isinstance(step, dict):
            continue
        command_type = str(step.get("command_type") or "").strip().lower()
        if command_type == "application_launch":
            required_caps.append("application_control")
        if command_type == "open_document_edit":
            required_caps.append("application_control")
        if command_type == "keyboard_type_text":
            required_caps.append("keyboard_control")
        if command_type in {"browser_open_url", "open_url"}:
            required_caps.append("network_control")
        if command_type in {"write_file_in_workspace", "read_file_in_workspace"}:
            required_caps.append("file_access")
        if command_type == "empty_trash":
            required_caps.append("file_access")
        if command_type == "capture_screenshot":
            required_caps.append("screen_vision")
        if command_type == "verify_screen_contains_text":
            required_caps.append("screen_vision")
        if command_type == "describe_active_window":
            required_caps.append("screen_vision")
        if command_type == "list_files_in_folder":
            required_caps.append("file_access")
    for cap in required_caps:
        if not ui_caps.get(cap, False):
            return _permission_block_result(
                cap,
                ui_caps=ui_caps,
                plan_blocked_ui_keys=plan_blocked_ui_keys,
                execution_plan=plan,
                plan_preview=True,
                is_compound=True,
                request_type=request_type,
            )
    if capabilities_enabled <= 0:
        return AvatarChatResult(
            "avatar.chat.response.no_capabilities",
            None,
            advisory_only=True,
            execution_plan=plan,
            plan_preview=True,
            is_compound=True,
            block_reason_key="capability_not_granted",
            request_type=request_type,
        )
    return AvatarChatResult(
        "avatar.chat.response.plan_preview",
        None,
        advisory_only=True,
        execution_plan=plan,
        plan_preview=True,
        is_compound=True,
        plan_fetch_error=plan_fetch_error,
        request_type=request_type,
    )


@dataclass(frozen=True)
class _LlmFirstRouteOutcome:
    result: AvatarChatResult | None = None
    compound: CompoundIntentResult | None = None


def _synthetic_text_for_llm_device_action(action: str, params: dict[str, Any], text: str) -> str:
    application = str(params.get("application") or params.get("app") or "").strip()
    if action == "application_launch" and application:
        return f"запусти {application}"
    if action == "browser_open_url":
        url = str(params.get("url") or "").strip()
        if url:
            return f"открой {url}"
    return text


def _finalize_action_ready_result(
    action: AvatarSuggestedAction,
    *,
    trust_state: str,
    capabilities_enabled: int,
    ui_caps: dict[str, bool],
    plan_blocked_ui_keys: set[str] | frozenset[str] | None,
    request_type: AvatarRequestType,
) -> AvatarChatResult:
    trust = str(trust_state or "").strip().lower()
    if trust != "trusted":
        return AvatarChatResult(
            "avatar.chat.response.trust_required",
            None,
            advisory_only=True,
            block_reason_key="trust_not_trusted",
            request_type=request_type,
        )
    if action.required_ui_capability and not ui_caps.get(action.required_ui_capability, False):
        return _permission_block_result(
            action.required_ui_capability,
            ui_caps=ui_caps,
            plan_blocked_ui_keys=plan_blocked_ui_keys,
            suggested_action=action,
            request_type=request_type,
        )
    if capabilities_enabled <= 0 and action.required_ui_capability:
        return AvatarChatResult(
            "avatar.chat.response.no_capabilities",
            None,
            advisory_only=True,
            suggested_action=action,
            block_reason_key="capability_not_granted",
            request_type=request_type,
        )
    return AvatarChatResult(
        "avatar.chat.response.action_ready",
        action.required_ui_capability,
        advisory_only=False,
        suggested_action=action,
        request_type=request_type,
    )


def _route_llm_device_action(
    text: str,
    *,
    action: str,
    params: dict[str, Any],
    trust_state: str,
    capabilities_enabled: int,
    ui_caps: dict[str, bool],
    plan_blocked_ui_keys: set[str] | frozenset[str] | None,
    request_type: AvatarRequestType,
) -> AvatarChatResult | None:
    routed_text = _synthetic_text_for_llm_device_action(action, params, text)
    action_key = str(action or "").strip().lower()
    router_map = {
        "get_diagnostics": route_diagnostics_intent,
        "application_launch": route_application_intent,
        "browser_open_url": route_browser_intent,
        "open_folder": route_folder_intent,
        "open_document_edit": route_document_intent,
        "check_update": route_update_intent,
        "keyboard_type_text": route_keyboard_type_intent,
        "mouse_move_relative": route_mouse_intent,
        "empty_trash": route_empty_trash_intent,
    }
    router = router_map.get(action_key)
    if router is None:
        return None
    suggested = router(routed_text)
    if suggested is None and action_key == "get_diagnostics":
        suggested = route_diagnostics_intent("сколько памяти")
    if suggested is None:
        return None
    if action_key == "get_diagnostics" and suggested.required_ui_capability is None:
        return _finalize_action_ready_result(
            suggested,
            trust_state=trust_state,
            capabilities_enabled=capabilities_enabled,
            ui_caps=ui_caps,
            plan_blocked_ui_keys=plan_blocked_ui_keys,
            request_type=request_type,
        )
    return _finalize_action_ready_result(
        suggested,
        trust_state=trust_state,
        capabilities_enabled=capabilities_enabled,
        ui_caps=ui_caps,
        plan_blocked_ui_keys=plan_blocked_ui_keys,
        request_type=request_type,
    )


def _try_llm_first_routing(
    text: str,
    *,
    trust_state: str,
    capabilities_enabled: int,
    ui_caps: dict[str, bool],
    plan_blocked_ui_keys: set[str] | frozenset[str] | None,
    request_type: AvatarRequestType,
    compound: CompoundIntentResult,
) -> _LlmFirstRouteOutcome:
    from iuve_connect_agents.shared.avatar_llm_intent import (
        AvatarIntentType,
        AvatarLlmIntentFirstResult,
        classify_avatar_intent_llm,
    )
    from iuve_connect_agents.shared.feature_flags import avatar_llm_intent_first_enabled

    try:
        from ..active.device_capability_scanner import is_device_inventory_question

        if is_device_inventory_question(text):
            return _LlmFirstRouteOutcome()
    except ImportError:
        pass

    if not avatar_llm_intent_first_enabled(trust_state=trust_state):
        return _LlmFirstRouteOutcome()

    classified: AvatarLlmIntentFirstResult = classify_avatar_intent_llm(
        text,
        context={"trust_state": trust_state},
        trust_state=trust_state,
    )
    if classified.source != "llm":
        return _LlmFirstRouteOutcome()

    if classified.intent_type == AvatarIntentType.CLARIFY:
        question = classified.clarify_question or "Уточните, пожалуйста, что именно нужно сделать?"
        return _LlmFirstRouteOutcome(
            result=AvatarChatResult(
                "avatar.chat.response.intent_clarify",
                None,
                advisory_only=True,
                request_type=request_type,
                response_params={"question": question},
            )
        )

    if classified.intent_type == AvatarIntentType.COMPOUND_PLAN:
        updated = CompoundIntentResult(
            is_compound=True,
            defer_regex=compound.defer_regex or classified.is_compound,
            categories_matched=compound.categories_matched or ("llm_compound",),
        )
        return _LlmFirstRouteOutcome(compound=updated)

    if classified.intent_type == AvatarIntentType.ADVISORY:
        trust = str(trust_state or "").strip().lower()
        if trust != "trusted":
            return _LlmFirstRouteOutcome(
                result=AvatarChatResult(
                    "avatar.chat.response.trust_required",
                    None,
                    advisory_only=True,
                    block_reason_key="trust_not_trusted",
                    request_type=request_type,
                )
            )
        answer = classified.advisory_answer
        if not answer:
            from iuve_connect_agents.shared.avatar_llm_advisory import fetch_avatar_llm_advisory_response

            answer = fetch_avatar_llm_advisory_response(text, trust_state=trust_state)
        if answer:
            return _LlmFirstRouteOutcome(
                result=AvatarChatResult(
                    "avatar.chat.response.llm_advisory",
                    None,
                    advisory_only=True,
                    request_type=request_type,
                    response_params={"answer": answer},
                )
            )
        return _LlmFirstRouteOutcome(
            result=_llm_unavailable_chat_result(request_type=request_type)
        )

    if classified.intent_type == AvatarIntentType.DEVICE_ACTION:
        try:
            from .avatar_local_plan_fallback import resolve_local_open_type_plan

            if resolve_local_open_type_plan(text) is not None:
                return _LlmFirstRouteOutcome()
        except ImportError:
            pass
        device_result = _route_llm_device_action(
            text,
            action=classified.action,
            params=classified.params,
            trust_state=trust_state,
            capabilities_enabled=capabilities_enabled,
            ui_caps=ui_caps,
            plan_blocked_ui_keys=plan_blocked_ui_keys,
            request_type=request_type,
        )
        if device_result is not None:
            return _LlmFirstRouteOutcome(result=device_result)

    return _LlmFirstRouteOutcome()


@dataclass(frozen=True)
class _AvatarIntentResolution:
    request_type: AvatarRequestType
    compound: CompoundIntentResult
    llm_intent: str | None = None
    source: str = "regex"


def _resolve_avatar_intent_for_chat(text: str, *, trust_state: str = "") -> _AvatarIntentResolution:
    """Resolve intent via shared bridge; regex-only fallback when package module is missing."""
    try:
        from iuve_connect_agents.shared.avatar_llm_intent import resolve_avatar_intent_for_chat

        bridge = resolve_avatar_intent_for_chat(text, trust_state=trust_state)
        return _AvatarIntentResolution(
            request_type=bridge.request_type,  # type: ignore[arg-type]
            compound=bridge.compound,
            llm_intent=bridge.llm_intent,
            source=bridge.source,
        )
    except ImportError:
        request_type = classify_avatar_request_type(text)
        return _AvatarIntentResolution(
            request_type=request_type,
            compound=detect_compound_intent(text),
            source="regex",
        )


def build_avatar_chat_result(
    text: str,
    *,
    trust_state: str = "",
    capabilities_enabled: int = 0,
    ui_capability_enabled: dict[str, bool] | None = None,
    plan_blocked_ui_keys: set[str] | frozenset[str] | None = None,
    cloud_suggestions: list[dict[str, Any]] | None = None,
    execution_plan: dict[str, Any] | None = None,
    plan_fetch_error: str | None = None,
    planner_mode: str = "",
    prior_user_text: str | None = None,
    orchestra_decision: dict[str, Any] | None = None,
) -> AvatarChatResult:
    from .avatar_intent_router import normalize_cursor_aliases, wants_screen_verification
    from .avatar_local_plan_fallback import build_screen_capture_plan

    text = expand_avatar_conversation_context(
        normalize_cursor_aliases(str(text or "")),
        prior_user_text=prior_user_text,
    )

    math_answer = try_local_math_answer(text)
    if math_answer:
        return _local_math_chat_result(math_answer, request_type="general_chat")

    if isinstance(orchestra_decision, dict) and orchestra_decision.get("ok"):
        decision_kind = str(orchestra_decision.get("decision") or "")
        contract = orchestra_decision.get("execution_contract")
        if not isinstance(contract, dict):
            contract = {}
        if decision_kind == "answer_only":
            answer = str(orchestra_decision.get("answer") or contract.get("answer") or "").strip()
            if answer:
                try:
                    from iuve_connect_agents.shared.answer_quality import AnswerQualityPipeline

                    answer = AnswerQualityPipeline().process(
                        answer,
                        question=str(text or ""),
                        channel="desktop_agent",
                    ).render_body()
                except Exception:
                    pass
                return AvatarChatResult(
                    "avatar.chat.response.llm_advisory",
                    None,
                    advisory_only=True,
                    request_type="general_chat",
                    response_params={"answer": answer},
                )
        if decision_kind in {"continue_plan", "revise_plan", "new_plan"} and execution_plan:
            trust = str(trust_state or "").strip().lower()
            if trust == "trusted":
                return AvatarChatResult(
                    "avatar.chat.response.plan_preview",
                    None,
                    advisory_only=True,
                    execution_plan=execution_plan,
                    plan_preview=True,
                    is_compound=True,
                    request_type="device_task",
                )
    intent_resolution = _resolve_avatar_intent_for_chat(text, trust_state=trust_state)
    request_type = intent_resolution.request_type  # type: ignore[assignment]
    compound = intent_resolution.compound
    ui_caps = ui_capability_enabled or {}

    from ..active.device_capability_scanner import (
        format_device_inventory_answer_ru,
        is_device_inventory_question,
        load_profile,
    )

    if is_device_inventory_question(text):
        return AvatarChatResult(
            "avatar.chat.response.device_inventory",
            None,
            advisory_only=True,
            request_type=request_type,
            response_params={"summary": format_device_inventory_answer_ru(text, load_profile())},
        )

    from .avatar_intent_router import is_plan_resume_intent
    from ..active.avatar_paused_plan_store import avatar_paused_plan_store

    if is_plan_resume_intent(text):
        paused = avatar_paused_plan_store().load()
        if paused is None or paused.status != "paused":
            return AvatarChatResult(
                "avatar.chat.response.plan_already_completed",
                None,
                advisory_only=True,
                request_type=request_type,
            )
        return AvatarChatResult(
            "avatar.chat.response.plan_resume",
            None,
            advisory_only=False,
            execution_plan=dict(paused.plan_json),
            plan_preview=True,
            plan_resume=True,
            resume_step_index=paused.step_index,
            request_type=request_type,
            response_params={
                "step_index": paused.step_index + 1,
                "goal": str(paused.plan_json.get("goal_summary") or ""),
                "pause_reason": paused.pause_reason,
            },
        )

    from .avatar_file_drop import build_image_path_vision_plan, extract_image_file_path

    image_path = extract_image_file_path(text)
    if image_path is not None:
        image_plan = build_image_path_vision_plan(text, image_path=image_path)
        if image_plan is not None:
            grant_active = bool(ui_caps.get("screen_vision", False))
            from iuve_connect_agents.shared.screen_vision_gate import desktop_vision_block_reason

            block_reason = desktop_vision_block_reason(screen_vision_granted=grant_active)
            if block_reason == "vision_grant_required":
                return AvatarChatResult(
                    "avatar.chat.response.vision_grant_required",
                    "screen_vision",
                    advisory_only=True,
                    block_reason_key="vision_grant_required",
                    execution_plan=image_plan,
                    plan_preview=True,
                    request_type=request_type,
                )
            if block_reason == "vision_flag_disabled":
                return AvatarChatResult(
                    "avatar.chat.response.vision_flag_required",
                    "screen_vision",
                    advisory_only=True,
                    block_reason_key="vision_flag_disabled",
                    execution_plan=image_plan,
                    plan_preview=True,
                    request_type=request_type,
                )
            if block_reason is not None:
                return AvatarChatResult(
                    "avatar.chat.response.vision_required",
                    "screen_vision",
                    advisory_only=True,
                    block_reason_key=block_reason,
                    request_type=request_type,
                )
            return _plan_preview_result(
                plan=image_plan,
                trust_state=trust_state,
                capabilities_enabled=capabilities_enabled,
                ui_caps=ui_caps,
                request_type=request_type,
                plan_blocked_ui_keys=plan_blocked_ui_keys,
            )

    if wants_screen_verification(text):
        capture_plan = build_screen_capture_plan(text)
        if capture_plan is not None:
            grant_active = bool(ui_caps.get("screen_vision", False))
            from iuve_connect_agents.shared.screen_vision_gate import desktop_vision_block_reason

            block_reason = desktop_vision_block_reason(screen_vision_granted=grant_active)
            if block_reason == "vision_grant_required":
                return AvatarChatResult(
                    "avatar.chat.response.vision_grant_required",
                    "screen_vision",
                    advisory_only=True,
                    block_reason_key="vision_grant_required",
                    execution_plan=capture_plan,
                    plan_preview=True,
                    request_type=request_type,
                )
            if block_reason == "vision_flag_disabled":
                return AvatarChatResult(
                    "avatar.chat.response.vision_flag_required",
                    "screen_vision",
                    advisory_only=True,
                    block_reason_key="vision_flag_disabled",
                    execution_plan=capture_plan,
                    plan_preview=True,
                    request_type=request_type,
                )
            if block_reason is not None:
                return AvatarChatResult(
                    "avatar.chat.response.vision_required",
                    "screen_vision",
                    advisory_only=True,
                    block_reason_key=block_reason,
                    request_type=request_type,
                )
            return _plan_preview_result(
                plan=capture_plan,
                trust_state=trust_state,
                capabilities_enabled=capabilities_enabled,
                ui_caps=ui_caps,
                request_type=request_type,
                plan_blocked_ui_keys=plan_blocked_ui_keys,
            )

    if request_type == "capabilities_faq":
        trust = str(trust_state or "").strip().lower()
        if trust == "trusted":
            from iuve_connect_agents.shared.avatar_llm_advisory import fetch_avatar_llm_advisory_response

            advisory = fetch_avatar_llm_advisory_response(text, trust_state=trust_state)
            if advisory:
                return AvatarChatResult(
                    "avatar.chat.response.llm_advisory",
                    None,
                    advisory_only=True,
                    request_type=request_type,
                    response_params={"answer": advisory},
                )
        return AvatarChatResult(
            "avatar.chat.response.capabilities_faq",
            None,
            advisory_only=True,
            request_type=request_type,
        )

    if request_type == "meta_external":
        return AvatarChatResult(
            "avatar.chat.response.meta_external",
            None,
            advisory_only=True,
            request_type=request_type,
        )

    from .avatar_local_plan_fallback import (
        build_plan_only_preview,
        maybe_prefer_libreoffice_plan,
        resolve_local_open_type_plan,
    )

    plan_only = build_plan_only_preview(text)
    if plan_only is not None:
        steps_text = format_plan_preview_steps(plan_only)
        return AvatarChatResult(
            "avatar.chat.response.plan_only",
            None,
            advisory_only=True,
            execution_plan=plan_only,
            plan_preview=True,
            is_compound=True,
            request_type=request_type,
            response_params={
                "goal": str(plan_only.get("goal_summary") or ""),
                "steps": steps_text,
            },
        )

    local_open_type_plan = resolve_local_open_type_plan(text, prior_user_text=prior_user_text)
    if execution_plan and avatar_execution_plan_enabled():
        execution_plan = maybe_prefer_libreoffice_plan(
            text,
            execution_plan,
            prior_user_text=prior_user_text,
        )
    if local_open_type_plan is not None and not (execution_plan and avatar_execution_plan_enabled()):
        return _plan_preview_result(
            plan=local_open_type_plan,
            trust_state=trust_state,
            capabilities_enabled=capabilities_enabled,
            ui_caps=ui_caps,
            request_type=request_type,
            plan_blocked_ui_keys=plan_blocked_ui_keys,
        )

    llm_route = _try_llm_first_routing(
        text,
        trust_state=trust_state,
        capabilities_enabled=capabilities_enabled,
        ui_caps=ui_caps,
        plan_blocked_ui_keys=plan_blocked_ui_keys,
        request_type=request_type,
        compound=compound,
    )
    if llm_route.compound is not None:
        compound = llm_route.compound
    if llm_route.result is not None:
        return llm_route.result

    defer = compound.defer_regex

    if execution_plan and avatar_execution_plan_enabled() and compound.is_compound:
        from .avatar_execution_status import should_show_advisory_consultation
        from .avatar_local_plan_fallback import maybe_correct_misrouted_plan, maybe_replace_get_status_stub

        corrected_plan = maybe_replace_get_status_stub(
            text,
            execution_plan,
            prior_user_text=prior_user_text,
        ) or execution_plan
        corrected_plan = maybe_correct_misrouted_plan(
            text,
            corrected_plan,
            prior_user_text=prior_user_text,
        ) or corrected_plan
        defer = should_defer_to_planner(text)
        if should_show_advisory_consultation(
            text,
            corrected_plan,
            is_compound=compound.is_compound,
            defer_to_planner=defer,
        ):
            return AvatarChatResult(
                "avatar.chat.response.advisory_consultation_required",
                None,
                advisory_only=True,
                execution_plan=corrected_plan,
                plan_preview=False,
                is_compound=True,
                plan_fetch_error=plan_fetch_error,
                request_type=request_type,
            )
        trust = str(trust_state or "").strip().lower()
        if trust != "trusted":
            return AvatarChatResult(
                "avatar.chat.response.trust_required",
                None,
                advisory_only=True,
                block_reason_key="trust_not_trusted",
                execution_plan=corrected_plan,
                plan_preview=True,
                is_compound=True,
                plan_fetch_error=plan_fetch_error,
                request_type=request_type,
            )
        return AvatarChatResult(
            "avatar.chat.response.plan_preview",
            None,
            advisory_only=True,
            execution_plan=corrected_plan,
            plan_preview=True,
            is_compound=True,
            plan_fetch_error=plan_fetch_error,
            request_type=request_type,
        )

    if defer and plan_fetch_error and not execution_plan:
        from .avatar_local_plan_fallback import build_local_compound_plan

        local_plan = build_local_compound_plan(text)
        if local_plan:
            trust = str(trust_state or "").strip().lower()
            if trust != "trusted":
                return AvatarChatResult(
                    "avatar.chat.response.trust_required",
                    None,
                    advisory_only=True,
                    block_reason_key="trust_not_trusted",
                    execution_plan=local_plan,
                    plan_preview=True,
                    is_compound=True,
                    plan_fetch_error=plan_fetch_error,
                    request_type=request_type,
                )
            return AvatarChatResult(
                "avatar.chat.response.plan_preview",
                None,
                advisory_only=True,
                execution_plan=local_plan,
                plan_preview=True,
                is_compound=True,
                plan_fetch_error=plan_fetch_error,
                request_type=request_type,
            )

    capability = classify_capability_requirement(text)
    if capability:
        if capability == "root_operations":
            return AvatarChatResult(
                "avatar.chat.response.root_required",
                capability,
                request_type=request_type,
            )
        if not ui_caps.get(capability, False):
            return _permission_block_result(
                capability,
                ui_caps=ui_caps,
                plan_blocked_ui_keys=plan_blocked_ui_keys,
                request_type=request_type,
            )

    if is_diagnostics_query(text):
        action = route_diagnostics_intent(text)
        if action is not None:
            trust = str(trust_state or "").strip().lower()
            if trust != "trusted":
                return AvatarChatResult(
                    "avatar.chat.response.trust_required",
                    None,
                    advisory_only=True,
                    block_reason_key="trust_not_trusted",
                    request_type=request_type,
                )
            return AvatarChatResult(
                "avatar.chat.response.action_ready",
                None,
                advisory_only=False,
                suggested_action=action,
                request_type=request_type,
            )
        return AvatarChatResult("avatar.chat.response.diagnostics", None, request_type=request_type)

    if defer:
        if plan_fetch_error:
            return AvatarChatResult(
                "avatar.chat.response.plan_fetch_failed",
                None,
                advisory_only=True,
                plan_preview=False,
                is_compound=True,
                block_reason_key="compound_intent_plan_fetch_failed",
                plan_fetch_error=plan_fetch_error,
                request_type=request_type,
            )
        regex_compound = detect_compound_intent(text)
        if not regex_compound.is_compound and not execution_plan:
            trust = str(trust_state or "").strip().lower()
            if trust == "trusted":
                from iuve_connect_agents.shared.avatar_llm_advisory import fetch_avatar_llm_advisory_response

                advisory = fetch_avatar_llm_advisory_response(text, trust_state=trust_state)
                if advisory:
                    return AvatarChatResult(
                        "avatar.chat.response.llm_advisory",
                        None,
                        advisory_only=True,
                        request_type=request_type,
                        response_params={"answer": advisory},
                    )
        return AvatarChatResult(
            "avatar.chat.response.plan_deferred",
            None,
            advisory_only=True,
            plan_preview=True,
            is_compound=True,
            block_reason_key="compound_intent_no_plan",
            request_type=request_type,
        )

    action = route_avatar_intent_with_cloud(
        text,
        cloud_suggestions=cloud_suggestions,
        defer_compound=defer,
    )
    if action is None:
        try:
            from ..active.memory_query_intent import answer_memory_query, is_memory_query_intent

            if is_memory_query_intent(text):
                return AvatarChatResult(
                    "avatar.chat.response.llm_advisory",
                    None,
                    advisory_only=True,
                    request_type=request_type,
                    response_params={"answer": answer_memory_query(text)},
                )
        except ImportError:
            pass
        if is_general_chat_query(text):
            trust = str(trust_state or "").strip().lower()
            if trust != "trusted":
                return AvatarChatResult(
                    "avatar.chat.response.trust_required",
                    None,
                    advisory_only=True,
                    block_reason_key="trust_not_trusted",
                    request_type=request_type,
                )
            math_answer = try_local_math_answer(text)
            if math_answer:
                return _local_math_chat_result(math_answer, request_type=request_type)
            from iuve_connect_agents.shared.avatar_llm_advisory import fetch_avatar_llm_advisory_response

            advisory = fetch_avatar_llm_advisory_response(text, trust_state=trust_state)
            if advisory:
                return AvatarChatResult(
                    "avatar.chat.response.llm_advisory",
                    None,
                    advisory_only=True,
                    request_type=request_type,
                    response_params={"answer": advisory},
                )
            return _llm_unavailable_chat_result(request_type=request_type)
        return AvatarChatResult("avatar.chat.response.placeholder", None, request_type=request_type)

    trust = str(trust_state or "").strip().lower()
    if trust != "trusted":
        return AvatarChatResult(
            "avatar.chat.response.trust_required",
            None,
            advisory_only=True,
            block_reason_key="trust_not_trusted",
            request_type=request_type,
        )

    if action.required_ui_capability and not ui_caps.get(action.required_ui_capability, False):
        return _permission_block_result(
            action.required_ui_capability,
            ui_caps=ui_caps,
            plan_blocked_ui_keys=plan_blocked_ui_keys,
            suggested_action=action,
            request_type=request_type,
        )

    if capabilities_enabled <= 0:
        return AvatarChatResult(
            "avatar.chat.response.no_capabilities",
            None,
            advisory_only=True,
            suggested_action=action,
            block_reason_key="capability_not_granted",
            request_type=request_type,
        )

    return AvatarChatResult(
        "avatar.chat.response.action_ready",
        action.required_ui_capability,
        advisory_only=False,
        suggested_action=action,
        request_type=request_type,
    )
