#!/usr/bin/env python3

import os
import sys
import json
import pytest
from unittest.mock import patch, MagicMock, mock_open, call
from pathlib import Path

# Add parent directory to path so we can import check_package_builds
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
from check_package_builds import build_package_test, load_package_config, main

class TestCheckPackageBuilds:
    
    def test_load_package_config(self):
        """Test loading package configuration from YAML file."""
        mock_yaml_content = """
        packages:
          test-package:
            github_repo: "owner/repo"
            version_method: "release"
            pkgbuild_path: "test-package/PKGBUILD"
        """
        
        with patch('builtins.open', mock_open(read_data=mock_yaml_content)) as mock_file:
            result = load_package_config()
            
            assert 'test-package' in result
            assert result['test-package']['github_repo'] == 'owner/repo'
    
    def test_package_build_success(self):
        """Test successful package build with makepkg."""
        with patch('subprocess.run') as mock_run, \
             patch('os.makedirs') as mock_makedirs:
            
            # Configure mocks for successful clone and build
            clone_success = MagicMock()
            clone_success.returncode = 0
            clone_success.stdout = "Cloning into 'test-package'..."
            clone_success.stderr = ""
            
            build_success = MagicMock()
            build_success.returncode = 0
            build_success.stdout = "==> Making package: test-package...\n==> Checking runtime dependencies...\n==> Finished"
            build_success.stderr = ""
            
            cleanup_process = MagicMock()
            
            # Set up the mock to return different values on consecutive calls
            mock_run.side_effect = [clone_success, build_success, cleanup_process]
            
            # Test the package build function
            result = build_package_test('test-package')
            
            # Verify the result
            assert result['success'] == True
            assert 'Build successful' in result['log']
            
            # Verify git clone was called
            first_call_args = mock_run.call_args_list[0][0][0]
            assert 'git' == first_call_args[0]
            assert 'clone' == first_call_args[1]
            
            # Verify makepkg was called
            second_call_args = mock_run.call_args_list[1][0][0]
            assert 'makepkg' == second_call_args[0]
    
    def test_package_build_fallback(self):
        """Test fallback to skippgpcheck when regular build fails."""
        with patch('subprocess.run') as mock_run, \
             patch('os.makedirs') as mock_makedirs:
            
            # Configure mocks for clone, failed build, successful fallback
            clone_success = MagicMock()
            clone_success.returncode = 0
            clone_success.stdout = "Cloning into 'test-package'..."
            clone_success.stderr = ""
            
            build_fail = MagicMock()
            build_fail.returncode = 1
            build_fail.stdout = ""
            build_fail.stderr = "==> ERROR: One or more PGP signatures could not be verified!"
            
            fallback_success = MagicMock()
            fallback_success.returncode = 0
            fallback_success.stdout = "==> Making package: test-package...\n==> Checking runtime dependencies...\n==> Finished"
            fallback_success.stderr = ""
            
            cleanup_process = MagicMock()
            
            # Configure mock to return different results on consecutive calls
            mock_run.side_effect = [
                clone_success,      # Clone succeeds
                build_fail,         # First build fails
                fallback_success,   # Fallback build succeeds  
                cleanup_process     # Cleanup
            ]
            
            # Test the package build function
            result = build_package_test('test-package')
            
            # Verify the result
            assert result['success'] == True
            assert 'First build attempt failed' in result['log']
            assert '--skippgpcheck' in result['log']
            
            # Verify all three commands were called
            assert mock_run.call_count >= 3
            
            # Check first call (git clone)
            args1 = mock_run.call_args_list[0][0][0]
            assert 'clone' in args1
            
            # Check second call (first build attempt)
            args2 = mock_run.call_args_list[1][0][0]
            assert 'makepkg' in args2
            
            # Check third call (fallback build)
            args3 = mock_run.call_args_list[2][0][0]
            assert '--skippgpcheck' in args3
    
    def test_package_build_failure(self):
        """Test package build failure detection."""
        with patch('subprocess.run') as mock_run, \
             patch('os.makedirs') as mock_makedirs:
            
            # Configure mocks for clone and failed builds
            clone_success = MagicMock()
            clone_success.returncode = 0
            clone_success.stdout = "Cloning into 'test-package'..."
            clone_success.stderr = ""
            
            build_fail = MagicMock()
            build_fail.returncode = 1
            build_fail.stdout = ""
            build_fail.stderr = "error: target not found: some-dependency"
            
            fallback_fail = MagicMock()
            fallback_fail.returncode = 1
            fallback_fail.stdout = ""
            fallback_fail.stderr = "error: target not found: some-dependency"
            
            cleanup_process = MagicMock()
            
            # Configure mocks for consecutive calls
            mock_run.side_effect = [
                clone_success,    # Clone succeeds
                build_fail,       # First build fails
                fallback_fail,    # Fallback build also fails
                cleanup_process   # Cleanup
            ]
            
            # Test the package build function
            result = build_package_test('test-package')
            
            # Verify the result
            assert result['success'] == False
            assert result['error'] == "Dependency not found"
    
    def test_main_function(self):
        """Test main function that processes multiple packages."""
        mock_packages = ['package1', 'package2']
        
        with patch('sys.argv', ['check_package_builds.py'] + mock_packages), \
             patch('check_package_builds.build_package_test') as mock_test_build, \
             patch('check_package_builds.write_results') as mock_write, \
             patch('check_package_builds.load_package_config') as mock_load_config, \
             patch('sys.exit') as mock_exit:  # Mock sys.exit to prevent test from exiting
            
            # Configure test_package_build to return success for package1 and failure for package2
            mock_results = [
                {'package': 'package1', 'success': True, 'error': "", 'log': "Build succeeded"},
                {'package': 'package2', 'success': False, 'error': "Dependency not found", 'log': "Build failed"}
            ]
            mock_test_build.side_effect = mock_results
            
            # Mock package config
            mock_load_config.return_value = {
                'package1': {'github_repo': 'owner/repo1'},
                'package2': {'github_repo': 'owner/repo2'}
            }
            
            # Run main
            main()
            
            # Verify test_package_build was called for both packages
            mock_test_build.assert_has_calls([
                call('package1'),
                call('package2')
            ])
            
            # Verify write_results was called with both results
            mock_write.assert_called_once()
            args, _ = mock_write.call_args
            assert len(args[0]) == 2  # Two results
            assert args[0][0]['package'] == 'package1'
            assert args[0][1]['package'] == 'package2'
            
            # Verify sys.exit was called with code 1 due to failed package
            mock_exit.assert_called_with(1) 