"""Tests for project init step."""

from __future__ import annotations

import tempfile
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

from installer.context import InstallContext
from installer.steps.project_init import ProjectInitStep
from installer.ui import Console


class TestProjectInitStepCheck:
    """Test ProjectInitStep.check()."""

    def test_returns_true_when_project_md_exists(self):
        """check() returns True when project.md already exists."""
        step = ProjectInitStep()
        with tempfile.TemporaryDirectory() as tmpdir:
            project_dir = Path(tmpdir)
            rules_dir = project_dir / ".claude" / "rules"
            rules_dir.mkdir(parents=True)
            (rules_dir / "project.md").write_text("# Project")

            ctx = InstallContext(project_dir=project_dir)
            assert step.check(ctx) is True

    def test_returns_false_when_project_md_missing(self):
        """check() returns False when project.md does not exist."""
        step = ProjectInitStep()
        with tempfile.TemporaryDirectory() as tmpdir:
            project_dir = Path(tmpdir)
            ctx = InstallContext(project_dir=project_dir)
            assert step.check(ctx) is False


class TestProjectInitStepRun:
    """Test ProjectInitStep.run()."""

    def test_has_correct_name(self):
        """Step has name 'project_init'."""
        step = ProjectInitStep()
        assert step.name == "project_init"

    @patch("installer.steps.project_init._find_tribunal_binary", return_value=None)
    def test_skips_when_binary_not_found(self, mock_find):
        """run() skips gracefully when tribunal binary is not available."""
        step = ProjectInitStep()
        with tempfile.TemporaryDirectory() as tmpdir:
            console = Console(non_interactive=True)
            ctx = InstallContext(project_dir=Path(tmpdir), ui=console)

            with patch.object(console, "info") as mock_info:
                step.run(ctx)
                mock_info.assert_called_once()
                assert "not available" in mock_info.call_args[0][0]

    @patch("installer.steps.project_init.subprocess.run")
    @patch("installer.steps.project_init._find_tribunal_binary")
    def test_calls_tribunal_skills_init(self, mock_find, mock_run):
        """run() calls 'tribunal skills init' subprocess."""
        mock_find.return_value = Path("/fake/bin/tribunal")
        mock_run.return_value = MagicMock(returncode=0, stdout="", stderr="")

        step = ProjectInitStep()
        with tempfile.TemporaryDirectory() as tmpdir:
            project_dir = Path(tmpdir)
            console = Console(non_interactive=True)
            ctx = InstallContext(project_dir=project_dir, ui=console)

            step.run(ctx)

            mock_run.assert_called_once()
            call_args = mock_run.call_args
            assert call_args[0][0] == ["/fake/bin/tribunal", "skills", "init"]
            assert call_args[1]["cwd"] == str(project_dir)

    @patch("installer.steps.project_init.subprocess.run")
    @patch("installer.steps.project_init._find_tribunal_binary")
    def test_reports_success_on_zero_exit(self, mock_find, mock_run):
        """run() reports success when subprocess exits 0."""
        mock_find.return_value = Path("/fake/bin/tribunal")
        mock_run.return_value = MagicMock(returncode=0)

        step = ProjectInitStep()
        with tempfile.TemporaryDirectory() as tmpdir:
            console = Console(non_interactive=True)
            ctx = InstallContext(project_dir=Path(tmpdir), ui=console)

            with patch.object(console, "success") as mock_success:
                step.run(ctx)
                mock_success.assert_called_once()
                assert "project.md" in mock_success.call_args[0][0]

    @patch("installer.steps.project_init.subprocess.run")
    @patch("installer.steps.project_init._find_tribunal_binary")
    def test_handles_nonzero_exit(self, mock_find, mock_run):
        """run() handles non-zero exit gracefully."""
        mock_find.return_value = Path("/fake/bin/tribunal")
        mock_run.return_value = MagicMock(returncode=1)

        step = ProjectInitStep()
        with tempfile.TemporaryDirectory() as tmpdir:
            console = Console(non_interactive=True)
            ctx = InstallContext(project_dir=Path(tmpdir), ui=console)

            with patch.object(console, "info") as mock_info:
                step.run(ctx)
                mock_info.assert_called_once()

    @patch("installer.steps.project_init.subprocess.run", side_effect=OSError("exec failed"))
    @patch("installer.steps.project_init._find_tribunal_binary")
    def test_handles_os_error(self, mock_find, mock_run):
        """run() handles OSError gracefully."""
        mock_find.return_value = Path("/fake/bin/tribunal")

        step = ProjectInitStep()
        with tempfile.TemporaryDirectory() as tmpdir:
            console = Console(non_interactive=True)
            ctx = InstallContext(project_dir=Path(tmpdir), ui=console)

            with patch.object(console, "warning") as mock_warning:
                step.run(ctx)
                mock_warning.assert_called_once()

    def test_run_without_ui(self):
        """run() works without a UI console."""
        step = ProjectInitStep()
        with tempfile.TemporaryDirectory() as tmpdir:
            ctx = InstallContext(project_dir=Path(tmpdir), ui=None)

            with patch("installer.steps.project_init._find_tribunal_binary", return_value=None):
                step.run(ctx)  # Should not raise
