from __future__ import annotations

from pathlib import Path

import pytest

from vibe.core.config.layer import UntrustedLayerError
from vibe.core.config.layers.project import ProjectConfigLayer
from vibe.core.config.types import MISSING_CONFIG_FILE_FINGERPRINT
from vibe.core.paths._vibe_home import GlobalPath
from vibe.core.trusted_folders import trusted_folders_manager


@pytest.mark.asyncio
async def test_reads_toml_when_trusted(tmp_working_directory: Path) -> None:
    trusted_folders_manager.add_trusted(tmp_working_directory)
    config_path = tmp_working_directory / ".vibe" / "config.toml"
    config_path.parent.mkdir(parents=True, exist_ok=True)
    config_path.write_text('active_model = "project-model"\n')

    layer = ProjectConfigLayer(path=tmp_working_directory)
    data = await layer.load()
    assert data.model_extra == {"active_model": "project-model"}
    fp1 = layer.fingerprint
    assert isinstance(fp1, str)
    assert fp1

    config_path.unlink()
    data = await layer.load(force=True)
    assert data.model_extra == {}
    assert layer.fingerprint == MISSING_CONFIG_FILE_FINGERPRINT


@pytest.mark.asyncio
async def test_untrusted_raises(tmp_working_directory: Path) -> None:
    trusted_folders_manager.add_untrusted(tmp_working_directory)
    config_path = tmp_working_directory / ".vibe" / "config.toml"
    config_path.parent.mkdir(parents=True, exist_ok=True)
    config_path.write_text('active_model = "project-model"\n')

    layer = ProjectConfigLayer(path=tmp_working_directory)
    with pytest.raises(UntrustedLayerError):
        await layer.load()


@pytest.mark.asyncio
async def test_unresolved_trust_defaults_to_untrusted(
    tmp_working_directory: Path,
) -> None:
    config_path = tmp_working_directory / ".vibe" / "config.toml"
    config_path.parent.mkdir(parents=True, exist_ok=True)
    config_path.write_text('key = "value"\n')

    layer = ProjectConfigLayer(path=tmp_working_directory)
    with pytest.raises(UntrustedLayerError):
        await layer.load()


@pytest.mark.asyncio
async def test_missing_file_returns_empty(tmp_working_directory: Path) -> None:
    trusted_folders_manager.add_trusted(tmp_working_directory)

    layer = ProjectConfigLayer(path=tmp_working_directory)
    data = await layer.load()
    assert data.model_extra == {}
    assert layer.fingerprint == MISSING_CONFIG_FILE_FINGERPRINT


@pytest.mark.asyncio
async def test_default_name(tmp_working_directory: Path) -> None:
    layer = ProjectConfigLayer(path=tmp_working_directory)
    assert layer.name == "project-toml"


@pytest.mark.asyncio
async def test_trust_uses_path_parent_for_resolution(
    tmp_working_directory: Path,
) -> None:
    project_dir = tmp_working_directory / "my-project"
    project_dir.mkdir()

    config_path = project_dir / ".vibe" / "config.toml"
    config_path.parent.mkdir(parents=True, exist_ok=True)
    config_path.write_text('value = "trusted"\n')
    trusted_folders_manager.add_trusted(project_dir / ".vibe")

    layer = ProjectConfigLayer(path=project_dir)
    data = await layer.load()
    assert data.model_extra == {"value": "trusted"}


@pytest.mark.asyncio
async def test_grant_trust_marks_folder_trusted(tmp_working_directory: Path) -> None:
    config_path = tmp_working_directory / ".vibe" / "config.toml"
    config_path.parent.mkdir(parents=True, exist_ok=True)
    config_path.write_text('key = "value"\n')
    layer = ProjectConfigLayer(path=tmp_working_directory)
    trusted_folders_manager.add_untrusted(tmp_working_directory / ".vibe")

    await layer.resolve_trust()
    await layer.grant_trust()
    assert trusted_folders_manager.is_trusted(tmp_working_directory / ".vibe") is True


@pytest.mark.asyncio
async def test_revoke_trust_marks_folder_untrusted(tmp_working_directory: Path) -> None:
    config_path = tmp_working_directory / ".vibe" / "config.toml"
    config_path.parent.mkdir(parents=True, exist_ok=True)
    config_path.write_text('key = "value"\n')
    layer = ProjectConfigLayer(path=tmp_working_directory)
    trusted_folders_manager.add_trusted(tmp_working_directory / ".vibe")

    await layer.resolve_trust()
    await layer.revoke_trust()
    assert trusted_folders_manager.is_trusted(tmp_working_directory / ".vibe") is False


@pytest.mark.asyncio
async def test_grant_trust_without_config_file_is_noop(
    tmp_working_directory: Path,
) -> None:
    layer = ProjectConfigLayer(path=tmp_working_directory)
    trusted_folders_manager.add_untrusted(tmp_working_directory)

    await layer.grant_trust()
    assert trusted_folders_manager.is_trusted(tmp_working_directory) is False


@pytest.mark.asyncio
async def test_revoke_trust_without_config_file_is_noop(
    tmp_working_directory: Path,
) -> None:
    layer = ProjectConfigLayer(path=tmp_working_directory)
    trusted_folders_manager.add_trusted(tmp_working_directory)

    await layer.revoke_trust()
    assert trusted_folders_manager.is_trusted(tmp_working_directory) is True


