import json
import logging
import os
import random
from time import sleep

from colorama import Fore, Style

from GramAddict.core.version import __version__

logger = logging.getLogger(__name__)


def sanitize_text(text):
    return (text or "").strip()


def random_sleep(inf=1.0, sup=3.0, modulable=True):
    from GramAddict.core.config import Config

    configs = Config()
    try:
        multiplier = float(getattr(configs.args, "speed_multiplier", 1.0))
    except (ValueError, TypeError):
        multiplier = 1.0
    delay = random.uniform(inf, sup) / (multiplier if modulable else 1.0)
    sleep(max(delay, 0.2))


def config_examples():
    logger.debug("Config examples handled by documentation.")


def check_if_updated():
    logger.info(f"GramAddict v.{__version__}", extra={"color": f"{Style.BRIGHT}{Fore.MAGENTA}"})


def get_instagram_version(device):
    try:
        output = device.shell(f"dumpsys package {device.app_id}").output
        import re

        version_match = re.findall("versionName=(\\S+)", output)
        return version_match[0] if version_match else "unknown"
    except Exception:
        return "unknown"


def close_instagram(device, force_kill=False):
    if force_kill:
        logger.info("Force-closing Instagram app to clean session state.")
        try:
            device.app_stop(device.app_id)
        except Exception as e:
            logger.debug(f"Error closing app: {e}")
    else:
        logger.info("Backgrounding Instagram app (minimizing).")
        try:
            device.press("home")
        except Exception as e:
            logger.debug(f"Error pressing home: {e}")


def open_instagram(device, force_restart=False):
    if force_restart:
        logger.info("Opening Instagram app (Fresh Start).")
        close_instagram(device, force_kill=True)
        device.app_start(device.app_id)
        random_sleep(3, 5, modulable=False)
    else:
        logger.info("Bringing Instagram app to foreground.")
        device.app_start(device.app_id)
        random_sleep(1, 2, modulable=False)
    return True


def set_time_delta(args):
    args.time_delta_session = random.randint(-300, 300)


def wait_for_next_session(time_left, session_state, sessions, device):
    logger.info(f"Waiting {time_left} until next working hours.")
    sleep(60)


def get_value(count, name, default=0):
    if count is None:
        return default
    if isinstance(count, (int, float)):
        return count
    try:
        if "-" in str(count):
            parts = str(count).split("-")
            return random.randint(int(parts[0]), int(parts[1]))
        return int(count)
    except Exception:
        return default


_LEARNED_AD_MARKERS_FILE = os.path.join(os.getcwd(), "learned_ad_markers.json")
_LEARNED_AD_MARKERS_CACHE = None


def get_learned_ad_markers() -> set:
    global _LEARNED_AD_MARKERS_CACHE
    if _LEARNED_AD_MARKERS_CACHE is not None:
        return _LEARNED_AD_MARKERS_CACHE

    if os.path.exists(_LEARNED_AD_MARKERS_FILE):
        try:
            with open(_LEARNED_AD_MARKERS_FILE, "r") as f:
                _LEARNED_AD_MARKERS_CACHE = set(json.load(f))
        except Exception as e:
            logger.error(f"Failed to load learned ad markers: {e}")
            _LEARNED_AD_MARKERS_CACHE = set()
    else:
        _LEARNED_AD_MARKERS_CACHE = set()

    return _LEARNED_AD_MARKERS_CACHE


def learn_ad_marker(marker: str, xml_hierarchy: str):
    global _LEARNED_AD_MARKERS_CACHE
    if not marker or len(marker) > 30:
        return

    marker = marker.strip().lower()

    # Structural verification: the VLM-suggested marker MUST exist as an exact node text/desc in the current UI!
    import xml.etree.ElementTree as ET

    try:
        root = ET.fromstring(xml_hierarchy)
        found_in_ui = False
        for node in root.iter("node"):
            text = node.attrib.get("text", "").strip().lower()
            desc = node.attrib.get("content-desc", "").strip().lower()
            if text == marker or desc == marker:
                found_in_ui = True
                break

        if not found_in_ui:
            logger.debug(
                f"🧠 [Autonomous FSD] Rejected hallucinated Ad marker '{marker}' (not found as exact node match in UI)."
            )
            return
    except Exception:
        return

    markers = get_learned_ad_markers()
    if marker not in markers and marker not in {"ad", "sponsored", "advertisement"}:
        markers.add(marker)
        logger.info(
            f"🧠 [Autonomous FSD] Verified and Learned new Ad marker: '{marker}'. Persisting for zero-latency detection.",
            extra={"color": f"{Style.BRIGHT}{Fore.GREEN}"},
        )
        try:
            with open(_LEARNED_AD_MARKERS_FILE, "w") as f:
                json.dump(list(markers), f)
        except Exception as e:
            logger.error(f"Failed to save learned ad markers: {e}")


def is_ad(xml_hierarchy: str, cognitive_stack: dict = None) -> bool:
    """
    Checks if the current view contains an advertisement using autonomous learning.

    If a cognitive_stack is provided, it uses the Telepathic Engine for
    semantic classification (Zero-Latency vector lookup).
    """
    import xml.etree.ElementTree as ET

    if cognitive_stack:
        telepathic = cognitive_stack.get("telepathic")
        if telepathic:
            # Semantic classification (ZERO hardcoded strings)
            classification = telepathic.classify_screen_content(xml_hierarchy, "sponsored_content")
            if classification == "sponsored":
                return True

    # --- Legacy Fallback ---
    # Regex word boundaries prevent false positives like 'brunette_abroad'
    AD_RESOURCE_IDS = [
        "com.instagram.android:id/ad_cta_button",
        "com.instagram.android:id/sponsored_label",
        "com.instagram.android:id/clips_single_image_ads_media_content",
        "com.instagram.android:id/ads_carousel_progress_bar",
        "com.instagram.android:id/ad_not_interested_button",
    ]

    # Standalone label patterns: match only when the text/desc IS the ad marker,
    # not when "ad" appears inside longer phrases like "Create messaging ad"
    AD_EXACT_LABELS = {"ad", "sponsored", "advertisement"}
    AD_EXACT_LABELS.update(get_learned_ad_markers())

    try:
        root = ET.fromstring(xml_hierarchy)

        # Check if we are in a feed (to prevent false positives on profiles with 'Ad Tools' buttons)
        from GramAddict.core.perception.feed_analysis import FEED_MARKERS

        in_feed = any(marker in xml_hierarchy for marker in FEED_MARKERS)

        for node in root.iter("node"):
            attrib = node.attrib
            content_desc = attrib.get("content-desc", "")
            text = attrib.get("text", "")
            res_id = attrib.get("resource-id", "")

            # Structural check (Instagram specific) is always trusted
            if any(marker_id in res_id for marker_id in AD_RESOURCE_IDS):
                return True

            # Exact label match: only trigger when the entire text/desc
            # IS an ad marker (e.g. text="Ad", content-desc="Sponsored")
            # We ONLY trust this if we are actually in a feed, to prevent triggering
            # on the "Ad Tools" / "Ad" buttons present on business profiles.
            if in_feed:
                if text.strip().lower() in AD_EXACT_LABELS:
                    return True
                if content_desc.strip().lower() in AD_EXACT_LABELS:
                    return True
    except Exception:
        pass

    return False
