<?php

namespace Consolidation\Filter;

use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Output\BufferedOutput;

class OpCommandsTest extends TestCase
{
    /** @var string[] */
    protected $commandClasses;

    /** @var string */
    protected $appName;

    /** @var string */
    protected $appVersion;

    const STATUS_OK = 0;
    const STATUS_ERROR = 1;

    /**
     * Instantiate a new runner
     */
    public function setUp()
    {
        // Store the command classes we are going to test
        $this->commandClasses = [ \Consolidation\Filter\Cli\OpCommands::class, \Consolidation\Filter\Hooks\FilterHooks::class ];

        // Define our invariants for our test
        $this->appName = 'DotProcessFixtureApp';
        $this->appVersion = '1.0.1';
    }

    /**
     * Data provider for testExample.
     *
     * Return an array of arrays, each of which contains the parameter
     * values to be used in one invocation of the testExample test function.
     */
    public function exampleTestCommandParameters()
    {
        return [

            [
                'a=b', self::STATUS_OK,
                'parse', 'a=b',
            ],

            [
                '!a=b', self::STATUS_OK,
                'parse', 'a!=b',
            ],

            [
                'a=b&&c=d||x=y', self::STATUS_OK,
                'parse', 'a=b&&c=d||x=y',
            ],

            [
                'Could not parse expression a', self::STATUS_ERROR,
                'parse', 'a&&b',
            ],

            [
                'a:
  color: red
  shape: round', self::STATUS_OK,
                'edit', 'tests/fixtures/data.yml', '--filter=color=red',
            ],

            [
                'b:
  color: blue
  shape: square', self::STATUS_OK,
                'edit', 'tests/fixtures/data.yml', '--filter=blue',
            ],

        ];
    }

    /**
     * Test our example class. Each time this function is called, it will
     * be passed data from the data provider function idendified by the
     * dataProvider annotation.
     *
     * @dataProvider exampleTestCommandParameters
     */
    public function testExampleCommands($expectedOutput, $expectedStatus, $variable_args)
    {
        // Create our argv array and run the command
        $argv = $this->argv(func_get_args());
        list($actualOutput, $statusCode) = $this->execute($argv);

        // Confirm that our output and status code match expectations
        $this->assertContains($expectedOutput, $actualOutput);
        $this->assertEquals($expectedStatus, $statusCode);
    }

    /**
     * Prepare our $argv array; put the app name in $argv[0] followed by
     * the command name and all command arguments and options.
     */
    protected function argv($functionParameters)
    {
        $argv = $functionParameters;
        array_shift($argv);
        array_shift($argv);
        array_unshift($argv, $this->appName);

        return $argv;
    }

    /**
     * Simulated front controller
     */
    protected function execute($argv)
    {
        // Define a global output object to capture the test results
        $output = new BufferedOutput();

        // We can only call `Runner::execute()` once; then we need to tear down.
        $runner = new \Robo\Runner($this->commandClasses);
        $statusCode = $runner->execute($argv, $this->appName, $this->appVersion, $output);
        \Robo\Robo::unsetContainer();

        // Return the output and status code.
        $actualOutput = trim($output->fetch());
        return [$actualOutput, $statusCode];
    }
}
