"""Tests for utility functions."""

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

import pytest

from fwauto.utils import (
    check_fwauto_config_exists,
    ensure_fwauto_dir,
    execute_make_command,
    get_fwauto_dir,
    load_toml,
)


class TestLoadToml:
    """Tests for load_toml function."""

    def test_load_valid_toml(self, tmp_path: Path):
        """Test loading a valid TOML file."""
        toml_file = tmp_path / "test.toml"
        toml_file.write_text(
            """
[build]
command = "make build"
output_dir = "output"

[flash]
tool = "st-link"
"""
        )

        data = load_toml(toml_file)
        assert data["build"]["command"] == "make build"
        assert data["build"]["output_dir"] == "output"
        assert data["flash"]["tool"] == "st-link"

    def test_load_nonexistent_file(self, tmp_path: Path):
        """Test loading a non-existent TOML file."""
        toml_file = tmp_path / "nonexistent.toml"

        with pytest.raises(FileNotFoundError):
            load_toml(toml_file)

    def test_load_malformed_toml(self, tmp_path: Path):
        """Test loading a malformed TOML file."""
        toml_file = tmp_path / "malformed.toml"
        toml_file.write_text("[invalid toml content")

        # tomllib.TOMLDecodeError is the expected exception
        # but it's only available in Python 3.11+
        with pytest.raises(Exception):  # Catch any parsing error
            load_toml(toml_file)


class TestCheckFwautoConfigExists:
    """Tests for check_fwauto_config_exists function."""

    def test_config_exists(self, tmp_path: Path):
        """Test when .fwauto/config.toml exists."""
        fwauto_dir = tmp_path / ".fwauto"
        fwauto_dir.mkdir()
        config_file = fwauto_dir / "config.toml"
        config_file.write_text("[build]\ncommand = 'make'")

        assert check_fwauto_config_exists(tmp_path) is True

    def test_config_not_exists(self, tmp_path: Path):
        """Test when .fwauto/config.toml does not exist."""
        assert check_fwauto_config_exists(tmp_path) is False

    def test_fwauto_dir_exists_but_no_config(self, tmp_path: Path):
        """Test when .fwauto directory exists but no config.toml."""
        fwauto_dir = tmp_path / ".fwauto"
        fwauto_dir.mkdir()

        assert check_fwauto_config_exists(tmp_path) is False


class TestGetFwautoDir:
    """Tests for get_fwauto_dir function."""

    def test_get_fwauto_dir_path(self, tmp_path: Path):
        """Test getting .fwauto directory path."""
        fwauto_dir = get_fwauto_dir(tmp_path)
        assert fwauto_dir == tmp_path / ".fwauto"

    def test_get_fwauto_dir_string_input(self):
        """Test with string input."""
        fwauto_dir = get_fwauto_dir("/home/user/project")
        assert fwauto_dir == Path("/home/user/project") / ".fwauto"


class TestEnsureFwautoDir:
    """Tests for ensure_fwauto_dir function."""

    def test_ensure_creates_dir(self, tmp_path: Path):
        """Test that directory is created if it doesn't exist."""
        fwauto_dir = ensure_fwauto_dir(tmp_path)
        assert fwauto_dir.exists()
        assert fwauto_dir.is_dir()
        assert fwauto_dir == tmp_path / ".fwauto"

    def test_ensure_existing_dir(self, tmp_path: Path):
        """Test that existing directory is not affected."""
        fwauto_dir = tmp_path / ".fwauto"
        fwauto_dir.mkdir()

        # Create a test file in the directory
        test_file = fwauto_dir / "test.txt"
        test_file.write_text("test content")

        # Call ensure_fwauto_dir
        result_dir = ensure_fwauto_dir(tmp_path)

        # Check directory still exists and file is intact
        assert result_dir.exists()
        assert test_file.exists()
        assert test_file.read_text() == "test content"

    def test_ensure_with_parents(self, tmp_path: Path):
        """Test that parent directories are created if needed."""
        nested_path = tmp_path / "level1" / "level2"
        fwauto_dir = ensure_fwauto_dir(nested_path)

        assert fwauto_dir.exists()
        assert fwauto_dir == nested_path / ".fwauto"


class TestExecuteMakeCommand:
    """Tests for execute_make_command function."""

    @patch("fwauto.utils.subprocess.Popen")
    def test_execute_make_command_success(self, mock_popen):
        """Test successful make execution."""
        # Arrange
        mock_process = MagicMock()
        mock_process.returncode = 0
        mock_process.communicate.return_value = ("Building...\nCompile complete\nBuild succeeded\n", None)
        mock_popen.return_value = mock_process

        makefile_path = Path("/fake/project/.fwauto/build/Makefile")
        target = "build"
        project_root = Path("/fake/project")

        # Act
        returncode, output = execute_make_command(
            makefile_path=makefile_path,
            target=target,
            project_root=project_root,
            timeout=60,
        )

        # Assert
        assert returncode == 0
        assert "Building..." in output
        assert "Build succeeded" in output
        assert "Exit code: 0" in output
        mock_popen.assert_called_once()

    @patch("fwauto.utils.subprocess.Popen")
    def test_execute_make_command_failure(self, mock_popen):
        """Test failed make execution."""
        # Arrange
        mock_process = MagicMock()
        mock_process.returncode = 2
        mock_process.communicate.return_value = ("Building...\nError: compilation failed\n", None)
        mock_popen.return_value = mock_process

        makefile_path = Path("/fake/project/.fwauto/build/Makefile")
        target = "build"
        project_root = Path("/fake/project")

        # Act
        returncode, output = execute_make_command(
            makefile_path=makefile_path,
            target=target,
            project_root=project_root,
            timeout=60,
        )

        # Assert
        assert returncode == 2
        assert "compilation failed" in output
        assert "Exit code: 2" in output

    @patch("fwauto.utils.subprocess.Popen")
    def test_execute_make_command_timeout(self, mock_popen):
        """Test make execution timeout."""
        # Arrange
        mock_process = MagicMock()
        mock_process.communicate.side_effect = subprocess.TimeoutExpired(cmd=["make", "build"], timeout=60)
        mock_popen.return_value = mock_process

        makefile_path = Path("/fake/project/.fwauto/build/Makefile")
        target = "build"
        project_root = Path("/fake/project")

        # Act & Assert
        with pytest.raises(subprocess.TimeoutExpired):
            execute_make_command(
                makefile_path=makefile_path,
                target=target,
                project_root=project_root,
                timeout=60,
            )

        # Verify process was killed
        mock_process.kill.assert_called_once()
