from typing import List, Optional

from GramAddict.core.perception.spatial_parser import SpatialNode

# Navigation tab intent → resource_id keyword mapping
_NAV_TAB_MAP = {
    "tap home tab": "feed_tab",
    "tap explore tab": "search_tab",
    "tap reels tab": "clips_tab",
    "tap profile tab": "profile_tab",
    "tap messages tab": "direct_tab",
}


class IntentResolver:
    """
    Translates natural language intents into spatial constraints and node filtering.
    Replaces the generic text/regex matching with structural intelligence.
    """

    def resolve(
        self, intent_description: str, candidates: List[SpatialNode], screen_height: int = 2400
    ) -> Optional[SpatialNode]:
        """
        Finds the best matching node for a given intent autonomously.

        Navigation tab intents use a structural Zone Guard (bottom 15% of screen)
        to guarantee we click the actual nav bar, not a content-area element.
        All other intents delegate to VLM resolution.
        """
        if not candidates:
            return None

        intent_lower = intent_description.lower()

        # ── Navigation Bar Zone Guard ──
        # When intent targets a nav tab, resolve structurally to the bottom nav zone.
        # This prevents the VLM from selecting content profile pictures instead of tabs.
        # The bottom navigation bar is always in the bottom 15% of the screen.
        tab_keyword = _NAV_TAB_MAP.get(intent_lower)
        if tab_keyword:
            nav_zone_y = int(screen_height * 0.85)
            nav_candidates = [
                n for n in candidates if n.y1 >= nav_zone_y and tab_keyword in (n.resource_id or "").lower()
            ]
            if nav_candidates:
                return nav_candidates[0]
            # Fallback: broader search in nav zone by content_desc
            tab_label = intent_lower.replace("tap ", "").replace(" tab", "")
            nav_candidates = [
                n for n in candidates if n.y1 >= nav_zone_y and tab_label in (n.content_desc or "").lower()
            ]
            if nav_candidates:
                return nav_candidates[0]
            return None

        # If the intent is a high-level GOAL that accidentally leaked into the IntentResolver,
        # we explicitly block it from clicking random nodes.
        # IMPORTANT: Use exact match to avoid blocking "tap profile tab" when filtering "open profile"
        abstract_goals = ["open profile", "open explore", "open following", "learn own profile"]
        if intent_lower in abstract_goals:
            return None

        # 1. Ask the Telepathic VLM to find the best node
        import json

        from GramAddict.core.config import Config
        from GramAddict.core.llm_provider import query_telepathic_llm

        # Pre-filter candidates to reduce VLM hallucinations
        filtered_candidates = []
        for n in candidates:
            # Skip massive background containers
            if n.area > 500000:
                continue

            # Structural heuristic: if looking for profile, prioritize nodes that might be profiles
            # and exclude obvious bottom tabs/navigation
            if "profile" in intent_lower:
                res = (n.resource_id or "").lower()
                if "tab" in res or "navigation" in res or "action_bar" in res:
                    continue
            filtered_candidates.append(n)

        if not filtered_candidates:
            filtered_candidates = candidates

        cfg = Config()
        model = getattr(cfg.args, "ai_telepathic_model", "qwen3.5:latest")
        url = getattr(cfg.args, "ai_telepathic_url", "http://localhost:11434/api/generate")

        # Prepare context
        node_context = []
        for i, node in enumerate(filtered_candidates):
            text = node.text or ""
            desc = node.content_desc or ""
            res_id = node.resource_id or ""
            node_context.append(f"[{i}] text='{text}', desc='{desc}', id='{res_id}', bounds=[{node.y1},{node.y2}]")

        prompt = (
            f"You are a Spatial UI Intent Resolver.\n"
            f"Goal: Find the single best UI element to interact with to satisfy the intent: '{intent_description}'.\n"
            f"CRITICAL RULES:\n"
            f"- If the intent is about opening the 'post author', STRICTLY require 'row_feed_photo_profile' in the ID. Do not select comment authors.\n"
            f"- If the intent is about opening a user profile generally, prioritize nodes containing 'profile_name' or 'profile_image' in their ID, NOT generic action bars or tabs.\n"
            f"- Ignore bottom navigation tabs (home, search, profile) UNLESS the intent explicitly asks to navigate to a primary feed.\n"
            f"Candidates:\n" + "\n".join(node_context) + "\n\n"
            "Reply ONLY with a valid JSON object strictly matching this schema:\n"
            '{"selected_index": <integer or null>}\n'
            "If none of the candidates match the intent, return null."
        )

        try:
            res = query_telepathic_llm(
                model=model,
                url=url,
                system_prompt="Strict JSON intent resolver.",
                user_prompt=prompt,
                use_local_edge=True,
            )
            data = json.loads(res)
            idx = data.get("selected_index")
            if idx is not None and 0 <= idx < len(filtered_candidates):
                return filtered_candidates[idx]
        except Exception as e:
            import logging

            logging.getLogger(__name__).warning(f"⚠️ [IntentResolver] VLM resolution failed ({e}).")

        return None
