"""Conftest.py (root-level).

We keep this in root pytest fixtures in pytest's doctest plugin to be available, as well
as avoiding conftest.py from being included in the wheel, in addition to pytest_plugin
for pytester only being available via the root directory.

See "pytest_plugins in non-top-level conftest files" in
https://docs.pytest.org/en/stable/deprecations.html
"""

from __future__ import annotations

import logging
import os
import pathlib
import shutil
import typing as t

import pytest
from _pytest.doctest import DoctestItem
from libtmux.test.random import namer

from tests.fixtures import utils as test_utils
from tmuxp.workspace.finders import get_workspace_dir

if t.TYPE_CHECKING:
    from libtmux.session import Session

logger = logging.getLogger(__name__)
USING_ZSH = "zsh" in os.getenv("SHELL", "")


@pytest.fixture(autouse=True)
def _pin_test_shell_env(monkeypatch: pytest.MonkeyPatch) -> None:
    """Pin ``$SHELL`` to ``/bin/sh`` for every test.

    tmux falls back to ``$SHELL`` for new panes when ``default-shell`` is
    unset, so pinning the env var here forces every test pane to spawn
    ``/bin/sh`` instead of the contributor's interactive shell. Eliminates
    rcfile init cost (zsh / oh-my-zsh / bash profile chains) and yields
    deterministic prompt output that doesn't flake capture-pane assertions.
    ``monkeypatch`` restores the prior value at teardown.
    """
    monkeypatch.setenv("SHELL", "/bin/sh")


@pytest.fixture(autouse=USING_ZSH, scope="session")
def zshrc(user_path: pathlib.Path) -> pathlib.Path | None:
    """Quiets ZSH default message.

    Needs a startup file .zshenv, .zprofile, .zshrc, .zlogin.
    """
    if not USING_ZSH:
        return None
    p = user_path / ".zshrc"
    p.touch()
    return p


@pytest.fixture(autouse=True)
def home_path_default(monkeypatch: pytest.MonkeyPatch, user_path: pathlib.Path) -> None:
    """Set HOME to user_path (random, temporary directory)."""
    monkeypatch.setenv("HOME", str(user_path))


@pytest.fixture
def tmuxp_configdir(user_path: pathlib.Path) -> pathlib.Path:
    """Ensure and return tmuxp config directory."""
    xdg_config_dir = user_path / ".config"
    xdg_config_dir.mkdir(exist_ok=True)

    tmuxp_configdir = xdg_config_dir / "tmuxp"
    tmuxp_configdir.mkdir(exist_ok=True)
    return tmuxp_configdir


@pytest.fixture
def tmuxp_configdir_default(
    monkeypatch: pytest.MonkeyPatch,
    tmuxp_configdir: pathlib.Path,
) -> None:
    """Set tmuxp configuration directory for ``TMUXP_CONFIGDIR``."""
    monkeypatch.setenv("TMUXP_CONFIGDIR", str(tmuxp_configdir))
    assert get_workspace_dir() == str(tmuxp_configdir)


@pytest.fixture
def monkeypatch_plugin_test_packages(monkeypatch: pytest.MonkeyPatch) -> None:
    """Monkeypatch tmuxp plugin fixtures to python path."""
    paths = [
        "tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bwb/",
        "tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bs/",
        "tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_r/",
        "tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_owc/",
        "tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_awf/",
        "tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_fail/",
    ]
    for path in paths:
        monkeypatch.syspath_prepend(str(pathlib.Path(path).resolve()))


@pytest.fixture
def session_params(session_params: dict[str, t.Any]) -> dict[str, t.Any]:
    """Terminal-friendly tmuxp session_params for dimensions."""
    session_params.update({"x": 800, "y": 600})
    return session_params


@pytest.fixture
def socket_name(request: pytest.FixtureRequest) -> str:
    """Random socket name for tmuxp."""
    return f"tmuxp_test{next(namer)}"


# Modules that actually need tmux fixtures in their doctests
DOCTEST_NEEDS_TMUX = {
    "tmuxp.workspace.builder.classic",
}


def pytest_collection_modifyitems(items: list[pytest.Item]) -> None:
    """Add rerun markers to tmux-dependent doctests for flaky shell timing."""
    for item in items:
        if isinstance(item, DoctestItem):
            module_name = item.dtest.globs.get("__name__", "")
            if module_name in DOCTEST_NEEDS_TMUX:
                item.add_marker(pytest.mark.flaky(reruns=2))


@pytest.fixture(autouse=True)
def add_doctest_fixtures(
    request: pytest.FixtureRequest,
    doctest_namespace: dict[str, t.Any],
    tmp_path: pathlib.Path,
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    """Harness pytest fixtures to doctests namespace."""
    if isinstance(request._pyfuncitem, DoctestItem):
        # Always provide lightweight fixtures
        doctest_namespace["test_utils"] = test_utils
        doctest_namespace["tmp_path"] = tmp_path
        doctest_namespace["monkeypatch"] = monkeypatch

        # Only load expensive tmux fixtures for modules that need them
        module_name = request._pyfuncitem.dtest.globs.get("__name__", "")
        if module_name in DOCTEST_NEEDS_TMUX and shutil.which("tmux"):
            doctest_namespace["server"] = request.getfixturevalue("server")
            session: Session = request.getfixturevalue("session")
            doctest_namespace["session"] = session
            doctest_namespace["window"] = session.active_window
            doctest_namespace["pane"] = session.active_pane
