from __future__ import annotations

import textwrap

import pytest

from jarvis_operas import (
    OperatorLoadError,
    get_global_registry,
    load_user_ops,
    refresh_register_dicts,
    register_dicts,
)
from jarvis_operas.registry import OperatorRegistry


def test_load_user_ops_with_decorator(tmp_path) -> None:
    op_file = tmp_path / "decor_ops.py"
    op_file.write_text(
        textwrap.dedent(
            """
            from jarvis_operas import oper

            @oper("double", tag="demo")
            def double(x):
                return x * 2
            """
        ),
        encoding="utf-8",
    )

    registry = OperatorRegistry()
    loaded = load_user_ops(str(op_file), registry)

    assert loaded == ["decor_ops.double"]
    assert registry.call("decor_ops.double", x=6) == 12
    assert registry.info("decor_ops.double")["metadata"]["tag"] == "demo"


def test_load_user_ops_with_export_whitelist(tmp_path) -> None:
    op_file = tmp_path / "whitelist_ops.py"
    op_file.write_text(
        textwrap.dedent(
            """
            def triple(x):
                return x * 3

            __JARVIS_OPERAS__ = {
                "triple": triple,
            }
            """
        ),
        encoding="utf-8",
    )

    registry = OperatorRegistry()
    loaded = load_user_ops(str(op_file), registry)

    assert loaded == ["whitelist_ops.triple"]
    assert registry.call("whitelist_ops.triple", x=5) == 15


def test_load_user_ops_wraps_import_errors(tmp_path) -> None:
    op_file = tmp_path / "broken_ops.py"
    op_file.write_text("def broken(:\n", encoding="utf-8")

    registry = OperatorRegistry()

    with pytest.raises(OperatorLoadError) as exc:
        load_user_ops(str(op_file), registry)

    assert "Failed to load operators" in str(exc.value)
    assert exc.value.__cause__ is not None


def test_load_user_ops_refreshes_register_dicts_for_global_registry(tmp_path) -> None:
    op_file = tmp_path / "refresh_ops.py"
    op_file.write_text(
        textwrap.dedent(
            """
            def plus_seven(x):
                return x + 7

            __JARVIS_OPERAS__ = {
                "plus_seven": plus_seven,
            }
            """
        ),
        encoding="utf-8",
    )

    namespace = "autorefresh_ops"
    full_name = f"{namespace}.plus_seven"
    registry = get_global_registry()
    registry.delete_namespace(namespace)
    refresh_register_dicts()
    assert full_name not in register_dicts

    try:
        loaded = load_user_ops(str(op_file), registry, namespace=namespace)
        assert full_name in loaded
        assert full_name in register_dicts
    finally:
        registry.delete_namespace(namespace)
        refresh_register_dicts()
