<?PHP
/**
 * Tests for MD_STD_IN.
 *
 * @author Joshua Ramon Enslin <joshua@museum-digital.de>
 */
declare(strict_types = 1);

use PHPUnit\Framework\TestCase;

/**
 * Tests for MD_STD_IN.
 */
final class MD_STD_IN_Test extends TestCase {
    /**
     * Data provider for valid IDs.
     *
     * @return array<array{0: mixed, 1: int}>
     */
    public static function valid_id_provider():array {

        $values = [
            [1, 1],
            ["1", 1],
            ["1111111", 1111111],
        ];

        $output = [];
        foreach ($values as $value) {
            $output[gettype($value[0]) . ': ' . var_export($value[0], true)] = $value;
        }
        return $output;

    }

    /**
     * Function for testing sanitize_id().
     *
     * @small
     * @covers \MD_STD_IN::sanitize_id
     * @dataProvider \MD_STD_IN_Test::valid_id_provider
     *
     * @param mixed   $to_validate Input to validate.
     * @param integer $expected    Expected output.
     *
     * @return void
     */
    public function test_sanitize_id_works(mixed $to_validate, int $expected):void {
        self::assertEquals($expected, MD_STD_IN::sanitize_id($to_validate));
    }

    /**
     * Data provider for valid IDs.
     *
     * @return array<array{0: mixed}>
     */
    public static function invalid_id_provider():array {

        $output = self::invalid_id_provider_without_zero();
        $output['Number 0'] = [0];
        return $output;

    }

    /**
     * Function for testing sanitize_id().
     *
     * @small
     * @covers \MD_STD_IN::sanitize_id
     * @dataProvider \MD_STD_IN_Test::invalid_id_provider
     *
     * @param mixed $to_validate Input to validate.
     *
     * @return void
     */
    public function test_sanitize_id_fails(mixed $to_validate):void {

        self::expectException(MDpageParameterNotNumericException::class);
        MD_STD_IN::sanitize_id($to_validate);

    }
    /**
     * Data provider for valid IDs.
     *
     * @return array<array{0: mixed, 1: int}>
     */
    public static function valid_id_or_zero_provider():array {

        $output = self::valid_id_provider();
        $output['Integer 0'] = [0, 0];
        $output['String 0'] = [0, 0];
        return $output;

    }

    /**
     * Function for testing sanitize_id_or_zero().
     *
     * @small
     * @covers \MD_STD_IN::sanitize_id_or_zero
     * @dataProvider \MD_STD_IN_Test::valid_id_or_zero_provider
     *
     * @param mixed   $to_validate Input to validate.
     * @param integer $expected    Expected output.
     *
     * @return void
     */
    public function test_sanitize_id_or_zero_works(mixed $to_validate, int $expected):void {

        self::assertEquals($expected, MD_STD_IN::sanitize_id_or_zero($to_validate));

    }

    /**
     * Data provider for valid IDs.
     *
     * @return array<array{0: mixed}>
     */
    public static function invalid_id_provider_without_zero():array {

        return [
            'Number too high' => [1000000000000000000000000000000000000],
            'Number with character in the middle' => ["1a2"],
            'Number with suffixed string' => ["12a"],
            'String character' => ["a"],
        ];

    }

    /**
     * Function for testing sanitize_id_or_zero().
     *
     * @small
     * @covers \MD_STD_IN::sanitize_id_or_zero
     * @dataProvider \MD_STD_IN_Test::invalid_id_provider_without_zero
     *
     * @param mixed $to_validate Input to validate.
     *
     * @return void
     */
    public function test_sanitize_id_or_zero_fails(mixed $to_validate):void {

        self::expectException(MDpageParameterNotNumericException::class);
        MD_STD_IN::sanitize_id_or_zero($to_validate);

    }

