"""Tests for brokr project CLI commands."""

import os
from pathlib import Path

import typer
from typer.testing import CliRunner

from brokr.cli_project import project_app
from brokr.store import load_store, save_store

# Standalone test app since sub-app isn't wired via main app in tests
test_app = typer.Typer()
test_app.add_typer(project_app, name="project")

runner = CliRunner()
PASSPHRASE = "test"


def _init_store(store_dir: Path, secrets: dict | None = None, projects: dict | None = None):
    """Create a store with optional secrets and projects."""
    data = {"secrets": secrets or {}}
    if projects is not None:
        data["projects"] = projects
    save_store(data, PASSPHRASE, store_dir=store_dir)


class TestProjectInit:
    def test_creates_project_and_file(self, tmp_path, monkeypatch):
        _init_store(tmp_path)
        monkeypatch.setenv("BROKR_DIR", str(tmp_path))
        monkeypatch.setenv("BROKR_PASSPHRASE", PASSPHRASE)
        project_dir = tmp_path / "myrepo"
        project_dir.mkdir()
        monkeypatch.chdir(project_dir)

        result = runner.invoke(test_app, ["project", "init", "my-app"])
        assert result.exit_code == 0
        assert "my-app" in result.stderr

        # .brokr file created in cwd
        brokr_file = project_dir / ".brokr"
        assert brokr_file.exists()
        assert brokr_file.read_text().strip() == "my-app"

        # Project registered in store
        data = load_store(PASSPHRASE, store_dir=tmp_path)
        assert "my-app" in data["projects"]
        assert data["projects"]["my-app"] == {"secrets": [], "agents": {}}

    def test_gitignore_reminder(self, tmp_path, monkeypatch):
        _init_store(tmp_path)
        monkeypatch.setenv("BROKR_DIR", str(tmp_path))
        monkeypatch.setenv("BROKR_PASSPHRASE", PASSPHRASE)
        monkeypatch.chdir(tmp_path)

        result = runner.invoke(test_app, ["project", "init", "app"])
        assert ".gitignore" in result.stderr

    def test_duplicate_project_errors(self, tmp_path, monkeypatch):
        _init_store(tmp_path, projects={"existing": {"secrets": [], "agents": {}}})
        monkeypatch.setenv("BROKR_DIR", str(tmp_path))
        monkeypatch.setenv("BROKR_PASSPHRASE", PASSPHRASE)
        monkeypatch.chdir(tmp_path)

        result = runner.invoke(test_app, ["project", "init", "existing"])
        assert result.exit_code == 1
        assert "already exists" in result.stderr

    def test_force_overwrites(self, tmp_path, monkeypatch):
        _init_store(
            tmp_path,
            projects={"existing": {"secrets": ["OLD"], "agents": {"bot": ["OLD"]}}},
        )
        monkeypatch.setenv("BROKR_DIR", str(tmp_path))
        monkeypatch.setenv("BROKR_PASSPHRASE", PASSPHRASE)
        monkeypatch.chdir(tmp_path)

        result = runner.invoke(test_app, ["project", "init", "existing", "--force"])
        assert result.exit_code == 0

        data = load_store(PASSPHRASE, store_dir=tmp_path)
        assert data["projects"]["existing"] == {"secrets": [], "agents": {}}


