import pytest

from typing import Annotated
from types import ModuleType

# from unittest.mock import MagicMock, patch

from kaya_module_sdk.src.module.template import Module, Config
from kaya_module_sdk.src.module.arguments import Args
from kaya_module_sdk.src.module.returns import Rets
from kaya_module_sdk.src.utils.generators.methods import kaya_io
from kaya_module_sdk.src.utils.metadata.abstract_metadata import KMetadata
from kaya_module_sdk.src.utils.metadata.abstract_validation import KValidation

# DUMMY DATA


class DummyResource:
    """Dummy resource classes to simulate importlib.resources behavior."""

    def __init__(self, file_path):
        self.dummy_path = file_path

    def joinpath(self, file_name):
        # For testing we ignore file_name and simply return self.
        return self


class DummyContextManager:
    def __init__(self, file_path):
        self.file_path = file_path

    def __enter__(self):
        return self.file_path

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass


class ArgsType(Args):
    key1: str
    key2: str

    def set_key1(self, value):
        pass

    def set_key2(self, value):
        pass


class RetsType(Rets):
    key1: str
    key2: str

    def __init__(self, *args, **kwargs):
        pass


@kaya_io()
class DecoratedArgsType(Args):
    _field1: Annotated[int, "metadata"]
    _field2: Annotated[str, "metadata"]


class DummyConfig(Config):
    def __init__(self, name="dummy", version="1.0"):
        self.name = name
        self.version = version
        self._mandatory = []  # NOTE: For simplicity, no mandatory fields


class TestModule(Module):
    def __init__(self, manifest=None, config=None):
        if manifest:
            self.manifest = manifest
        #       if config:
        self.config = config or DummyConfig()
        # mod1 has a valid manifest; mod2 is missing a manifest.
        self.modules = {
            "mod1": DummyModule(
                manifest={
                    "inputs": [{"label": "a", "validations": ["rule1"]}],
                    "outputs": [],
                },
                config=DummyConfig(),
            ),
            "mod2": DummyModule(manifest=None, config=DummyConfig()),
        }

    def __repr__(self):
        return "DummyModule"

    def main(self, args) -> Rets:
        return Rets()  # NOTE: Simulating main method behavior for testing


class DummyModule:
    def __init__(self, manifest, config=None):
        self.manifest = manifest
        self.config = config or DummyConfig()

    def __repr__(self):
        return "DummyModule"


class KMetadataSubclass(KMetadata):
    def __init__(self, data):
        self._data = data

    def get_data(self):
        return self._data

    def __str__(self) -> str:
        return f"KMetadata: {self._data}"


class KValidationSubclass(KValidation):
    def __init__(self, data):
        self._data = data

    def get_data(self):
        return self._data

    def __str__(self) -> str:
        return f"KValidation: {self._data}"


class DummyKVLE:
    """Dummy implementations for KVLE to isolate KVL tests."""

    def __init__(self, **context):
        self.context = context

    def check_rules(self):
        # Return a dummy rules result
        return {"dummy_rule": "passed"}

    def check_meta(self, module_package):
        # Return a dummy metadata result
        return {"dummy_meta": "passed"}

    def check_source(self, python_files):
        # Return a dummy source result
        return {"dummy_source": "passed"}


class DummyKVLR:
    """Dummy implementations for KVLR to isolate KVL tests."""

    def __init__(self, **context):
        self.context = context

    def generate_report(self, data, dump=False):
        return {"report": data}


class DummyKVLEForSuccess:
    def check_source(self, python_files):
        # Return a dummy "success" result.
        # Format: a dict with keys "ok" and "nok".
        file_key = list(python_files.keys())[0] if python_files else "unknown"
        return {"ok": [{"path": file_key, "errors": [], "result": "OK"}], "nok": []}


class DummyKVLRForSuccess:
    def generate_report(self, data, dump=False):
        # Simply wrap the provided data in a dict under "report".
        return {"report": data}


class DummyKVLEWithSourceError:
    def check_source(self, python_files):
        # Return a dummy error structure:
        file_key = list(python_files.keys())[0] if python_files else "unknown"
        return {
            "ok": [],
            "nok": [{"path": file_key, "errors": ["dummy error"], "result": "NOK"}],
        }