    /**
     * Data provider for text with its expected cleaned values.
     *
     * @return array<array{0: mixed, 1: string}>
     */
    public static function text_with_expected_return_value_provider():array {

        return [
            'Empty string' => ['', ''],
            'Integer 0' => [0, '0'],
            'String 0' => ['0', '0'],
            'Regular string' => ['a', 'a'],
            'String with double whitespace in between' => ['a  a', 'a a'],
            'String to be trimmed (spaces)' => ['a   ', 'a'],
            'String to be trimmed (newline)' => ['a   ' . PHP_EOL, 'a'],
            'Empty array' => [[], ''],
            'Array with content' => [['test' => 'test'], ''],
        ];

    }

    /**
     * Function for testing sanitize_text().
     *
     * @small
     * @covers \MD_STD_IN::sanitize_text
     * @dataProvider \MD_STD_IN_Test::text_with_expected_return_value_provider
     *
     * @param mixed  $to_validate Input to validate.
     * @param string $expected    Expected output.
     *
     * @return void
     */
    public function test_sanitize_text(mixed $to_validate, string $expected):void {

        self::assertEquals($expected, MD_STD_IN::sanitize_text($to_validate));

    }

    /**
     * Data provider for working RGB colors.
     *
     * @return array<array{0: mixed, 1: string}>
     */
    public static function valid_rgb_colors_provider():array {

        return [
            'Three character version' => ['AAA', 'AAA'],
            'Three character version (int)' => ['111', '111'],
            'Three character version (mixed)' => ['1a1', '1A1'],
            'Six character version' => ['AAAAAA', 'AAAAAA'],
            'Six character version (int)' => ['111111', '111111'],
            'Six character version (mixed)' => ['1a1AAA', '1A1AAA'],
        ];

    }

    /**
     * Data provider for strings that are not rgb colors.
     *
     * @return array<array{0: mixed}>
     */
    public static function invalid_rgb_colors_provider():array {

        $output = [
            'Array' => [[]],
            'Three characters, but invalid ones' => ['ZZZ'],
            'Six characters, but invalid ones' => ['111ZZZ'],
            'Three characters, but with spaces' => ['ZZZ  '],
        ];

        for ($i = 0; $i++; $i < 10) {
            if ($i === 3 || $i === 6) continue;
            $output['Valid characters repeated ' . $i . ' times'] = [$i];
        }

        return $output;

    }

    /**
     * Function for testing sanitize_rgb_color().
     *
     * @small
     * @covers \MD_STD_IN::sanitize_rgb_color
     * @dataProvider \MD_STD_IN_Test::valid_rgb_colors_provider
     *
     * @param mixed  $to_validate Input to validate.
     * @param string $expected    Expected output.
     *
     * @return void
     */
    public function test_sanitize_rgb_color_works(mixed $to_validate, string $expected):void {

        self::assertEquals($expected, MD_STD_IN::sanitize_rgb_color($to_validate));

    }

    /**
     * Function for testing sanitize_rgb_color()'s failure modes.
     *
     * @small
     * @covers \MD_STD_IN::sanitize_rgb_color
     * @dataProvider \MD_STD_IN_Test::invalid_rgb_colors_provider
     *
     * @param mixed $to_validate Input to validate.
     *
     * @return void
     */
    public function test_sanitize_rgb_color_fails(mixed $to_validate):void {

        self::expectException(MDInvalidColorCode::class);
        MD_STD_IN::sanitize_rgb_color($to_validate);

    }

