<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * Test class for \Magento\TestFramework\Bootstrap\Settings.
 */
namespace Magento\Test\Bootstrap;

class SettingsTest extends \PHPUnit\Framework\TestCase
{
    /**
     * @var \Magento\TestFramework\Bootstrap\Settings
     */
    protected $_object;

    /**
     * @var string
     */
    private static string $_fixtureDir;

    /**
     * Define the fixture directory to be used in both data providers and tests
     *
     * @param string|null $name
     * @param array $data
     * @param string $dataName
     */
    public function __construct($name = null, array $data = [], $dataName = '')
    {
        parent::__construct($name, $data, $dataName);
        self::$_fixtureDir = realpath(__DIR__ . '/_files') . '/';
    }

    protected function setUp(): void
    {
        $this->_object = new \Magento\TestFramework\Bootstrap\Settings(
            self::$_fixtureDir,
            [
                'item_label' => 'Item Label',
                'number_of_items' => 42,
                'item_price' => 12.99,
                'is_in_stock' => true,
                'free_shipping' => 'enabled',
                'zero_value' => '0',
                'test_file' => 'metrics.php',
                'all_xml_files' => '*.xml',
                'all_xml_or_one_php_file' => '{*.xml,4.php}',
                'one_xml_or_any_php_file' => '1.xml;?.php',
                'config_file_with_dist' => '1.xml',
                'config_file_no_dist' => '2.xml',
                'no_config_file_dist' => '3.xml'
            ]
        );
    }

    protected function tearDown(): void
    {
        $this->_object = null;
    }

    /**
     */
    public function testConstructorNonExistingBaseDir()
    {
        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage('Base path \'non_existing_dir\' has to be an existing directory.');

        new \Magento\TestFramework\Bootstrap\Settings('non_existing_dir', []);
    }

    /**
     * @param string $settingName
     * @param mixed $defaultValue
     * @param mixed $expectedResult
     * @dataProvider getDataProvider
     */
    public function testGet($settingName, $defaultValue, $expectedResult)
    {
        $this->assertSame($expectedResult, $this->_object->get($settingName, $defaultValue));
    }

    public static function getDataProvider(): array
    {
        return [
            'string type' => ['item_label', null, 'Item Label'],
            'integer type' => ['number_of_items', null, 42],
            'float type' => ['item_price', null, 12.99],
            'boolean type' => ['is_in_stock', null, true],
            'non-existing' => ['non_existing', null, null],
            'zero string' => ['zero_value', '1', '0'],
            'default value' => ['non_existing', 'default', 'default']
        ];
    }

    /**
     * @param string $settingName
     * @param bool $expectedResult
     * @dataProvider getAsBooleanDataProvider
     */
    public function testGetAsBoolean($settingName, $expectedResult)
    {
        $this->assertSame($expectedResult, $this->_object->getAsBoolean($settingName));
    }

    public static function getAsBooleanDataProvider(): array
    {
        return [
            'non-enabled string' => ['item_label', false],
            'non-enabled boolean' => ['is_in_stock', false],
            'enabled string' => ['free_shipping', true]
        ];
    }

    /**
     * @param string $settingName
     * @param mixed $defaultValue
     * @param string $expectedResult
     * @dataProvider getAsFileDataProvider
     */
    public function testGetAsFile($settingName, $defaultValue, $expectedResult)
    {
        $this->assertSame($expectedResult, $this->_object->getAsFile($settingName, $defaultValue));
    }

    public static function getAsFileDataProvider(): array
    {
        return [
            'existing file' => ['test_file', '', self::$_fixtureDir. 'metrics.php'],
            'zero value setting' => ['zero_value', 'default_should_be_ignored', self::$_fixtureDir. '0'],
            'empty default value' => ['non_existing_file', '', ''],
            'zero default value' => ['non_existing_file', '0', self::$_fixtureDir. '0'],
            'default value' => ['non_existing_file', 'metrics.php', self::$_fixtureDir. 'metrics.php']
        ];
    }

    /**
     * @param string $settingName
     * @param string $expectedResult
     * @dataProvider getAsMatchingPathsDataProvider
     */
    public function testGetAsMatchingPaths($settingName, $expectedResult)
    {
        $actualResult = $this->_object->getAsMatchingPaths($settingName);
        if (is_array($actualResult)) {
            sort($actualResult);
        }
        $this->assertEquals($expectedResult, $actualResult);
    }

    public static function getAsMatchingPathsDataProvider(): array
    {
        return [
            'single pattern' => [
                'all_xml_files',
                [self::$_fixtureDir. '1.xml', self::$_fixtureDir. '2.xml'],
            ],
            'pattern with braces' => [
                'all_xml_or_one_php_file',
                [self::$_fixtureDir. '1.xml', self::$_fixtureDir. '2.xml', self::$_fixtureDir. '4.php'],
            ],
            'multiple patterns' => [
                'one_xml_or_any_php_file',
                [self::$_fixtureDir. '1.xml', self::$_fixtureDir. '4.php'],
            ],
            'non-existing setting' => ['non_existing', []],
            'setting with zero value' => ['zero_value', [self::$_fixtureDir. '0']]
        ];
    }

    /**
     * @param string $settingName
     * @param mixed $expectedResult
     * @dataProvider getAsConfigFileDataProvider
     */
    public function testGetAsConfigFile($settingName, $expectedResult)
    {
        $actualResult = $this->_object->getAsConfigFile($settingName);
        if (is_array($actualResult)) {
            sort($actualResult);
        }
        $this->assertEquals($expectedResult, $actualResult);
    }

    public static function getAsConfigFileDataProvider(): array
    {
        return [
            'config file & dist file' => ['config_file_with_dist', self::$_fixtureDir. '1.xml'],
            'config file & no dist file' => ['config_file_no_dist', self::$_fixtureDir. '2.xml'],
            'no config file & dist file' => ['no_config_file_dist', self::$_fixtureDir. '3.xml.dist']
        ];
    }

    /**
     * @param string $settingName
     * @param string $expectedExceptionMsg
     * @dataProvider getAsConfigFileExceptionDataProvider
     */
    public function testGetAsConfigFileException($settingName, $expectedExceptionMsg)
    {
        $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
        $this->expectExceptionMessage((string)$expectedExceptionMsg);
        $this->_object->getAsConfigFile($settingName);
    }

    public static function getAsConfigFileExceptionDataProvider(): array
    {
        return [
            'non-existing setting' => [
                'non_existing',
                __("Setting 'non_existing' specifies the non-existing file ''."),
            ],
            'non-existing file' => [
                'item_label',
                __("Setting 'item_label' specifies the non-existing file '%1Item Label.dist'.", self::$_fixtureDir),
            ]
        ];
    }
}
