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")
        url = getattr(self.args, "ai_telepathic_url")

        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]:
        pass