    /**
     * Function for testing sanitize_id_array().
     *
     * @small
     * @covers \MD_STD_IN::sanitize_id_array
     *
     * @return void
     */
    public function test_sanitize_id_array():void {

        self::assertEquals([1], MD_STD_IN::sanitize_id_array([1]));
        self::assertEquals([1, 2], MD_STD_IN::sanitize_id_array(["1", 2]));

        self::expectException(MDpageParameterNotNumericException::class);
        MD_STD_IN::sanitize_id_array([0]);
        self::expectException(MDpageParameterNotNumericException::class);
        MD_STD_IN::sanitize_id_array([0, 1]);
        self::expectException(MDpageParameterNotNumericException::class);
        MD_STD_IN::sanitize_id_array([100000000000000000000000000000]);
        self::expectException(MDpageParameterNotNumericException::class);
        MD_STD_IN::sanitize_id_array(["1a2"]);
        self::expectException(MDpageParameterNotNumericException::class);
        MD_STD_IN::sanitize_id_array(["12a"]);
        self::expectException(MDpageParameterNotNumericException::class);
        MD_STD_IN::sanitize_id_array(["a"]);

    }

    /**
     * Function for testing get_http_input_text().
     *
     * @small
     * @covers \MD_STD_IN::get_http_input_text
     *
     * @return void
     */
    public function test_get_http_input_text():void {

        $_GET['test'] = "a";
        self::assertEquals("a", MD_STD_IN::get_http_input_text("test"));
        unset($_GET['test']);

        $_POST['test'] = "a";
        self::assertEquals("a", MD_STD_IN::get_http_input_text("test"));
        unset($_POST['test']);

        $_POST['test'] = [];
        self::assertEquals("", MD_STD_IN::get_http_input_text("test"));
        unset($_POST['test']);

        self::assertEquals("", MD_STD_IN::get_http_input_text("test"));

        self::expectException(MDpageParameterNotFromListException::class);
        MD_STD_IN::get_http_input_text("a", "", ['a']);


    }

    /**
     * Function for testing get_http_post_text().
     *
     * @small
     * @covers \MD_STD_IN::get_http_post_text
     *
     * @return void
     */
    public function test_get_http_post_text():void {

        $_POST['test'] = "a";
        self::assertEquals("a", MD_STD_IN::get_http_post_text("test"));
        unset($_POST['test']);

        $_POST['test'] = [];
        self::assertEquals("", MD_STD_IN::get_http_post_text("test"));
        unset($_POST['test']);

        self::assertEquals("", MD_STD_IN::get_http_post_text("test"));

        self::expectException(MDpageParameterNotFromListException::class);
        MD_STD_IN::get_http_post_text("a", "", ['a']);


    }

    /**
     * Function for testing sanitize_url().
     *
     * @small
     * @covers \MD_STD_IN::sanitize_url
     *
     * @return void
     */
    public function test_sanitize_url_with_empty_string():void {

        // Ensure empty inputs return empty output
        self::assertEquals("", MD_STD_IN::sanitize_url(""));

    }

    /**
     * Function for testing sanitize_url().
     *
     * @small
     * @covers \MD_STD_IN::sanitize_url
     * @dataProvider \MD_STD_TEST_PROVIDERS::valid_url_provider
     *
     * @param string $to_validate Input to validate.
     * @param string $expected    Expected output.
     *
     * @return void
     */
    public function test_sanitize_url_works(string $to_validate, string $expected):void {

        self::assertEquals($expected, MD_STD_IN::sanitize_url($to_validate));

    }

    /**
     * Function for testing sanitize_url().
     *
     * @small
     * @covers \MD_STD_IN::sanitize_url
     * @dataProvider \MD_STD_TEST_PROVIDERS::invalid_url_provider
     *
     * @param string $to_validate Input to validate.
     *
     *
     * @return void
     */
    public function test_sanitize_url_fails(string $to_validate):void {

        self::expectException(MDInvalidUrl::class);
        MD_STD_IN::sanitize_url($to_validate);

    }

    /**
     * Function for testing sanitize_email().
     *
     * @small
     * @covers \MD_STD_IN::sanitize_email
     *
     * @return void
     */
    public function test_sanitize_email_empty():void {

        self::assertEquals("", MD_STD_IN::sanitize_email(""));

    }