class DummyKVLRForErrors:
    def generate_report(self, data, dump=False):
        return {"report": data}


class DummyConstraint:
    """Dummy constraint class for testing load_constraint_rules"""

    def __init__(self, a, b=None):
        self._data = {"dummy": "value"}

    def load(self, rule):
        # NOTE: For testing purposes, we ignore the rule and assume successful load.
        pass


class DummyModuleInstance:
    def __init__(self):
        self.config = DummyConfig()
        # NOTE: mod1 has a valid manifest; mod2 is missing a manifest.
        self.modules = {
            "mod1": DummyModule(
                manifest={
                    "inputs": [{"label": "a", "validations": ["rule1"]}],
                    "outputs": [],
                },
                config=DummyConfig(),
            ),
            "mod2": DummyModule(manifest=None, config=DummyConfig()),
        }


# UTILS


def fake_import_module(name):
    """Fake import_module for module package loading."""
    # NOTE: When KVL.load_module_package calls import_module,
    #   we expect a module with a "KayaStrategyModule" attribute.
    if name == "dummy_module.module":
        dummy_mod = ModuleType("dummy_module.module")

        class DummyStrategyModule:
            def __init__(self):
                self.config = type("Config", (), {"name": "dummy", "version": "1.0"})

        setattr(dummy_mod, "KayaStrategyModule", DummyStrategyModule)
        return dummy_mod
    raise ModuleNotFoundError("Module not found: " + name)


def patch_metadata_classes_kvle(monkeypatch):
    """
    Patch all metadata constraint class names used in KVLE.load_constraint_rules
    to our DummyConstraint.
    """
    # NOTE: Import the module where KVLE is defined.
    import kaya_module_sdk.src.testing.kvl_executer as kvle_mod

    names = [
        "EQ",
        "GT",
        "GTE",
        "LT",
        "LTE",
        "MaxLen",
        "Max",
        "MinLen",
        "Min",
        "ValueRange",
        "EQLen",
        "Const",
        "NotConst",
        "Order",
    ]
    for name in names:
        monkeypatch.setattr(kvle_mod, name, DummyConstraint)


@pytest.fixture
def dummy_resource():
    return DummyResource


@pytest.fixture
def dummy_context_manager():
    return DummyContextManager


@pytest.fixture
def dummy_kvle():
    return DummyKVLE


@pytest.fixture
def dummy_kvlr():
    return DummyKVLR


@pytest.fixture
def dummy_kvle_ok():
    return DummyKVLEForSuccess


@pytest.fixture
def dummy_kvlr_ok():
    return DummyKVLRForSuccess


@pytest.fixture
def dummy_kvle_source_nok():
    return DummyKVLEWithSourceError


@pytest.fixture
def dummy_kvlr_nok():
    return DummyKVLRForErrors


@pytest.fixture
def dummy_config():
    return DummyConfig


@pytest.fixture
def dummy_constraint():
    return DummyConstraint


@pytest.fixture
def patch_meta_kvle():
    return patch_metadata_classes_kvle


@pytest.fixture
def import_module():
    return fake_import_module


@pytest.fixture
def mock_module():
    return TestModule()


@pytest.fixture
def mock_module_instance():
    return DummyModuleInstance()


@pytest.fixture
def kaya_io_decorated_args_type(request):
    instance = DecoratedArgsType()
    instance.set_field1(request.param.get("_field1"))
    instance.set_field2(request.param.get("_field2"))
    return instance


@pytest.fixture
def args_type():
    return ArgsType()


@pytest.fixture
def rets_type_obj():
    return RetsType()


@pytest.fixture
def rets_type_cls():
    return RetsType


@pytest.fixture
def kmetadata_subclass(request):
    instance = KMetadataSubclass(data=request.param.get("_data"))
    return instance


@pytest.fixture
def kvalidation_subclass(request):
    instance = KValidationSubclass(data=request.param.get("_data"))
    return instance


# CODE DUMP
