"""
Tests for thread lock serialization and import detection.
Ensures verlex objects can be pickled without 'cannot serialize a thread lock' errors.
"""

import pickle
import threading

import cloudpickle
import pytest


class TestThreadLockSerialization:

    def test_lock_pickle(self):
        import verlex
        lock = threading.Lock()
        data = cloudpickle.dumps(lock)
        restored = pickle.loads(data)
        assert not restored.locked()

    def test_locked_lock_pickle(self):
        import verlex
        lock = threading.Lock()
        lock.acquire()
        data = cloudpickle.dumps(lock)
        restored = pickle.loads(data)
        assert restored.locked()

    def test_rlock_pickle(self):
        import verlex
        rlock = threading.RLock()
        data = cloudpickle.dumps(rlock)
        restored = pickle.loads(data)
        assert restored.acquire(blocking=False)
        restored.release()

    def test_event_pickle(self):
        import verlex
        event = threading.Event()
        event.set()
        data = cloudpickle.dumps(event)
        restored = pickle.loads(data)
        assert restored.is_set()

    def test_condition_pickle(self):
        import verlex
        cond = threading.Condition()
        data = cloudpickle.dumps(cond)
        restored = pickle.loads(data)
        assert restored.acquire(blocking=False)
        restored.release()

    def test_semaphore_pickle(self):
        import verlex
        sem = threading.Semaphore(3)
        data = cloudpickle.dumps(sem)
        restored = pickle.loads(data)
        assert restored.acquire(blocking=False)

    def test_gateway_pickle(self):
        from verlex.client import GateWay
        gw = GateWay.__new__(GateWay)
        gw._api_key = "gw_test_fake"
        gw._base_url = "https://example.com"
        gw._jobs = []
        gw._jobs_lock = threading.Lock()
        gw._client = None
        gw._session = None
        gw.fast = False
        gw.verbose = False
        gw.default_timeout = 300
        gw._warm_session_id = None
        gw._offloader = None

        data = cloudpickle.dumps(gw)
        restored = pickle.loads(data)
        assert restored._api_key == "gw_test_fake"
        assert hasattr(restored, "_jobs_lock")

    def test_function_with_lock_in_closure(self):
        import verlex
        lock = threading.Lock()

        def fn():
            with lock:
                return 42

        data = cloudpickle.dumps(fn)
        restored = pickle.loads(data)
        assert restored() == 42

    def test_function_with_event_in_closure(self):
        import verlex
        event = threading.Event()

        def fn():
            return event.is_set()

        data = cloudpickle.dumps(fn)
        restored = pickle.loads(data)
        assert restored() is False


class TestImportDetection:

    def test_detect_numpy_from_source(self):
        from verlex.client import GateWay

        def uses_numpy():
            import numpy as np
            return np.array([1, 2, 3])

        import inspect
        import textwrap
        source = textwrap.dedent(inspect.getsource(uses_numpy))
        imports = GateWay._get_func_imports(uses_numpy, source)
        assert "numpy" in imports

    def test_detect_globals_import(self):
        from verlex.client import GateWay

        def uses_threading():
            return threading.current_thread().name

        import inspect
        import textwrap
        source = textwrap.dedent(inspect.getsource(uses_threading))
        imports = GateWay._get_func_imports(uses_threading, source)
        assert "threading" in imports

    def test_stdlib_filtered(self):
        from verlex.client import GateWay

        def uses_os():
            import os
            return os.getcwd()

        import inspect
        import textwrap
        source = textwrap.dedent(inspect.getsource(uses_os))
        packages = GateWay._detect_local_packages(uses_os, source)
        assert not any(p.startswith("os==") for p in packages)

    def test_module_to_package_mapping(self):
        from verlex.client import GateWay
        assert GateWay._MODULE_TO_PACKAGE["cv2"] == "opencv-python"
        assert GateWay._MODULE_TO_PACKAGE["PIL"] == "Pillow"
        assert GateWay._MODULE_TO_PACKAGE["sklearn"] == "scikit-learn"
        assert GateWay._MODULE_TO_PACKAGE["yaml"] == "PyYAML"

    def test_detect_arg_packages_numpy(self):
        from verlex.client import GateWay
        try:
            import numpy as np
            arr = np.array([1, 2, 3])
            packages = GateWay._detect_arg_packages((arr,), {})
            assert any("numpy" in p for p in packages)
        except ImportError:
            pytest.skip("numpy not installed")

    def test_detect_arg_packages_nested(self):
        from verlex.client import GateWay
        try:
            import numpy as np
            data = [np.array([1]), np.array([2])]
            packages = GateWay._detect_arg_packages((data,), {})
            assert any("numpy" in p for p in packages)
        except ImportError:
            pytest.skip("numpy not installed")

    def test_detect_arg_packages_builtin_only(self):
        from verlex.client import GateWay
        packages = GateWay._detect_arg_packages((1, "hello", [1, 2]), {})
        assert packages == []
