from __future__ import annotations

from pathlib import Path

import pytest

import workspace


def test_a_plain_project_directory_is_accepted(tmp_path: Path) -> None:
    project = tmp_path / "my-project"
    project.mkdir()
    assert workspace.resolve_workspace(str(project)) == project.resolve()


def test_home_is_refused() -> None:
    with pytest.raises(ValueError, match="single project directory"):
        workspace.resolve_workspace(str(Path.home()))


def test_filesystem_root_is_refused() -> None:
    with pytest.raises(ValueError, match="single project directory"):
        workspace.resolve_workspace("/")


def test_an_ancestor_of_home_is_refused() -> None:
    with pytest.raises(ValueError, match="single project directory"):
        workspace.resolve_workspace(str(Path.home().parent))


@pytest.mark.parametrize("name", [".ssh", ".claude", ".codex"])
def test_credential_directories_are_refused(name: str) -> None:
    protected = Path.home() / name
    if not protected.is_dir():
        pytest.skip(f"{protected} does not exist on this machine")
    with pytest.raises(ValueError, match="credentials or agent state"):
        workspace.resolve_workspace(str(protected))


SYSTRAY_CREDENTIAL_STORE = Path.home() / ".local" / "state" / "overdeck" / "systray" / "runtime"


def test_the_systray_credential_store_sits_inside_a_protected_root() -> None:
    protected = workspace._protected_paths()
    assert any(workspace._is_within(SYSTRAY_CREDENTIAL_STORE, path) for path in protected)


def test_the_overdeck_state_root_is_protected() -> None:
    assert Path.home() / ".local" / "state" / "overdeck" in workspace.PROTECTED


def test_a_directory_containing_a_protected_tree_is_refused(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    secrets = tmp_path / "project" / ".ssh"
    secrets.mkdir(parents=True)
    monkeypatch.setattr(workspace, "PROTECTED", (secrets,))
    with pytest.raises(ValueError, match="it contains"):
        workspace.resolve_workspace(str(tmp_path / "project"))


def test_a_missing_directory_is_refused(tmp_path: Path) -> None:
    with pytest.raises(ValueError, match="not a directory"):
        workspace.resolve_workspace(str(tmp_path / "absent"))


def test_a_toolchain_directory_is_an_acceptable_readable_root(tmp_path: Path) -> None:
    toolchain = tmp_path / ".nvm"
    toolchain.mkdir()
    assert workspace.resolve_readable_root(str(toolchain)) == toolchain.resolve()


def test_home_is_refused_as_a_readable_root() -> None:
    """`--readable-root ~` would mount every credential directory read-only
    inside the jail, which is the whole point of confining reads."""
    with pytest.raises(ValueError, match="refusing to expose"):
        workspace.resolve_readable_root(str(Path.home()))


@pytest.mark.parametrize("name", [".ssh", ".claude", ".codex"])
def test_credential_directories_are_refused_as_readable_roots(name: str) -> None:
    protected = Path.home() / name
    if not protected.is_dir():
        pytest.skip(f"{protected} does not exist on this machine")
    with pytest.raises(ValueError, match="credentials or agent state"):
        workspace.resolve_readable_root(str(protected))


def test_cwd_defaults_to_the_workspace_root(tmp_path: Path) -> None:
    assert workspace.resolve_cwd(tmp_path, None) == tmp_path


def test_relative_cwd_resolves_inside_the_workspace(tmp_path: Path) -> None:
    (tmp_path / "src").mkdir()
    assert workspace.resolve_cwd(tmp_path, "src") == tmp_path / "src"


def test_absolute_cwd_outside_the_workspace_is_refused(tmp_path: Path) -> None:
    with pytest.raises(ValueError, match="escapes the workspace"):
        workspace.resolve_cwd(tmp_path, "/etc")


def test_dotdot_cwd_is_refused(tmp_path: Path) -> None:
    (tmp_path / "src").mkdir()
    with pytest.raises(ValueError, match="escapes the workspace"):
        workspace.resolve_cwd(tmp_path, "src/../..")


def test_a_symlink_out_of_the_workspace_is_refused(tmp_path: Path) -> None:
    outside = tmp_path.parent / "outside-target"
    outside.mkdir(exist_ok=True)
    link = tmp_path / "escape"
    link.symlink_to(outside)
    with pytest.raises(ValueError, match="escapes the workspace"):
        workspace.resolve_cwd(tmp_path, "escape")
