import json
import logging
import re
from typing import List, Optional

from GramAddict.core.llm_provider import query_telepathic_llm
from GramAddict.core.perception.spatial_parser import SpatialNode

logger = logging.getLogger(__name__)


class SemanticEvaluator:
    """
    Handles LLM/VLM interaction for high-level semantic analysis of the UI.
    Delegates vision processing and prompt engineering out of the core routing engine.
    """

    def __init__(self):
        from GramAddict.core.config import Config

        try:
            self.args = Config().args
        except Exception:
            self.args = None

    def _query_vlm(self, prompt: str, screenshot_b64: str) -> Optional[str]:
        if not self.args:
            logger.warning("👁️ [Vision Core] No config available. Cannot query VLM.")
            return None

        model = getattr(self.args, "ai_telepathic_model", "llama3.2-vision")
        url = getattr(self.args, "ai_telepathic_url", "http://localhost:11434/api/generate")

        try:
            res = query_telepathic_llm(
                model=model,
                url=url,
                system_prompt="You are an expert Instagram assistant.",
                user_prompt=prompt,
                images_b64=[screenshot_b64],
            )
            return res
        except Exception as e:
            logger.error(f"👁️ [Vision Core] LLM query failed: {e}")
            return None

    def evaluate_grid_visuals(
        self, device, persona_interests: list[str], grid_nodes: List[SpatialNode]
    ) -> Optional[SpatialNode]:
        """
        Takes the spatial grid nodes and asks the VLM which one best matches the persona.
        """
        logger.info(f"👁️ [Vision Core] Analyzing grid aesthetics against niche interests: {persona_interests}...")

        if not grid_nodes:
            return None

        # Take a screenshot
        try:
            screenshot_b64 = device.get_screenshot_b64()
        except Exception as e:
            logger.error(f"👁️ [Vision Core] Failed to capture screenshot: {e}")
            return None

        simplified_nodes = []
        for i, node in enumerate(grid_nodes[:9]):  # Limit to 9 to save tokens
            simplified_nodes.append({"index": i, "bounds": node.bounds})

        prompt = f"""
        You are a highly perceptive Instagram user with the following interests: {', '.join(persona_interests)}.
        Look at the provided screenshot of the Instagram Explore/Profile grid.
        Below are the bounding boxes for the top grid posts currently visible.

        {simplified_nodes}

        Your task:
        1. Identify which of these posts visually aligns BEST with your interests.
        2. Reply ONLY in JSON format: {{"index": <int>}}
        3. If absolutely none of them are relevant, reply with {{"index": -1}}.
        """

        try:
            response = self._query_vlm(prompt, screenshot_b64)
            if not response:
                return None

            try:
                data = json.loads(response)
                idx = data.get("index", -1)
                if idx == -1:
                    logger.info("👁️ [Vision Core] VLM rejected all grid items. Will scroll down.")
                    return None

                if 0 <= idx < len(grid_nodes):
                    logger.info(f"👁️ [Vision Core] VLM selected grid item index [{idx}] as the best match.")
                    return grid_nodes[idx]
            except json.JSONDecodeError:
                # Fallback to fuzzy
                clean_res = response.strip().upper()
                match = re.search(r"\d+", clean_res)
                if match:
                    idx = int(match.group())
                    if 0 <= idx < len(grid_nodes):
                        logger.info(f"👁️ [Vision Core] VLM selected grid item index [{idx}] as the best match.")
                        return grid_nodes[idx]
        except Exception as e:
            logger.warning(f"👁️ [Vision Core] Exception during grid evaluation: {e}")

        return None

    def evaluate_post_vibe(self, device, persona_interests: list[str]) -> Optional[dict]:
        """Evaluates whether the currently viewed post aligns with persona interests."""
        logger.info(f"👁️ [Vision Core] Evaluating post vibe against: {persona_interests}")
        try:
            screenshot_b64 = device.get_screenshot_b64()
            prompt = f"""
            You are a user with the following interests: {', '.join(persona_interests)}.
            You are looking at an Instagram post.
            Evaluate if this post is highly relevant to your interests and if you should like/comment on it.
            CRITICAL: Check if this post is an advertisement or sponsored content (look for "Sponsored", "Ad", or promotional product placement).

            Reply ONLY in valid JSON format:
            {{
                "should_like": true/false,
                "should_comment": true/false,
                "is_ad": true/false
            }}
            """
            response = self._query_vlm(prompt, screenshot_b64)
            if response:
                if "```json" in response:
                    json_str = response.split("```json")[1].split("```")[0].strip()
                else:
                    json_str = response.strip()
                try:
                    return json.loads(json_str)
                except json.JSONDecodeError:
                    # Try to close potential unclosed JSON strings
                    if not json_str.endswith("}"):
                        json_str += "}"
                        try:
                            return json.loads(json_str)
                        except json.JSONDecodeError:
                            pass
                    logger.warning(f"👁️ [Vision Core] VLM returned malformed JSON: {response}")
        except Exception as e:
            logger.warning(f"Failed to evaluate post vibe: {e}")
        return None

    def evaluate_profile_vibe(self, device, persona_interests: list[str]) -> Optional[dict]:
        """Evaluates if a profile is worth following."""
        pass

    def classify_screen_content(self, xml_hierarchy: str, target_class: str) -> Optional[str]:
        """
        Fast-Path Structural Analysis.
        Replaces VLM calls for basic structural states.
        """
        if not xml_hierarchy:
            return None

        xml_lower = xml_hierarchy.lower()

        if target_class == "carousel_or_single_post":
            if 'resource-id="com.instagram.android:id/carousel_image"' in xml_lower or (
                'scrollable="true"' in xml_lower and "viewpager" in xml_lower
            ):
                return "carousel"
            return "single"

        elif target_class == "post_has_comments":
            if 'resource-id="com.instagram.android:id/row_feed_button_comment"' in xml_lower:
                return "has_comments"
            return "no_comments"

        elif target_class == "sponsored_content":
            # Heuristic for sponsored content
            if "sponsored" in xml_lower or "gesponsert" in xml_lower:
                return "sponsored"
            return "organic"

        elif target_class == "story_ring_presence":
            if "reel_ring" in xml_lower or "story_ring" in xml_lower:
                return "has_unseen_story"
            return "no_unseen_story"

        elif target_class == "main_feed_presence":
            if 'content-desc="home"' in xml_lower and 'selected="true"' in xml_lower:
                return "main_feed"
            if "feed_tab" in xml_lower and 'selected="true"' in xml_lower:
                return "main_feed"
            return "other"

        elif target_class == "private_account":
            if (
                'resource-id="com.instagram.android:id/row_profile_header_empty_profile_notice_title"' in xml_lower
                and "private" in xml_lower
            ):
                return "private"
            return "public"

        elif target_class == "empty_account":
            if 'resource-id="com.instagram.android:id/row_profile_header_empty_profile_notice_title"' in xml_lower and (
                "no posts" in xml_lower or "noch keine" in xml_lower
            ):
                return "empty"
            return "not_empty"

        elif target_class == "close_friends_content":
            if "close_friends" in xml_lower or "close friends" in xml_lower or "enge freunde" in xml_lower:
                return "close_friends"
            return "normal_content"

        elif target_class in ("profile_follow_status", "follow_button_state"):
            if 'resource-id="com.instagram.android:id/profile_header_follow_button"' in xml_lower:
                return "not_following"
            if 'resource-id="com.instagram.android:id/profile_header_following_button"' in xml_lower:
                return "already_following"
            # Fallback text check for requested state
            if "requested" in xml_lower or "angefragt" in xml_lower:
                return "requested"
            return "unknown"

        elif target_class == "unfollow_bottom_sheet_presence":
            if 'resource-id="com.instagram.android:id/follow_sheet_unfollow_row"' in xml_lower:
                return "unfollow_sheet"
            return "other"

        return None
