<?php

declare(strict_types=1);

use App\Framework\Console\ExitCode;
use App\Framework\Filesystem\ValueObjects\FilePath;
use App\Framework\Logging\Logger;
use App\Framework\Process\SystemProcess;
use App\Framework\Process\ValueObjects\Command;
use App\Framework\Process\ValueObjects\EnvironmentVariables;

beforeEach(function () {
    $this->logger = Mockery::mock(Logger::class);
    $this->logger->shouldReceive('debug')->andReturn(null);

    $this->process = new SystemProcess($this->logger);
});

afterEach(function () {
    Mockery::close();
});

describe('Process Integration - Real World Scenarios', function () {
    it('can execute multi-line script', function () {
        $command = Command::fromString('
            echo "line1"
            echo "line2"
            echo "line3"
        ');

        $result = $this->process->run($command);

        expect($result->isSuccess())->toBeTrue()
            ->and($result->stdout)->toContain('line1')
            ->and($result->stdout)->toContain('line2')
            ->and($result->stdout)->toContain('line3');
    });

    it('can create and read temporary file', function () {
        $tempFile = FilePath::temp('process_test.txt');
        $command = Command::fromString(
            "echo 'test content' > {$tempFile->toString()} && cat {$tempFile->toString()}"
        );

        $result = $this->process->run($command);

        expect($result->isSuccess())->toBeTrue()
            ->and($result->stdout)->toContain('test content');

        // Cleanup
        if ($tempFile->exists()) {
            unlink($tempFile->toString());
        }
    });

    it('can pipe commands', function () {
        $command = Command::fromString('echo "hello world" | grep "world"');

        $result = $this->process->run($command);

        expect($result->isSuccess())->toBeTrue()
            ->and($result->stdout)->toContain('world');
    });

    it('handles complex environment variable substitution', function () {
        $env = EnvironmentVariables::fromArray([
            'PREFIX' => 'test',
            'SUFFIX' => 'value',
        ]);

        $command = Command::fromString('echo "$PREFIX-$SUFFIX"');

        $result = $this->process->run(
            command: $command,
            env: $env
        );

        expect($result->stdout)->toContain('test-value');
    });

    it('can execute PHP script', function () {
        $phpCode = 'echo json_encode(["status" => "ok", "pid" => getmypid()]);';
        $command = Command::fromArray(['php', '-r', $phpCode]);

        $result = $this->process->run($command);

        expect($result->isSuccess())->toBeTrue();

        $output = json_decode($result->stdout, true);
        expect($output)->toBeArray()
            ->and($output['status'])->toBe('ok')
            ->and($output['pid'])->toBeGreaterThan(0);
    });

    it('handles large output', function () {
        // Generate 1000 lines of output
        $command = Command::fromString('for i in {1..1000}; do echo "Line $i"; done');

        $result = $this->process->run($command);

        expect($result->isSuccess())->toBeTrue();

        $lines = explode("\n", trim($result->stdout));
        expect(count($lines))->toBeGreaterThanOrEqual(1000);
    });

    it('preserves exit codes correctly', function () {
        $testCases = [
            ['exit 0', ExitCode::SUCCESS],
            ['exit 1', ExitCode::GENERAL_ERROR],
            ['exit 2', ExitCode::USAGE_ERROR],
            ['exit 126', ExitCode::PERMISSION_DENIED],
        ];

        foreach ($testCases as [$cmd, $expectedCode]) {
            $result = $this->process->run(Command::fromString($cmd));
            expect($result->exitCode)->toBe($expectedCode);
        }
    });
});

describe('Process Integration - Error Handling', function () {
    it('handles command not found', function () {
        $result = $this->process->run(
            Command::fromString('nonexistent_command_xyz_123')
        );

        expect($result->isFailed())->toBeTrue()
            ->and($result->hasErrors())->toBeTrue();
    });

    it('handles permission denied', function () {
        // Try to write to root directory (should fail)
        $command = Command::fromString('touch /root/test_file.txt');

        $result = $this->process->run($command);

        expect($result->isFailed())->toBeTrue();
    });

    it('handles syntax errors in shell commands', function () {
        $command = Command::fromString('echo "unclosed string');

        $result = $this->process->run($command);

        expect($result->isFailed())->toBeTrue();
    });
});

describe('Process Integration - Performance', function () {
    it('can execute multiple processes sequentially', function () {
        $startTime = microtime(true);

        for ($i = 0; $i < 5; $i++) {
            $result = $this->process->run(
                Command::fromArray(['echo', "iteration {$i}"])
            );

            expect($result->isSuccess())->toBeTrue();
        }

        $totalTime = microtime(true) - $startTime;

        // Should complete 5 simple commands in under 2 seconds
        expect($totalTime)->toBeLessThan(2.0);
    });

    it('tracks runtime accurately', function () {
        $sleepTime = 0.2; // 200ms
        $result = $this->process->run(
            Command::fromArray(['sleep', (string) $sleepTime])
        );

        $runtime = $result->runtime->toSeconds();

        // Runtime should be approximately the sleep time (with some tolerance)
        expect($runtime)->toBeGreaterThanOrEqual($sleepTime)
            ->and($runtime)->toBeLessThan($sleepTime + 0.1);
    });
});

describe('Process Integration - Async Process Management', function () {
    it('can manage multiple async processes', function () {
        $processes = [];

        // Start 3 processes
        for ($i = 0; $i < 3; $i++) {
            $processes[] = $this->process->start(
                Command::fromArray(['sleep', '0.1'])
            );
        }

        // All should be running
        foreach ($processes as $proc) {
            expect($proc->isRunning())->toBeTrue();
        }

        // Wait for all
        foreach ($processes as $proc) {
            $result = $proc->wait();
            expect($result->isSuccess())->toBeTrue();
        }
    });

    it('can capture output from async process', function () {
        $running = $this->process->start(
            Command::fromArray(['echo', 'async output'])
        );

        $result = $running->wait();

        expect($result->stdout)->toContain('async output')
            ->and($result->isSuccess())->toBeTrue();
    });
});
