<?php

declare(strict_types=1);

/**
 * Descomplicar® Crescimento Digital
 * https://descomplicar.pt
 */

/**
 * TDD Test Suite Runner for Desk-Moloni v3.0
 * 
 * Runs tests in strict TDD order: Contract → Integration → Security → Performance → Unit → E2E
 * Ensures all tests fail initially before any implementation begins.
 */

require_once __DIR__ . '/bootstrap.php';

class TDDTestRunner
{
    private array $testSuites = [
        'contract' => [
            'name' => 'Contract Tests',
            'description' => 'API endpoint validation and database schema contracts',
            'required' => true,
            'order' => 1
        ],
        'integration' => [
            'name' => 'Integration Tests', 
            'description' => 'Synchronization workflows and external service integration',
            'required' => true,
            'order' => 2
        ],
        'security' => [
            'name' => 'Security Tests',
            'description' => 'Encryption, authentication, and vulnerability testing',
            'required' => true,
            'order' => 3
        ],
        'performance' => [
            'name' => 'Performance Tests',
            'description' => 'Queue processing, rate limiting, and benchmark testing',
            'required' => true,
            'order' => 4
        ],
        'unit' => [
            'name' => 'Unit Tests',
            'description' => 'Business logic validation and isolated component testing',
            'required' => true,
            'order' => 5
        ],
        'e2e' => [
            'name' => 'End-to-End Tests',
            'description' => 'Complete user workflow validation',
            'required' => true,
            'order' => 6
        ],
        'database' => [
            'name' => 'Database Tests',
            'description' => 'Database schema and constraint validation',
            'required' => true,
            'order' => 0
        ]
    ];
    
    private array $config;
    private bool $strictTDD;
    private bool $continueOnFailure;

    public function __construct(array $config = [])
    {
        $this->config = array_merge([
            'strict_tdd' => true,
            'continue_on_failure' => false,
            'coverage_threshold' => 100,
            'performance_benchmarks' => true,
            'security_audits' => true,
            'real_api_testing' => true,
            'parallel_execution' => false
        ], $config);
        
        $this->strictTDD = $this->config['strict_tdd'];
        $this->continueOnFailure = $this->config['continue_on_failure'];
    }

    public function runFullSuite(): array
    {
        echo "\n" . str_repeat("=", 80) . "\n";
        echo "DESK-MOLONI v3.0 TDD TEST SUITE\n";
        echo "Following Test-Driven Development Methodology\n";
        echo str_repeat("=", 80) . "\n\n";
        
        if ($this->strictTDD) {
            echo "🔴 STRICT TDD MODE: All tests MUST FAIL initially\n";
            echo "🟢 Implementation only begins after RED phase\n\n";
        }
        
        $results = [
            'suites' => [],
            'overall_success' => true,
            'total_tests' => 0,
            'total_failures' => 0,
            'total_errors' => 0,
            'total_skipped' => 0,
            'execution_time' => 0,
            'coverage_percentage' => 0,
            'tdd_compliance' => true
        ];
        
        $startTime = microtime(true);
        
        // Sort test suites by order
        uasort($this->testSuites, function($a, $b) {
            return $a['order'] <=> $b['order'];
        });
        
        foreach ($this->testSuites as $suite => $config) {
            echo "🧪 Running {$config['name']}...\n";
            echo "   {$config['description']}\n";
            
            $suiteResult = $this->runTestSuite($suite);
            $results['suites'][$suite] = $suiteResult;
            
            // Aggregate results
            $results['total_tests'] += $suiteResult['tests'];
            $results['total_failures'] += $suiteResult['failures'];
            $results['total_errors'] += $suiteResult['errors'];
            $results['total_skipped'] += $suiteResult['skipped'];
            
            // Check TDD compliance
            if ($this->strictTDD && $suiteResult['success']) {
                echo "   ⚠️  WARNING: Tests should FAIL in TDD red phase!\n";
                $results['tdd_compliance'] = false;
            }
            
            if (!$suiteResult['success']) {
                $results['overall_success'] = false;
                
                if (!$this->continueOnFailure && $config['required']) {
                    echo "   ❌ Critical test suite failed. Stopping execution.\n";
                    break;
                }
            }
            
            $this->displaySuiteResults($suiteResult);
            echo "\n";
        }
        
        $results['execution_time'] = microtime(true) - $startTime;
        
        // Generate coverage report
        if ($this->config['coverage_threshold'] > 0) {
            $results['coverage_percentage'] = $this->generateCoverageReport();
        }
        
        $this->displayOverallResults($results);
        
        return $results;
    }

