"""Steps for sandbox_create_for_plan.feature."""

from __future__ import annotations

import os
import shutil
import subprocess
import tempfile
from pathlib import Path
from unittest.mock import MagicMock, patch

from behave import given, then, when
from behave.runner import Context

_PLAN_ID = "01TESTSCFP000000000000000"


def _git(args: list[str], cwd: str) -> subprocess.CompletedProcess[str]:
    return subprocess.run(
        ["git", *args],
        cwd=cwd,
        capture_output=True,
        text=True,
        check=True,
        timeout=10,
    )


def _init_git_repo(path: str) -> None:
    _git(["init", "-q", "-b", "main"], path)
    _git(["config", "user.name", "T"], path)
    _git(["config", "user.email", "t@t"], path)
    _git(["config", "commit.gpgsign", "false"], path)


def _build_mocks(context: object, repo_path: str | None) -> None:
    """Build mock service + container for _create_sandbox_for_plan."""
    if repo_path is None:
        mock_plan = MagicMock()
        mock_plan.project_links = []
        mock_service = MagicMock()
        mock_service.get_plan.return_value = mock_plan
        mock_container = MagicMock()
        context.scfp_service = mock_service
        context.scfp_container = mock_container
        return

    mock_resource = MagicMock()
    mock_resource.resource_type_name = "git-checkout"
    mock_resource.location = repo_path
    mock_resource.resource_id = "res-scfp-test"

    mock_lr = MagicMock()
    mock_lr.resource_id = "res-scfp-test"

    mock_project = MagicMock()
    mock_project.linked_resources = [mock_lr]

    mock_plan = MagicMock()
    mock_plan.project_links = [MagicMock(project_name="local/scfp-test")]

    mock_service = MagicMock()
    mock_service.get_plan.return_value = mock_plan

    mock_project_repo = MagicMock()
    mock_project_repo.get.return_value = mock_project

    mock_resource_registry = MagicMock()
    mock_resource_registry.show_resource.return_value = mock_resource

    mock_container = MagicMock()
    mock_container.namespaced_project_repo.return_value = mock_project_repo
    mock_container.resource_registry_service.return_value = mock_resource_registry

    context.scfp_service = mock_service
    context.scfp_container = mock_container


@given("a temp git project for scfp")
def step_create_git_project(context: Context) -> None:
    d = tempfile.mkdtemp(prefix="scfp-")
    context.add_cleanup(shutil.rmtree, d, True)
    _init_git_repo(d)
    Path(d, "README.md").write_text("# test\n")
    _git(["add", "."], d)
    _git(["commit", "-q", "-m", "init"], d)
    context.scfp_project = d


@given("a temp git project without a worktree for scfp")
def step_create_git_project_no_worktree(context: Context) -> None:
    d = tempfile.mkdtemp(prefix="scfp-clean-")
    context.add_cleanup(shutil.rmtree, d, True)
    _init_git_repo(d)
    Path(d, "README.md").write_text("# test\n")
    _git(["add", "."], d)
    _git(["commit", "-q", "-m", "init"], d)
    context.scfp_project = d


@given("a mocked plan service linking the git project for scfp")
def step_mock_service_with_git_project(context: Context) -> None:
    _build_mocks(context, context.scfp_project)


@given("a mocked plan service with no linked resources for scfp")
def step_mock_service_no_resources(context: Context) -> None:
    _build_mocks(context, None)


@when("I call _create_sandbox_for_plan for scfp")
def step_call_create_sandbox(context: Context) -> None:
    from cleveragents.cli.commands.plan import _create_sandbox_for_plan

    with patch(
        "cleveragents.application.container.get_container",
        return_value=context.scfp_container,
    ):
        sandbox_root, sandbox_infos = _create_sandbox_for_plan(
            _PLAN_ID, context.scfp_service
        )
    context.scfp_sandbox_root = sandbox_root
    # _create_sandbox_for_plan returns (str | None, list[_SandboxInfo]).
    # Extract the first sandbox object from the list, or None if empty.
    context.scfp_sandbox_obj = sandbox_infos[0].sandbox_obj if sandbox_infos else None
    if context.scfp_sandbox_obj is not None:
        context.add_cleanup(context.scfp_sandbox_obj.cleanup)


@when("I call _cleanup_sandbox_for_plan with no stale branch for scfp")
def step_call_cleanup_no_stale(context: Context) -> None:
    from cleveragents.cli.commands.plan import _cleanup_sandbox_for_plan

    with patch(
        "cleveragents.cli.commands.plan.get_container",
        return_value=context.scfp_container,
    ):
        _cleanup_sandbox_for_plan(_PLAN_ID, context.scfp_service)
    context.scfp_cleanup_done = True


@then("the sandbox root should be a directory for scfp")
def step_sandbox_root_is_dir(context: Context) -> None:
    assert os.path.isdir(context.scfp_sandbox_root), (
        f"Expected sandbox root to be a directory: {context.scfp_sandbox_root}"
    )


@then("the sandbox object should not be None for scfp")
def step_sandbox_obj_not_none(context: Context) -> None:
    assert context.scfp_sandbox_obj is not None, (
        "Expected sandbox object to not be None"
    )


@then("the sandbox object should be None for scfp")
def step_sandbox_obj_is_none(context: Context) -> None:
    assert context.scfp_sandbox_obj is None, (
        f"Expected sandbox object to be None, got {context.scfp_sandbox_obj}"
    )


@then("the sandbox root should differ from the project path for scfp")
def step_sandbox_root_differs(context: Context) -> None:
    assert context.scfp_sandbox_root != context.scfp_project, (
        "Expected sandbox root to differ from project path"
    )


@then("the cleanup call should complete without error for scfp")
def step_cleanup_no_error(context: Context) -> None:
    assert context.scfp_cleanup_done is True