    /**
     * Function for testing sanitize_email().
     *
     * @small
     * @covers \MD_STD_IN::sanitize_email
     * @dataProvider \MD_STD_TEST_PROVIDERS::valid_email_provider
     *
     * @param string $to_validate Input to validate.
     * @param string $expected    Expected output.
     *
     * @return void
     */
    public function test_sanitize_email_works(string $to_validate, string $expected):void {
        self::assertEquals($expected, MD_STD_IN::sanitize_email($to_validate));
    }

    /**
     * Function for testing sanitize_email() fails when it should.
     *
     * @small
     * @covers \MD_STD_IN::sanitize_email
     * @dataProvider \MD_STD_TEST_PROVIDERS::invalid_email_provider
     *
     * @param string $to_validate Input to validate.
     *
     * @return void
     */
    public function test_sanitize_email_fails(string $to_validate):void {

        self::expectException(MDInvalidEmail::class);
        MD_STD_IN::sanitize_email($to_validate);

    }

    /**
     * Function for testing validate_password().
     *
     * @small
     * @covers \MD_STD_IN::validate_password
     *
     * @return void
     */
    public function test_validate_password():void {

        self::assertEquals(['password_too_short', 'password_has_no_number_no_special_char'], MD_STD_IN::validate_password("a"));
        self::assertEquals(['password_has_no_number_no_special_char'], MD_STD_IN::validate_password("aaaaaaaaaaaaaaaaaaaa"));
        self::assertEquals(['password_too_short'], MD_STD_IN::validate_password("!a323!"));
        self::assertEquals([], MD_STD_IN::validate_password("!a324324324123!"));

    }

    /**
     * Function for testing validate_phone_number().
     *
     * @small
     * @covers \MD_STD_IN::validate_phone_number
     *
     * @return void
     */
    public function test_validate_phone_number():void {

        self::assertEquals("", MD_STD_IN::validate_phone_number(""));
        self::assertEquals("+1932-1321123", MD_STD_IN::validate_phone_number("+1932-1321123"));
        self::assertEquals("+49 (030) 21321123", MD_STD_IN::validate_phone_number("+49 (030) 21321123"));

        self::expectException(MDgenericInvalidInputsException::class);
        MD_STD_IN::validate_phone_number("test@example.org");

        self::expectException(MDgenericInvalidInputsException::class);
        MD_STD_IN::validate_phone_number("+123456789 z");

    }

    /**
     * Function for testing sanitize_float().
     *
     * @small
     * @covers \MD_STD_IN::sanitize_float
     *
     * @return void
     */
    public function test_sanitize_float():void {

        self::assertEquals(0, MD_STD_IN::sanitize_float("0"));
        self::assertEquals(12, MD_STD_IN::sanitize_float("12"));
        self::assertEquals(12.12, MD_STD_IN::sanitize_float("12.12"));
        self::assertEquals(12.12, MD_STD_IN::sanitize_float("12,12"));

        self::expectException(MDgenericInvalidInputsException::class);
        MD_STD_IN::sanitize_float("test");
        self::expectException(MDgenericInvalidInputsException::class);
        MD_STD_IN::sanitize_float("");

    }

    /**
     * Function for testing validate_longitude().
     *
     * @small
     * @covers \MD_STD_IN::validate_longitude
     *
     * @return void
     */
    public function test_validate_longitude():void {

        self::assertEquals(0, MD_STD_IN::validate_longitude("0"));
        self::assertEquals(12, MD_STD_IN::validate_longitude("12"));
        self::assertEquals(12, MD_STD_IN::validate_longitude(12));
        self::assertEquals(12.12, MD_STD_IN::validate_longitude("12.12"));
        self::assertEquals(12.12, MD_STD_IN::validate_longitude("12,12"));
        self::assertEquals(12.12, MD_STD_IN::validate_longitude(12.12));

        self::expectException(MDgenericInvalidInputsException::class);
        MD_STD_IN::validate_longitude("test");
        self::expectException(MDgenericInvalidInputsException::class);
        MD_STD_IN::validate_longitude("");
        self::expectException(MDCoordinateOutOfRange::class);
        MD_STD_IN::validate_longitude("1900");
        self::expectException(MDCoordinateOutOfRange::class);
        MD_STD_IN::validate_longitude(1900);
        self::expectException(MDCoordinateOutOfRange::class);
        MD_STD_IN::validate_longitude(-1900);

    }