    private function runTestSuite(string $suite): array
    {
        $command = sprintf(
            'cd %s && vendor/bin/phpunit --testsuite %s --log-junit reports/%s-junit.xml',
            escapeshellarg(dirname(__DIR__)),
            escapeshellarg($suite),
            escapeshellarg($suite)
        );
        
        if ($this->config['coverage_threshold'] > 0) {
            $command .= sprintf(' --coverage-clover reports/%s-coverage.xml', escapeshellarg($suite));
        }
        
        $output = [];
        $returnCode = 0;
        
        exec($command . ' 2>&1', $output, $returnCode);
        
        return $this->parseTestOutput($output, $returnCode);
    }

    private function parseTestOutput(array $output, int $returnCode): array
    {
        $result = [
            'success' => $returnCode === 0,
            'tests' => 0,
            'failures' => 0,
            'errors' => 0,
            'skipped' => 0,
            'time' => 0.0,
            'output' => implode("\n", $output)
        ];
        
        foreach ($output as $line) {
            // Parse PHPUnit output
            if (preg_match('/Tests: (\d+), Assertions: \d+/', $line, $matches)) {
                $result['tests'] = (int)$matches[1];
            }
            
            if (preg_match('/Failures: (\d+)/', $line, $matches)) {
                $result['failures'] = (int)$matches[1];
            }
            
            if (preg_match('/Errors: (\d+)/', $line, $matches)) {
                $result['errors'] = (int)$matches[1];
            }
            
            if (preg_match('/Skipped: (\d+)/', $line, $matches)) {
                $result['skipped'] = (int)$matches[1];
            }
            
            if (preg_match('/Time: ([0-9.]+)/', $line, $matches)) {
                $result['time'] = (float)$matches[1];
            }
        }
        
        return $result;
    }

    private function displaySuiteResults(array $result): void
    {
        $status = $result['success'] ? '✅ PASSED' : '❌ FAILED';
        $testsInfo = "Tests: {$result['tests']}, Failures: {$result['failures']}, Errors: {$result['errors']}";
        
        if ($result['skipped'] > 0) {
            $testsInfo .= ", Skipped: {$result['skipped']}";
        }
        
        echo "   {$status} - {$testsInfo} (Time: {$result['time']}s)\n";
        
        if (!$result['success']) {
            $errorLines = array_filter(explode("\n", $result['output']), function($line) {
                return strpos($line, 'FAIL') !== false || strpos($line, 'ERROR') !== false;
            });
            
            foreach (array_slice($errorLines, 0, 3) as $errorLine) {
                echo "   💥 " . trim($errorLine) . "\n";
            }
        }
    }

    private function generateCoverageReport(): float
    {
        echo "📊 Generating code coverage report...\n";
        
        $command = sprintf(
            'cd %s && vendor/bin/phpunit --testsuite unit,integration --coverage-html coverage --coverage-text',
            escapeshellarg(dirname(__DIR__))
        );
        
        $output = [];
        exec($command . ' 2>&1', $output);
        
        // Parse coverage percentage from output
        $coveragePercentage = 0;
        foreach ($output as $line) {
            if (preg_match('/Lines:\s+([0-9.]+)%/', $line, $matches)) {
                $coveragePercentage = (float)$matches[1];
                break;
            }
        }
        
        return $coveragePercentage;
    }

