"""Tests for Makefile-based build node."""

from unittest.mock import Mock, patch

import pytest

from fwauto.nodes.build import build_node
from fwauto.state import FirmwareState


@pytest.fixture
def base_state() -> FirmwareState:
    """Create a base state for testing."""
    return {
        "user_prompt": "test build",
        "project_root": "/test/project",
        "project_initialized": True,
        "build_status": "pending",
        "build_errors": [],
        "build_log": "",
        "flash_status": "pending",
        "flash_errors": [],
        "flash_log": "",
        "iteration": 0,
        "max_retries": 3,
        "execution_mode": "build",
        "command": "build",
        "command_args": {},
        "mode_metadata": None,
        "command_error": None,
        "log_file_path": None,
        "log_analysis_result": None,
        "firmware_path": None,
    }


def test_build_node_not_initialized(base_state):
    """Test build_node returns error when project not initialized."""
    base_state["project_initialized"] = False

    result = build_node(base_state)

    assert result["build_status"] == "error"
    assert "not initialized" in result["build_errors"][0].lower()


@patch("fwauto.nodes.build.load_project_config")
@patch("fwauto.utils.subprocess.Popen")
def test_build_node_success(mock_popen, mock_load_config, base_state):
    """Test successful build with Makefile."""
    # Mock config
    mock_config = Mock()
    mock_config.build_makefile = ".fwauto/build/Makefile"
    mock_config.build_target = "build"
    mock_load_config.return_value = mock_config

    # Mock process
    mock_process = Mock()
    mock_process.returncode = 0
    mock_process.communicate.return_value = ("Build successful\n", None)
    mock_popen.return_value = mock_process

    result = build_node(base_state)

    assert result["build_status"] == "success"
    assert "Build successful" in result["build_log"]


@patch("fwauto.nodes.build.load_project_config")
def test_build_node_config_not_found(mock_load_config, base_state):
    """Test build_node returns error when config.toml not found."""
    from fwauto.config import ConfigError

    mock_load_config.side_effect = ConfigError("Config file not found")

    result = build_node(base_state)

    assert result["build_status"] == "error"
    assert "Config error" in result["build_errors"][0]


@patch("fwauto.nodes.build.load_project_config")
@patch("fwauto.nodes.build.subprocess.Popen")
def test_build_node_makefile_not_found(mock_popen, mock_load_config, base_state):
    """Test build_node returns error when Makefile not found."""
    # Mock config
    mock_config = Mock()
    mock_config.build_makefile = ".fwauto/build/Makefile"
    mock_config.build_target = "build"
    mock_load_config.return_value = mock_config

    # Mock Popen to raise FileNotFoundError (make command not found or Makefile issue)
    mock_popen.side_effect = FileNotFoundError("make: not found")

    result = build_node(base_state)

    assert result["build_status"] == "error"
    assert "Build exception" in result["build_errors"][0]


@patch("fwauto.nodes.build.load_project_config")
@patch("fwauto.utils.subprocess.Popen")
def test_build_node_make_failed(mock_popen, mock_load_config, base_state):
    """Test build_node handles make failure."""
    # Mock config
    mock_config = Mock()
    mock_config.build_makefile = ".fwauto/build/Makefile"
    mock_config.build_target = "build"
    mock_load_config.return_value = mock_config

    # Mock process failure
    mock_process = Mock()
    mock_process.returncode = 1
    mock_process.communicate.return_value = ("Make error\n", None)
    mock_popen.return_value = mock_process

    result = build_node(base_state)

    assert result["build_status"] == "error"
    assert result["iteration"] == 1
    assert "Build failed with exit code 1" in result["build_errors"][0]


@patch("fwauto.nodes.build.load_project_config")
@patch("fwauto.utils.subprocess.Popen")
def test_build_node_build_output_toml_missing(mock_popen, mock_load_config, base_state):
    """Test build_node handles build success without firmware_path tracking."""
    # Mock config
    mock_config = Mock()
    mock_config.build_makefile = ".fwauto/build/Makefile"
    mock_config.build_target = "build"
    mock_load_config.return_value = mock_config

    # Mock process success
    mock_process = Mock()
    mock_process.returncode = 0
    mock_process.communicate.return_value = ("Build successful\n", None)
    mock_popen.return_value = mock_process

    result = build_node(base_state)

    # Should succeed (current implementation doesn't track firmware_path in build)
    assert result["build_status"] == "success"