    /**
     * Function for testing validate_latitude().
     *
     * @small
     * @covers \MD_STD_IN::validate_latitude
     *
     * @return void
     */
    public function test_validate_latitude():void {

        self::assertEquals(0, MD_STD_IN::validate_latitude("0"));
        self::assertEquals(12, MD_STD_IN::validate_latitude("12"));
        self::assertEquals(12, MD_STD_IN::validate_latitude(12));
        self::assertEquals(12.12, MD_STD_IN::validate_latitude("12.12"));
        self::assertEquals(12.12, MD_STD_IN::validate_latitude("12,12"));
        self::assertEquals(12.12, MD_STD_IN::validate_latitude(12.12));

        self::expectException(MDgenericInvalidInputsException::class);
        MD_STD_IN::validate_latitude("test");
        self::expectException(MDgenericInvalidInputsException::class);
        MD_STD_IN::validate_latitude("");
        self::expectException(MDCoordinateOutOfRange::class);
        MD_STD_IN::validate_latitude("1900");
        self::expectException(MDCoordinateOutOfRange::class);
        MD_STD_IN::validate_latitude(1900);
        self::expectException(MDCoordinateOutOfRange::class);
        MD_STD_IN::validate_latitude(-1900);

    }

    /**
     * Function for testing validate_isbn().
     *
     * @small
     * @covers \MD_STD_IN::validate_isbn
     *
     * @return void
     */
    public function test_validate_isbn():void {

        self::assertEquals("", MD_STD_IN::validate_isbn(""));
        self::assertEquals("0943396042", MD_STD_IN::validate_isbn("0943396042"));
        self::assertEquals("0943396042", MD_STD_IN::validate_isbn("0-943396-04-2"));
        self::assertEquals("094339604X", MD_STD_IN::validate_isbn("0-943396-04-X"));

        self::assertEquals("1230943396042", MD_STD_IN::validate_isbn("1230943396042"));
        self::assertEquals("1230943396042", MD_STD_IN::validate_isbn("1230-943396-04-2"));

        self::expectException(MDgenericInvalidInputsException::class);
        MD_STD_IN::validate_isbn("X094339604");

        self::expectException(MDgenericInvalidInputsException::class);
        MD_STD_IN::validate_isbn("094339604a");

    }

    /**
     * Function for testing validate_zip_code().
     *
     * @small
     * @covers \MD_STD_IN::validate_zip_code
     *
     * @return void
     */
    public function test_validate_zip_code():void {

        self::assertEquals("", MD_STD_IN::validate_zip_code(""));
        self::assertEquals("1234", MD_STD_IN::validate_zip_code(" 1234"));

        self::expectException(MDgenericInvalidInputsException::class);
        MD_STD_IN::validate_zip_code("X094339604");

    }

    /**
     * Function for testing ensureStringIsUtf8().
     *
     * @small
     * @covers \MD_STD_IN::ensureStringIsUtf8
     *
     * @return void
     */
    public function test_ensureStringIsUtf8():void {

        self::assertEquals("ä", MD_STD_IN::ensureStringIsUtf8("ä"));
        self::assertEquals("ä", MD_STD_IN::ensureStringIsUtf8(iconv("UTF-8", 'ISO-8859-1//TRANSLIT', "ä")));
        self::assertEquals("a", MD_STD_IN::ensureStringIsUtf8(iconv("UTF-8", 'ISO-2022-JP//TRANSLIT', "ä")));

    }
}