"""Tests for brokr.project — discovery and context resolution."""

from pathlib import Path

import pytest

from brokr.project import (
    find_project_file,
    get_agent_secrets,
    get_project_secrets,
    read_project_name,
    resolve_project,
)
from brokr.store import BrokrError


class TestFindProjectFile:
    def test_finds_in_cwd(self, tmp_path):
        (tmp_path / ".brokr").write_text("my-app\n")
        result = find_project_file(tmp_path)
        assert result == tmp_path / ".brokr"

    def test_finds_in_parent(self, tmp_path):
        child = tmp_path / "src" / "lib"
        child.mkdir(parents=True)
        (tmp_path / ".brokr").write_text("my-app\n")
        result = find_project_file(child)
        assert result == tmp_path / ".brokr"

    def test_none_when_missing(self, tmp_path):
        assert find_project_file(tmp_path) is None

    def test_stops_at_max_depth(self, tmp_path):
        deep = tmp_path
        for i in range(15):
            deep = deep / f"d{i}"
        deep.mkdir(parents=True)
        (tmp_path / ".brokr").write_text("my-app\n")
        # .brokr is more than 10 levels up — should not be found
        result = find_project_file(deep)
        assert result is None

    def test_finds_within_max_depth(self, tmp_path):
        deep = tmp_path
        for i in range(5):
            deep = deep / f"d{i}"
        deep.mkdir(parents=True)
        (tmp_path / ".brokr").write_text("my-app\n")
        result = find_project_file(deep)
        assert result == tmp_path / ".brokr"


class TestReadProjectName:
    def test_reads_name(self, tmp_path):
        f = tmp_path / ".brokr"
        f.write_text("my-app\n")
        assert read_project_name(f) == "my-app"

    def test_strips_whitespace(self, tmp_path):
        f = tmp_path / ".brokr"
        f.write_text("  my-app  \n")
        assert read_project_name(f) == "my-app"

    def test_first_line_only(self, tmp_path):
        f = tmp_path / ".brokr"
        f.write_text("my-app\nextra-stuff\n")
        assert read_project_name(f) == "my-app"

    def test_empty_file_raises(self, tmp_path):
        f = tmp_path / ".brokr"
        f.write_text("")
        with pytest.raises(ValueError, match="Empty"):
            read_project_name(f)


class TestResolveProject:
    def test_resolves_existing_project(self, tmp_path):
        (tmp_path / ".brokr").write_text("my-app\n")
        store = {
            "secrets": {"KEY": "val"},
            "projects": {"my-app": {"secrets": ["KEY"], "agents": {}}},
        }
        name, data = resolve_project(store, start_dir=tmp_path)
        assert name == "my-app"
        assert data["secrets"] == ["KEY"]

    def test_raises_no_project_file(self, tmp_path):
        store = {"secrets": {}}
        with pytest.raises(BrokrError, match="No project context"):
            resolve_project(store, start_dir=tmp_path)

    def test_raises_unknown_project(self, tmp_path):
        (tmp_path / ".brokr").write_text("ghost\n")
        store = {"secrets": {}, "projects": {}}
        with pytest.raises(BrokrError, match="not found in store"):
            resolve_project(store, start_dir=tmp_path)


class TestGetProjectSecrets:
    def test_resolves_names_to_values(self):
        store = {
            "secrets": {"A": "1", "B": "2", "C": "3"},
            "projects": {"p": {"secrets": ["A", "C"], "agents": {}}},
        }
        result = get_project_secrets(store, "p")
        assert result == {"A": "1", "C": "3"}

    def test_skips_deleted_secrets(self):
        store = {
            "secrets": {"A": "1"},
            "projects": {"p": {"secrets": ["A", "GONE"], "agents": {}}},
        }
        result = get_project_secrets(store, "p")
        assert result == {"A": "1"}

    def test_empty_project(self):
        store = {
            "secrets": {"A": "1"},
            "projects": {"p": {"secrets": [], "agents": {}}},
        }
        assert get_project_secrets(store, "p") == {}

    def test_missing_project(self):
        store = {"secrets": {"A": "1"}, "projects": {}}
        assert get_project_secrets(store, "nope") == {}


class TestGetAgentSecrets:
    def test_resolves_grants(self):
        store = {
            "secrets": {"A": "1", "B": "2"},
            "projects": {
                "p": {"secrets": ["A", "B"], "agents": {"bot": ["A"]}}
            },
        }
        result = get_agent_secrets(store, "p", "bot")
        assert result == {"A": "1"}

    def test_skips_deleted_secrets(self):
        store = {
            "secrets": {"A": "1"},
            "projects": {
                "p": {"secrets": ["A", "GONE"], "agents": {"bot": ["A", "GONE"]}}
            },
        }
        result = get_agent_secrets(store, "p", "bot")
        assert result == {"A": "1"}

    def test_unknown_agent_raises(self):
        store = {
            "secrets": {},
            "projects": {"p": {"secrets": [], "agents": {}}},
        }
        with pytest.raises(BrokrError, match="Agent 'ghost' not found"):
            get_agent_secrets(store, "p", "ghost")

    def test_agent_with_no_grants(self):
        store = {
            "secrets": {"A": "1"},
            "projects": {"p": {"secrets": ["A"], "agents": {"bot": []}}},
        }
        assert get_agent_secrets(store, "p", "bot") == {}