class TestProjectAdd:
    def test_links_secret(self, tmp_path, monkeypatch):
        _init_store(
            tmp_path,
            secrets={"API_KEY": "sk-123"},
            projects={"my-app": {"secrets": [], "agents": {}}},
        )
        monkeypatch.setenv("BROKR_DIR", str(tmp_path))
        monkeypatch.setenv("BROKR_PASSPHRASE", PASSPHRASE)
        (tmp_path / ".brokr").write_text("my-app\n")
        monkeypatch.chdir(tmp_path)

        result = runner.invoke(test_app, ["project", "add", "API_KEY"])
        assert result.exit_code == 0
        assert "linked" in result.stderr

        data = load_store(PASSPHRASE, store_dir=tmp_path)
        assert "API_KEY" in data["projects"]["my-app"]["secrets"]

    def test_secret_not_in_store(self, tmp_path, monkeypatch):
        _init_store(
            tmp_path,
            projects={"my-app": {"secrets": [], "agents": {}}},
        )
        monkeypatch.setenv("BROKR_DIR", str(tmp_path))
        monkeypatch.setenv("BROKR_PASSPHRASE", PASSPHRASE)
        (tmp_path / ".brokr").write_text("my-app\n")
        monkeypatch.chdir(tmp_path)

        result = runner.invoke(test_app, ["project", "add", "GHOST"])
        assert result.exit_code == 1
        assert "not found in store" in result.stderr

    def test_duplicate_link_warns(self, tmp_path, monkeypatch):
        _init_store(
            tmp_path,
            secrets={"API_KEY": "sk-123"},
            projects={"my-app": {"secrets": ["API_KEY"], "agents": {}}},
        )
        monkeypatch.setenv("BROKR_DIR", str(tmp_path))
        monkeypatch.setenv("BROKR_PASSPHRASE", PASSPHRASE)
        (tmp_path / ".brokr").write_text("my-app\n")
        monkeypatch.chdir(tmp_path)

        result = runner.invoke(test_app, ["project", "add", "API_KEY"])
        assert result.exit_code == 0
        assert "already linked" in result.stderr

    def test_invalid_key_name(self, tmp_path, monkeypatch):
        _init_store(tmp_path, projects={"p": {"secrets": [], "agents": {}}})
        monkeypatch.setenv("BROKR_DIR", str(tmp_path))
        monkeypatch.setenv("BROKR_PASSPHRASE", PASSPHRASE)
        (tmp_path / ".brokr").write_text("p\n")
        monkeypatch.chdir(tmp_path)

        result = runner.invoke(test_app, ["project", "add", "bad-name"])
        assert result.exit_code != 0


class TestProjectRm:
    def test_unlinks_secret(self, tmp_path, monkeypatch):
        _init_store(
            tmp_path,
            secrets={"API_KEY": "sk-123"},
            projects={"my-app": {"secrets": ["API_KEY"], "agents": {}}},
        )
        monkeypatch.setenv("BROKR_DIR", str(tmp_path))
        monkeypatch.setenv("BROKR_PASSPHRASE", PASSPHRASE)
        (tmp_path / ".brokr").write_text("my-app\n")
        monkeypatch.chdir(tmp_path)

        result = runner.invoke(test_app, ["project", "rm", "API_KEY"])
        assert result.exit_code == 0

        data = load_store(PASSPHRASE, store_dir=tmp_path)
        assert "API_KEY" not in data["projects"]["my-app"]["secrets"]

    def test_cascades_to_agents(self, tmp_path, monkeypatch):
        _init_store(
            tmp_path,
            secrets={"API_KEY": "sk-123", "TOKEN": "tok"},
            projects={
                "my-app": {
                    "secrets": ["API_KEY", "TOKEN"],
                    "agents": {
                        "bot-a": ["API_KEY", "TOKEN"],
                        "bot-b": ["API_KEY"],
                    },
                }
            },
        )
        monkeypatch.setenv("BROKR_DIR", str(tmp_path))
        monkeypatch.setenv("BROKR_PASSPHRASE", PASSPHRASE)
        (tmp_path / ".brokr").write_text("my-app\n")
        monkeypatch.chdir(tmp_path)

        result = runner.invoke(test_app, ["project", "rm", "API_KEY"])
        assert result.exit_code == 0
        assert "Revoked from agents" in result.stderr

        data = load_store(PASSPHRASE, store_dir=tmp_path)
        assert "API_KEY" not in data["projects"]["my-app"]["agents"]["bot-a"]
        assert "API_KEY" not in data["projects"]["my-app"]["agents"]["bot-b"]
        assert "TOKEN" in data["projects"]["my-app"]["agents"]["bot-a"]

    def test_not_linked_errors(self, tmp_path, monkeypatch):
        _init_store(
            tmp_path,
            secrets={"API_KEY": "sk-123"},
            projects={"my-app": {"secrets": [], "agents": {}}},
        )
        monkeypatch.setenv("BROKR_DIR", str(tmp_path))
        monkeypatch.setenv("BROKR_PASSPHRASE", PASSPHRASE)
        (tmp_path / ".brokr").write_text("my-app\n")
        monkeypatch.chdir(tmp_path)

        result = runner.invoke(test_app, ["project", "rm", "API_KEY"])
        assert result.exit_code == 1
        assert "not linked" in result.stderr


