import logging
import os
from unittest.mock import MagicMock, create_autospec

import pytest

from GramAddict.core.device_facade import DeviceFacade
from GramAddict.core.telepathic_engine import TelepathicEngine


def pytest_addoption(parser):
    parser.addoption(
        "--live",
        action="store_true",
        default=False,
        help="run tests against a live ADB device (disable DeviceFacade mocks)",
    )


MagicMock.app_id = "com.instagram.android"
MagicMock._get_current_app = MagicMock(return_value="com.instagram.android")


class MockArgs:
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)


class MockConfigs:
    def __init__(self, args):
        self.args = args


def create_mock_device():
    mock = create_autospec(DeviceFacade, instance=True)
    mock.app_id = "com.instagram.android"
    mock.device_id = "test_device"

    mock.info = {"displayWidth": 1080, "displayHeight": 2400}
    mock.get_info.return_value = {"displayWidth": 1080, "displayHeight": 2400}
    mock.cm_to_pixels.side_effect = lambda cm: int(cm * 10)
    mock.shell.return_value = ""  # Ensure SendEventInjector detection gets a string
    import uuid

    mock.dump_hierarchy.side_effect = (
        lambda: f'<hierarchy><node resource-id="com.instagram.android:id/row_feed_photo_profile_name" bounds="[0,200][1080,260]" text="testuser" /><node resource-id="com.instagram.android:id/row_comment_imageview" bounds="[10,10][20,20]" content-desc="Story" text="following" /><node resource-id="com.instagram.android:id/button_like" bounds="[50,50][60,60]" /><node resource-id="com.instagram.android:id/reel_viewer" /><node sid="{uuid.uuid4()}" /></hierarchy>'
    )

    return mock


def create_mock_telepathic_engine():
    mock = create_autospec(TelepathicEngine, instance=True)
    mock.find_best_node.return_value = {"x": 500, "y": 500, "confidence": 0.9}
    mock.evaluate_profile_vibe.return_value = {
        "quality_score": 8,
        "matches_niche": True,
        "reason": "Mocked positive vibe",
    }
    mock.evaluate_grid_visuals.return_value = {
        "x": 500,
        "y": 500,
        "score": 0.99,
        "semantic": "Mocked matching grid cell",
        "source": "vlm_grid",
    }
    mock.find_best_node.return_value = {"x": 500, "y": 500, "semantic_string": "dummy node"}
    return mock


@pytest.fixture
def mock_logger():
    return logging.getLogger("test")


@pytest.fixture
def device(request):
    if request.config.getoption("--live"):
        import os

        import yaml

        from GramAddict.core.device_facade import create_device

        device_id = "emulator-5554"
        app_id = "com.instagram.android"

        config_path = "test_config.yml"
        if os.path.exists(config_path):
            try:
                with open(config_path, "r", encoding="utf-8") as f:
                    config = yaml.safe_load(f)
                    if config:
                        device_id = config.get("device", device_id)
                        app_id = config.get("app-id", app_id)
            except Exception as e:
                print(f"⚠️ Warning: Could not load {config_path}: {e}")

        print(f"🚀 Connecting to live device: {device_id} (App: {app_id})")
        return create_device(device_id, app_id)
    return create_mock_device()


@pytest.fixture(autouse=True)
def reset_singletons():
    """Ensure all core engine singletons are fresh for each test."""
    from GramAddict.core.behaviors import PluginRegistry
    from GramAddict.core.goap import GoalExecutor
    from GramAddict.core.physics.biomechanics import PhysicsBody
    from GramAddict.core.physics.sendevent_injector import SendEventInjector
    from GramAddict.core.qdrant_memory import QdrantBase
    from GramAddict.core.situational_awareness import SituationalAwarenessEngine
    from GramAddict.core.telepathic_engine import TelepathicEngine

    TelepathicEngine.reset()
    GoalExecutor.reset()
    SituationalAwarenessEngine.reset()
    PluginRegistry.reset()
    PhysicsBody.reset()
    SendEventInjector.reset()

    QdrantBase._connection_failed_logged = False

    from GramAddict.core.dojo_engine import DojoEngine

    if hasattr(DojoEngine, "reset"):
        DojoEngine.reset()
    else:
        DojoEngine._instance = None

    # Aggressively wipe on-disk session files to prevent state leakage in tests
    for f in [
        "telepathic_memory.json",
        "telepathic_blacklist.json",
        "growth_brain_memory.json",
        "gramaddict_nav_map.json",
        "l2_channels_cache.json",
    ]:
        if os.path.exists(f):
            try:
                os.remove(f)
            except Exception:
                pass
    yield

    # Post-test cleanup
    PhysicsBody.reset()
    SendEventInjector.reset()


@pytest.fixture(autouse=True)
def telepathic_mock(monkeypatch, request):
    if request.config.getoption("--live") or "e2e" in str(request.node.fspath):
        # TelepathicEngine is a singleton, allow it to run natively in e2e or live mode
        return None
    import GramAddict.core.telepathic_engine

    engine = create_mock_telepathic_engine()
    monkeypatch.setattr(GramAddict.core.telepathic_engine.TelepathicEngine, "get_instance", lambda: engine)
    return engine


@pytest.fixture
def mock_cognitive_stack():
    stack = {
        "dopamine": MagicMock(),
        "darwin": MagicMock(),
        "resonance": MagicMock(),
        "active_inference": MagicMock(),
        "growth_brain": MagicMock(),
        "swarm": MagicMock(),
        "radome": MagicMock(),
        "nav_graph": MagicMock(),
        "zero_engine": MagicMock(),
        "crm": MagicMock(),
        "telepathic": create_mock_telepathic_engine(),
    }
    stack["radome"].sanitize_xml.side_effect = lambda x: x
    return stack
