from __future__ import annotations

import os
import pickle
import sys

import pytest


@pytest.mark.skipif(
    sys.platform.startswith("emscripten"), reason="Requires loadable modules"
)
def test_independent_subinterpreters():
    """Makes sure the internals object differs across independent subinterpreters"""

    sys.path.append(".")

    # This is supposed to be added in 3.14.0b3
    if sys.version_info >= (3, 15):
        import interpreters
    elif sys.version_info >= (3, 13):
        import _interpreters as interpreters
    elif sys.version_info >= (3, 12):
        import _xxsubinterpreters as interpreters
    else:
        pytest.skip("Test requires the interpreters stdlib module")

    m = pytest.importorskip("mod_per_interpreter_gil")

    if not m.defined_PYBIND11_HAS_SUBINTERPRETER_SUPPORT:
        pytest.skip("Does not have subinterpreter support compiled in")

    code = """
import mod_per_interpreter_gil as m
import pickle
with open(pipeo, 'wb') as f:
    pickle.dump(m.internals_at(), f)
"""

    interp1 = interpreters.create()
    interp2 = interpreters.create()
    try:
        try:
            res0 = interpreters.run_string(interp1, "import mod_shared_interpreter_gil")
            if res0 is not None:
                res0 = res0.msg
        except Exception as e:
            res0 = str(e)

        pipei, pipeo = os.pipe()
        interpreters.run_string(interp1, code, shared={"pipeo": pipeo})
        with open(pipei, "rb") as f:
            res1 = pickle.load(f)

        pipei, pipeo = os.pipe()
        interpreters.run_string(interp2, code, shared={"pipeo": pipeo})
        with open(pipei, "rb") as f:
            res2 = pickle.load(f)

        # do this while the two interpreters are active
        import mod_per_interpreter_gil as m2

        assert m.internals_at() == m2.internals_at(), (
            "internals should be the same within the main interpreter"
        )
    finally:
        interpreters.destroy(interp1)
        interpreters.destroy(interp2)

    assert "does not support loading in subinterpreters" in res0, (
        "cannot use shared_gil in a default subinterpreter"
    )
    assert res1 != m.internals_at(), "internals should differ from main interpreter"
    assert res2 != m.internals_at(), "internals should differ from main interpreter"
    assert res1 != res2, "internals should differ between interpreters"

    # do this after the two interpreters are destroyed and only one remains
    import mod_per_interpreter_gil as m3

    assert m.internals_at() == m3.internals_at(), (
        "internals should be the same within the main interpreter"
    )


@pytest.mark.skipif(
    sys.platform.startswith("emscripten"), reason="Requires loadable modules"
)
def test_dependent_subinterpreters():
    """Makes sure the internals object differs across subinterpreters"""

    sys.path.append(".")

    if sys.version_info >= (3, 15):
        import interpreters
    elif sys.version_info >= (3, 13):
        import _interpreters as interpreters
    elif sys.version_info >= (3, 12):
        import _xxsubinterpreters as interpreters
    else:
        pytest.skip("Test requires the interpreters stdlib module")

    m = pytest.importorskip("mod_shared_interpreter_gil")

    if not m.defined_PYBIND11_HAS_SUBINTERPRETER_SUPPORT:
        pytest.skip("Does not have subinterpreter support compiled in")

    code = """
import mod_shared_interpreter_gil as m
import pickle
with open(pipeo, 'wb') as f:
    pickle.dump(m.internals_at(), f)
"""

    try:
        interp1 = interpreters.create("legacy")
    except TypeError:
        pytest.skip("interpreters module needs to support legacy config")

    try:
        pipei, pipeo = os.pipe()
        interpreters.run_string(interp1, code, shared={"pipeo": pipeo})
        with open(pipei, "rb") as f:
            res1 = pickle.load(f)

        # do this while the other interpreter is active
        import mod_shared_interpreter_gil as m2

        assert m.internals_at() == m2.internals_at(), (
            "internals should be the same within the main interpreter"
        )
    finally:
        interpreters.destroy(interp1)

    assert res1 != m.internals_at(), "internals should differ from main interpreter"

    # do this after the other interpreters are destroyed and only one remains
    import mod_shared_interpreter_gil as m3

    assert m.internals_at() == m3.internals_at(), (
        "internals should be the same within the main interpreter"
    )