class TestProjectList:
    def test_lists_all_projects(self, tmp_path, monkeypatch):
        _init_store(
            tmp_path,
            projects={
                "app-a": {"secrets": ["K"], "agents": {}},
                "app-b": {"secrets": [], "agents": {"bot": []}},
            },
        )
        monkeypatch.setenv("BROKR_DIR", str(tmp_path))
        monkeypatch.setenv("BROKR_PASSPHRASE", PASSPHRASE)
        # No .brokr file — should list all projects
        monkeypatch.chdir(tmp_path / ".." if tmp_path.parent != tmp_path else tmp_path)
        # Ensure no .brokr file in cwd
        no_project = tmp_path / "noproj"
        no_project.mkdir()
        monkeypatch.chdir(no_project)

        result = runner.invoke(test_app, ["project", "list"])
        assert result.exit_code == 0
        assert "app-a" in result.stderr
        assert "app-b" in result.stderr

    def test_lists_current_project_secrets(self, tmp_path, monkeypatch):
        _init_store(
            tmp_path,
            secrets={"API_KEY": "sk-123456789"},
            projects={"my-app": {"secrets": ["API_KEY"], "agents": {}}},
        )
        monkeypatch.setenv("BROKR_DIR", str(tmp_path))
        monkeypatch.setenv("BROKR_PASSPHRASE", PASSPHRASE)
        (tmp_path / ".brokr").write_text("my-app\n")
        monkeypatch.chdir(tmp_path)

        result = runner.invoke(test_app, ["project", "list"])
        assert result.exit_code == 0
        assert "API_KEY" in result.stderr

    def test_no_projects(self, tmp_path, monkeypatch):
        _init_store(tmp_path)
        monkeypatch.setenv("BROKR_DIR", str(tmp_path))
        monkeypatch.setenv("BROKR_PASSPHRASE", PASSPHRASE)
        no_project = tmp_path / "noproj"
        no_project.mkdir()
        monkeypatch.chdir(no_project)

        result = runner.invoke(test_app, ["project", "list"])
        assert "No projects" in result.stderr


class TestProjectShow:
    def test_shows_project_details(self, tmp_path, monkeypatch):
        _init_store(
            tmp_path,
            secrets={"API_KEY": "sk-123456789", "TOKEN": "tok-abc"},
            projects={
                "my-app": {
                    "secrets": ["API_KEY", "TOKEN"],
                    "agents": {"deploy-bot": ["TOKEN"]},
                }
            },
        )
        monkeypatch.setenv("BROKR_DIR", str(tmp_path))
        monkeypatch.setenv("BROKR_PASSPHRASE", PASSPHRASE)
        (tmp_path / ".brokr").write_text("my-app\n")
        monkeypatch.chdir(tmp_path)

        result = runner.invoke(test_app, ["project", "show"])
        assert result.exit_code == 0
        assert "my-app" in result.stderr
        assert "API_KEY" in result.stderr
        assert "deploy-bot" in result.stderr

    def test_no_project_context_errors(self, tmp_path, monkeypatch):
        _init_store(tmp_path)
        monkeypatch.setenv("BROKR_DIR", str(tmp_path))
        monkeypatch.setenv("BROKR_PASSPHRASE", PASSPHRASE)
        no_project = tmp_path / "noproj"
        no_project.mkdir()
        monkeypatch.chdir(no_project)

        result = runner.invoke(test_app, ["project", "show"])
        assert result.exit_code == 1
        assert "No project context" in result.stderr