    private function displayOverallResults(array $results): void
    {
        echo str_repeat("=", 80) . "\n";
        echo "OVERALL TEST RESULTS\n";
        echo str_repeat("=", 80) . "\n";
        
        $status = $results['overall_success'] ? '✅ PASSED' : '❌ FAILED';
        echo "Status: {$status}\n";
        echo "Total Tests: {$results['total_tests']}\n";
        echo "Failures: {$results['total_failures']}\n";
        echo "Errors: {$results['total_errors']}\n";
        echo "Skipped: {$results['total_skipped']}\n";
        echo "Execution Time: " . number_format($results['execution_time'], 2) . "s\n";
        
        if ($results['coverage_percentage'] > 0) {
            $coverageStatus = $results['coverage_percentage'] >= $this->config['coverage_threshold'] ? '✅' : '❌';
            echo "Code Coverage: {$coverageStatus} {$results['coverage_percentage']}% (Target: {$this->config['coverage_threshold']}%)\n";
        }
        
        // TDD Compliance Check
        if ($this->strictTDD) {
            $tddStatus = $results['tdd_compliance'] ? '❌ NOT COMPLIANT' : '✅ COMPLIANT';
            echo "TDD Compliance: {$tddStatus}\n";
            
            if (!$results['tdd_compliance']) {
                echo "\n⚠️  TDD VIOLATION: Some tests passed when they should fail in RED phase\n";
                echo "🔴 All tests must FAIL before implementation begins\n";
            } else {
                echo "\n🔴 Perfect! All tests failed as expected in TDD RED phase\n";
                echo "🟢 Now proceed with implementation to make tests pass\n";
            }
        }
        
        echo "\n";
        $this->displayNextSteps($results);
    }

    private function displayNextSteps(array $results): void
    {
        echo "NEXT STEPS:\n";
        echo str_repeat("-", 40) . "\n";
        
        if ($this->strictTDD && $results['tdd_compliance']) {
            echo "1. ✅ RED phase complete - All tests are failing as expected\n";
            echo "2. 🟢 Begin GREEN phase - Implement minimal code to make tests pass\n";
            echo "3. 🔵 REFACTOR phase - Improve code quality while keeping tests green\n";
            echo "4. 🔄 Repeat TDD cycle for next feature\n";
        } elseif (!$results['overall_success']) {
            // Analyze which implementations are needed
            $failedSuites = array_filter($results['suites'], function($suite) {
                return !$suite['success'];
            });
            
            echo "Required implementations based on failing tests:\n";
            foreach ($failedSuites as $suiteName => $suite) {
                echo "   • {$this->testSuites[$suiteName]['name']}\n";
            }
        } else {
            echo "1. ✅ All tests are passing\n";
            echo "2. 📊 Review code coverage report\n";
            echo "3. 🔍 Consider additional edge cases\n";
            echo "4. 📝 Update documentation\n";
        }
        
        echo "\nTest reports available at:\n";
        echo "   • HTML Coverage: " . dirname(__DIR__) . "/coverage/index.html\n";
        echo "   • JUnit Reports: " . dirname(__DIR__) . "/tests/reports/\n";
    }
}

// Run the test suite
if (basename(__FILE__) === basename($_SERVER['SCRIPT_NAME'])) {
    $config = [
        'strict_tdd' => isset($_SERVER['argv']) && in_array('--strict-tdd', $_SERVER['argv']),
        'continue_on_failure' => isset($_SERVER['argv']) && in_array('--continue', $_SERVER['argv']),
        'coverage_threshold' => 100,
        'real_api_testing' => !isset($_SERVER['argv']) || !in_array('--no-api', $_SERVER['argv'])
    ];
    
    $runner = new TDDTestRunner($config);
    $results = $runner->runFullSuite();
    
    // Exit with appropriate code
    exit($results['overall_success'] ? 0 : 1);
}