#!/usr/bin/env python3
"""
Unit tests for the builder module
"""

import os
import sys
import unittest
from unittest import mock
import tempfile
import shutil
import time
import threading

# Add the project directory to the path so we can import the module
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

from esppal.builder import CommandRunner, BuildRunner


class TestCommandRunner(unittest.TestCase):
    """Test cases for CommandRunner class"""
    
    def setUp(self):
        """Set up a temporary directory for test files"""
        self.test_dir = tempfile.mkdtemp()
        self.log_dir = os.path.join(self.test_dir, "logs")
        os.makedirs(self.log_dir, exist_ok=True)
    
    def tearDown(self):
        """Clean up the temporary directory"""
        shutil.rmtree(self.test_dir)
    
    @mock.patch('subprocess.Popen')
    def test_run_command(self, mock_popen):
        """Test running a command"""
        # Setup minimal mocking that doesn't rely on threading
        mock_process = mock.Mock()
        mock_process.poll.return_value = None  # Just return None for poll
        mock_popen.return_value = mock_process
        
        # Create command runner
        runner = CommandRunner(log_dir=self.log_dir)
        
        # Run a command
        result = runner.run(["echo", "test"])
        
        # Only verify the command was started correctly
        self.assertTrue(result)
        mock_popen.assert_called_once()
        args, kwargs = mock_popen.call_args
        self.assertEqual(args[0], ["echo", "test"])
    
    def test_terminate_command(self):
        """Test terminating a running command - simple version"""
        # Create a mock process
        mock_process = mock.Mock()
        
        # Create command runner with mocked process
        runner = CommandRunner(log_dir=self.log_dir)
        runner.process = mock_process
        runner.running = True
        
        # Call terminate
        runner.terminate()
        
        # Verify terminate was called
        mock_process.terminate.assert_called_once()


class TestBuildRunner(unittest.TestCase):
    """Test cases for BuildRunner class"""
    
    def setUp(self):
        """Set up a temporary directory for test files"""
        self.test_dir = tempfile.mkdtemp()
        self.log_dir = os.path.join(self.test_dir, "logs")
        os.makedirs(self.log_dir, exist_ok=True)
    
    def tearDown(self):
        """Clean up the temporary directory"""
        shutil.rmtree(self.test_dir)
    
    @mock.patch('subprocess.Popen')
    def test_build_command(self, mock_popen):
        """Test running a build command"""
        # Setup minimal mocking
        mock_process = mock.Mock()
        mock_process.poll.return_value = None  # Just return None for poll
        mock_popen.return_value = mock_process
        
        # Create build runner
        runner = BuildRunner(log_dir=self.log_dir)
        
        # Run a build command
        result = runner.build(["platformio", "run"])
        
        # Only verify the command was started correctly
        self.assertTrue(result)
        mock_popen.assert_called_once()
        args, kwargs = mock_popen.call_args
        self.assertEqual(args[0], ["platformio", "run"])
    
    @mock.patch('subprocess.Popen')
    def test_upload_command(self, mock_popen):
        """Test running an upload command"""
        # Setup minimal mocking
        mock_process = mock.Mock()
        mock_process.poll.return_value = None  # Just return None for poll
        mock_popen.return_value = mock_process
        
        # Create build runner
        runner = BuildRunner(log_dir=self.log_dir)
        
        # Run an upload command
        result = runner.upload(["platformio", "run", "--target", "upload"])
        
        # Only verify the command was started correctly
        self.assertTrue(result)
        mock_popen.assert_called_once()
        args, kwargs = mock_popen.call_args
        self.assertEqual(args[0], ["platformio", "run", "--target", "upload"])
    
    @mock.patch('subprocess.Popen')
    def test_clean_command(self, mock_popen):
        """Test running a clean command"""
        # Setup minimal mocking
        mock_process = mock.Mock()
        mock_process.poll.return_value = None  # Just return None for poll
        mock_popen.return_value = mock_process
        
        # Create build runner
        runner = BuildRunner(log_dir=self.log_dir)
        
        # Run a clean command
        result = runner.clean(["platformio", "run", "--target", "clean"])
        
        # Only verify the command was started correctly
        self.assertTrue(result)
        mock_popen.assert_called_once()
        args, kwargs = mock_popen.call_args
        self.assertEqual(args[0], ["platformio", "run", "--target", "clean"])
    
    @mock.patch('subprocess.Popen')
    def test_exit_callback(self, mock_popen):
        """Test exit callback setup"""
        # Simple test to verify callback is stored
        mock_process = mock.Mock()
        mock_process.poll.return_value = None
        mock_popen.return_value = mock_process
        
        def mock_callback(code):
            pass
        
        # Create build runner with exit callback
        runner = BuildRunner(log_dir=self.log_dir, exit_callback=mock_callback)
        
        # Verify the callback was stored
        self.assertEqual(runner.exit_callback, mock_callback)


if __name__ == '__main__':
    unittest.main()