@pytest.mark.asyncio
async def test_trust_stored_at_vibe_subdir(tmp_working_directory: Path) -> None:
    config_path = tmp_working_directory / ".vibe" / "config.toml"
    config_path.parent.mkdir(parents=True, exist_ok=True)
    config_path.write_text('key = "value"\n')
    layer = ProjectConfigLayer(path=tmp_working_directory)

    await layer.resolve_trust()
    await layer.grant_trust()

    assert (
        str((tmp_working_directory / ".vibe").resolve())
        in trusted_folders_manager._trusted
    )
    assert str(tmp_working_directory.resolve()) not in trusted_folders_manager._trusted


@pytest.mark.asyncio
async def test_find_file_prefers_closest_config(tmp_working_directory: Path) -> None:
    parent_config = tmp_working_directory / ".vibe" / "config.toml"
    parent_config.parent.mkdir(parents=True, exist_ok=True)
    parent_config.write_text('level = "parent"\n')

    child = tmp_working_directory / "child"
    child.mkdir()
    child_config = child / ".vibe" / "config.toml"
    child_config.parent.mkdir(parents=True, exist_ok=True)
    child_config.write_text('level = "child"\n')

    trusted_folders_manager.add_trusted(child)
    layer = ProjectConfigLayer(path=child)
    data = await layer.load()
    assert data.model_extra == {"level": "child"}


@pytest.mark.asyncio
async def test_find_file_result_is_cached(tmp_working_directory: Path) -> None:
    config_path = tmp_working_directory / ".vibe" / "config.toml"
    config_path.parent.mkdir(parents=True, exist_ok=True)
    config_path.write_text("x = 1\n")
    trusted_folders_manager.add_trusted(tmp_working_directory)

    layer = ProjectConfigLayer(path=tmp_working_directory)
    await layer.load()

    assert layer._is_set is True
    cached_path = layer._config_file_path
    await layer.resolve_trust()
    assert layer._config_file_path is cached_path


@pytest.mark.asyncio
async def test_config_file_added_after_first_search_is_not_detected(
    tmp_working_directory: Path,
) -> None:
    layer = ProjectConfigLayer(path=tmp_working_directory)
    await layer._find_config_file()

    assert layer._is_set is True
    assert layer._config_file_path is None

    config_path = tmp_working_directory / ".vibe" / "config.toml"
    config_path.parent.mkdir(parents=True, exist_ok=True)
    config_path.write_text('active_model = "new-model"\n')

    await layer._find_config_file()

    assert layer._config_file_path is None


@pytest.mark.asyncio
async def test_finds_config_in_parent_directory(tmp_working_directory: Path) -> None:
    trusted_folders_manager.add_trusted(tmp_working_directory)
    config_path = tmp_working_directory / ".vibe" / "config.toml"
    config_path.parent.mkdir(parents=True, exist_ok=True)
    config_path.write_text('active_model = "parent-model"\n')

    subdir = tmp_working_directory / "sub" / "project"
    subdir.mkdir(parents=True)

    layer = ProjectConfigLayer(path=subdir)
    data = await layer.load()
    assert data.model_extra == {"active_model": "parent-model"}


@pytest.mark.asyncio
async def test_trusted_ancestor_satisfies_trust_check(
    tmp_working_directory: Path,
) -> None:
    parent = tmp_working_directory / "workspace"
    parent.mkdir()
    child = parent / "project"
    child.mkdir()

    trusted_folders_manager.add_trusted(parent)

    config_path = child / ".vibe" / "config.toml"
    config_path.parent.mkdir(parents=True)
    config_path.write_text('active_model = "child-model"\n')

    layer = ProjectConfigLayer(path=child)
    data = await layer.load()
    assert data.model_extra == {"active_model": "child-model"}


@pytest.mark.asyncio
async def test_grant_trust_then_load_reads_config_without_prior_resolve(
    tmp_working_directory: Path,
) -> None:
    config_path = tmp_working_directory / ".vibe" / "config.toml"
    config_path.parent.mkdir(parents=True, exist_ok=True)
    config_path.write_text('key = "value"\n')

    layer = ProjectConfigLayer(path=tmp_working_directory)
    await layer.resolve_trust()
    await layer.grant_trust()
    data = await layer.load()

    assert data.model_extra == {"key": "value"}


@pytest.mark.asyncio
async def test_walk_stops_at_vibe_home_parent(
    tmp_path_factory: pytest.TempPathFactory, monkeypatch: pytest.MonkeyPatch
) -> None:
    # Create an isolated tmp root and point VIBE_HOME into it
    tmp_root = tmp_path_factory.mktemp("walk_stop")
    fake_vibe_home = tmp_root / ".vibe"
    fake_vibe_home.mkdir()
    monkeypatch.setattr(
        "vibe.core.config.layers.project.VIBE_HOME", GlobalPath(lambda: fake_vibe_home)
    )

    # Place a config at tmp_root/.vibe/config.toml — should NOT be picked up
    home_config = fake_vibe_home / "config.toml"
    home_config.write_text('active_model = "home-model"\n')

    # subdir lives inside tmp_root so the walk would reach the config without the stop guard
    subdir = tmp_root / "vibe-test-project"
    subdir.mkdir()
    trusted_folders_manager.add_trusted(subdir)

    layer = ProjectConfigLayer(path=subdir)
    data = await layer.load()
    assert data.model_extra == {}
