from __future__ import annotations

from contextlib import contextmanager
from pathlib import Path
from types import SimpleNamespace

from terminaluse.core import ApiError
from terminaluse.lib.cli.handlers.source_snapshot_handlers import (
    capture_deploy_source_snapshot,
    get_or_create_builder_artifact_project,
)


def test_get_or_create_builder_artifact_project_reuses_latest_matching_project() -> None:
    client = SimpleNamespace()
    detailed_old = SimpleNamespace(
        id="proj_old",
        name="TerminalUse Builder Artifacts",
        namespace_id="ns_1",
        description="Managed immutable Builder deploy artifacts. [terminaluse-builder-artifacts:v1;namespace=ns_1]",
        created_at="2026-03-15T10:00:00Z",
    )
    detailed_new = SimpleNamespace(
        id="proj_new",
        name="TerminalUse Builder Artifacts",
        namespace_id="ns_1",
        description="Managed immutable Builder deploy artifacts. [terminaluse-builder-artifacts:v1;namespace=ns_1]",
        created_at="2026-03-16T10:00:00Z",
    )
    client.projects = SimpleNamespace(
        list=lambda **_: [
            SimpleNamespace(id="proj_old", name="TerminalUse Builder Artifacts"),
            SimpleNamespace(id="proj_new", name="TerminalUse Builder Artifacts"),
        ],
        retrieve=lambda project_id: detailed_old if project_id == "proj_old" else detailed_new,
        create=lambda **_: (_ for _ in ()).throw(AssertionError("should not create a new project")),
    )

    assert get_or_create_builder_artifact_project(client, "ns_1") == "proj_new"


def test_get_or_create_builder_artifact_project_creates_when_no_owned_project_exists() -> None:
    client = SimpleNamespace()
    created_calls: list[dict] = []
    unrelated = SimpleNamespace(
        id="proj_unrelated",
        name="TerminalUse Builder Artifacts",
        namespace_id="ns_other",
        description="Some other project",
        created_at="2026-03-15T10:00:00Z",
    )
    client.projects = SimpleNamespace(
        list=lambda **_: [SimpleNamespace(id="proj_unrelated", name="TerminalUse Builder Artifacts")],
        retrieve=lambda project_id: unrelated,
        create=lambda **kwargs: created_calls.append(kwargs) or SimpleNamespace(id="proj_created"),
    )

    assert get_or_create_builder_artifact_project(client, "ns_1") == "proj_created"
    assert created_calls == [
        {
            "namespace_id": "ns_1",
            "name": "TerminalUse Builder Artifacts",
            "description": "Managed immutable Builder deploy artifacts. [terminaluse-builder-artifacts:v1;namespace=ns_1]",
        }
    ]


def test_get_or_create_builder_artifact_project_searches_across_pages() -> None:
    client = SimpleNamespace()
    detailed = SimpleNamespace(
        id="proj_managed",
        name="TerminalUse Builder Artifacts",
        namespace_id="ns_1",
        description="Managed immutable Builder deploy artifacts. [terminaluse-builder-artifacts:v1;namespace=ns_1]",
        created_at="2026-03-16T10:00:00Z",
    )
    first_page = [
        SimpleNamespace(id=f"proj_workspace_{index}", name="TerminalUse Builder Workspace Archive")
        for index in range(100)
    ]
    listed_pages = {
        1: first_page,
        2: [SimpleNamespace(id="proj_managed", name="TerminalUse Builder Artifacts")],
    }
    seen_pages: list[int] = []

    def list_projects(**kwargs):
        page_number = kwargs.get("page_number", 1)
        seen_pages.append(page_number)
        return listed_pages.get(page_number, [])

    client.projects = SimpleNamespace(
        list=list_projects,
        retrieve=lambda project_id: detailed,
        create=lambda **_: (_ for _ in ()).throw(AssertionError("should not create a new project")),
    )

    assert get_or_create_builder_artifact_project(client, "ns_1") == "proj_managed"
    assert seen_pages == [1, 2]


def test_get_or_create_builder_artifact_project_recovers_after_duplicate_create() -> None:
    client = SimpleNamespace()
    detailed = SimpleNamespace(
        id="proj_managed",
        name="TerminalUse Builder Artifacts",
        namespace_id="ns_1",
        description="Managed immutable Builder deploy artifacts. [terminaluse-builder-artifacts:v1;namespace=ns_1]",
        created_at="2026-03-16T10:00:00Z",
    )
    calls = {"list": 0}

    def list_projects(**kwargs):
        calls["list"] += 1
        if calls["list"] == 1:
            return []
        return [SimpleNamespace(id="proj_managed", name="TerminalUse Builder Artifacts")]

    def create_project(**kwargs):
        raise ApiError(
            status_code=400,
            body={"message": "Item already exists. Please check all unique constraints and try again."},
        )

    client.projects = SimpleNamespace(
        list=list_projects,
        retrieve=lambda project_id: detailed,
        create=create_project,
    )

    assert get_or_create_builder_artifact_project(client, "ns_1") == "proj_managed"
    assert calls["list"] == 2


def test_capture_deploy_source_snapshot_pushes_manifest_context(
    monkeypatch,
    tmp_path: Path,
) -> None:
    config_path = tmp_path / "config.yaml"
    config_path.write_text("agent:\n  name: acme/test-agent\n", encoding="utf-8")
    build_root = tmp_path / "workspace"
    build_root.mkdir()
    (build_root / "agent.py").write_text("print('hello')\n", encoding="utf-8")

    created = {}
    pushed = {}

    @contextmanager
    def fake_context_manager(path: Path):
        yield SimpleNamespace(path=str(path))

    fake_manifest = SimpleNamespace(
        build=SimpleNamespace(context=SimpleNamespace(root="workspace")),
        context_manager=lambda root: fake_context_manager(root),
    )

    monkeypatch.setattr(
        "terminaluse.lib.cli.handlers.source_snapshot_handlers.AgentManifest.from_yaml",
        lambda file_path: fake_manifest,
    )
    monkeypatch.setattr(
        "terminaluse.lib.cli.handlers.source_snapshot_handlers.get_or_create_builder_artifact_project",
        lambda client, namespace_id: "proj_builder",
    )
    monkeypatch.setattr(
        "terminaluse.lib.cli.handlers.source_snapshot_handlers.create_filesystem",
        lambda client, project_id, name: created.setdefault(
            "filesystem",
            SimpleNamespace(id="fs_snapshot", project_id=project_id, name=name),
        ),
    )
    monkeypatch.setattr(
        "terminaluse.lib.cli.handlers.source_snapshot_handlers.push_directory",
        lambda client, filesystem_id, local_path: pushed.setdefault(
            "call",
            (filesystem_id, local_path),
        ),
    )

    snapshot = capture_deploy_source_snapshot(
        client=SimpleNamespace(),
        namespace_id="ns_1",
        target_agent_name="acme/test-agent",
        config_path=str(config_path),
    )

    assert snapshot.filesystem_id == "fs_snapshot"
    assert snapshot.project_id == "proj_builder"
    assert snapshot.filesystem_name.startswith("builder-artifact-acme-test-agent-")
    assert created["filesystem"].project_id == "proj_builder"
    assert pushed["call"] == ("fs_snapshot", build_root.resolve())
