<?php
/**
 * CMB2_Utils tests
 *
 * @package   Tests_CMB2
 * @author    CMB2 team
 * @license   GPL-2.0+
 * @link      https://cmb2.io
 */

require_once( 'cmb-tests-base.php' );

class Test_CMB2_Utils extends CMB2TestCase {

	/**
	 * Test cases for empty values
	 * @var array
	 */
	protected $test_empty = array(
      array(
         'val' => null,
         'empty' => true,
      ),
      array(
         'val' => false,
         'empty' => true,
      ),
      array(
         'val' => '',
         'empty' => true,
      ),
      array(
         'val' => 0,
         'empty' => false,
      ),
      array(
         'val' => 0.0,
         'empty' => false,
      ),
      array(
         'val' => '0',
         'empty' => false,
      ),
      array(
         'val' => '0.0',
         'empty' => false,
      ),
      array(
         'val' => 1,
         'empty' => false,
      ),
      array(
         'val' => ' ',
         'empty' => false,
      ),
      array(
         'val' => "\n",
         'empty' => false,
      ),
      array(
         'val' => '&nbsp;',
         'empty' => false,
      ),
      array(
         'val' => array(),
         'empty' => true,
      ),
      array(
         'val' => array( 0 ),
         'empty' => false,
      ),
   );

	/**
	 * Post ID for testing
	 * @var int
	 */
	protected $post_id;

	/**
	 * Image attachment ID for testing
	 * @var int
	 */
	protected $img_attachment;

	/**
	 * Image name for testing
	 * @var string
	 */
	protected $img_name;

	/**
	 * Attachment ID for testing
	 * @var int
	 */
	protected $attachment_id;

	/**
	 * WP Factory instance
	 * @var WP_UnitTest_Factory
	 */
	protected $factory;

	/**
	 * Set up the test fixture
	 */
	public function set_up() {
		parent::set_up();

		$this->post_id = $this->factory->post->create();
		$this->img_name = 'test-image.jpg';

		$filename = ( CMB2_TESTDATA . '/images/test-image.jpg' );
		$contents = file_get_contents( $filename );
		$upload   = wp_upload_bits( basename( $filename ), null, $contents );

		$this->attachment_id = $this->_make_attachment( $upload, $this->post_id );
	}

	public function tear_down() {
		parent::tear_down();
	}

	public function test_image_id_from_url() {

		$_id_value = CMB2_Utils::image_id_from_url( esc_url_raw( wp_get_attachment_url( $this->attachment_id ) ) );
		if ( get_bloginfo( 'version' ) > 3.9 ) {
			$this->assertEquals( $_id_value, $this->attachment_id );
		} else {
			$this->assertGreaterThan( 0, $this->attachment_id );
		}
	}

	function _make_attachment( $upload, $parent_post_id = -1 ) {

		$type = '';
		if ( ! empty( $upload['type'] ) ) {
			$type = $upload['type'];
		} else {
			$mime = wp_check_filetype( $upload['file'] );
			if ( $mime ) {
				$type = $mime['type'];
			}
		}

		$attachment = array(
			'post_title' => basename( $upload['file'] ),
			'post_content' => '',
			'post_type' => 'attachment',
			'post_parent' => $parent_post_id,
			'post_mime_type' => $type,
			'guid' => $upload['url'],
		);

		// Save the data
		$id = wp_insert_attachment( $attachment, $upload['file'], $parent_post_id );
		wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) );

		return $id;
	}

	public function test_get_url_from_dir() {
		$this->assertEquals(
			trailingslashit( site_url() ),
			CMB2_Utils::get_url_from_dir( ABSPATH )
		);

		foreach ( array(
			'cmb2',
			'wp-content/cmb2',
			'vendor/cmb2/',
			'wp-content/themes/cmb2/',
			'wp-content/themes/twentysixteen/cmb2/',
			'wp-content/plugins/cmb2/',
			'wp-content/plugins/some-plugin/cmb2/',
			'wp-content/mu-plugins/cmb2/',
			'wp-content/mu-plugins/some-mu-plugin/cmb2/',
		) as $located ) {
			$this->assertEquals(
				site_url( $located ),
				CMB2_Utils::get_url_from_dir( ABSPATH . $located )
			);

			add_filter( 'theme_root', array( 'Test_CMB2_Utils_WIN', '_change_to_wamp_theme_root' ) );

			$this->assertEquals(
				site_url( $located ),
				Test_CMB2_Utils_WIN::get_url_from_dir( ABSPATH . $located )
			);
			remove_filter( 'theme_root', array( 'Test_CMB2_Utils_WIN', '_change_to_wamp_theme_root' ) );
		}

	}

	public function test_isempty() {
		foreach ( $this->test_empty as $test ) {
			$this->assertEquals( $test['empty'], CMB2_Utils::isempty( $test['val'] ) );
		}
	}

	public function test_notempty() {
		foreach ( $this->test_empty as $test ) {
			$this->assertEquals( ! $test['empty'], CMB2_Utils::notempty( $test['val'] ) );
		}
	}

	public function test_filter_empty() {
		$vals = wp_list_pluck( $this->test_empty, 'val' );

		$non_empties = array(
			3 => 0,
			4 => 0.0,
			5 => '0',
			6 => '0.0',
			7 => 1,
			8 => ' ',
			9 => "\n",
			10 => '&nbsp;',
			12 => array( 0 ),
		);

		$this->assertEquals( $non_empties, CMB2_Utils::filter_empty( $vals ) );
	}

	public function test_concat_attrs() {
		$data = array(
			'att3'  => 'att3 value',
			'att5'  => 'att5 value',
			'att7'  => 'att7 value',
			'att10' => 'att10 value',
		);
		$data2 = array(
			'val1' => 1,
			'val2' => 'two',
			'val3' => array(
				'val1' => 1,
				'val2' => 'two',
				'val3' => 'two',
			),
		);

		$attributes = array(
			'rendered'  => 'rendered',
			'false'     => false,
			'value'     => false,
			'att3'      => 'att3 value',
			'att5'      => 'att5 value',
			'att7'      => 'att7 value',
			'att10'     => 'att10 value',
			'data-blah' => function_exists( 'wp_json_encode' ) ? wp_json_encode( $data ) : json_encode( $data ),
			'data-pizza' => function_exists( 'wp_json_encode' ) ? wp_json_encode( $data2 ) : json_encode( $data2 ),
		);

		$to_exclude = array(
			'att5',
			'att7',
		);

		$this->assertHTMLstringsAreEqual(
			'value="" att3="att3 value" att10="att10 value" data-blah=\'{"att3":"att3 value","att5":"att5 value","att7":"att7 value","att10":"att10 value"}\' data-pizza=\'{"val1":1,"val2":"two","val3":{"val1":1,"val2":"two","val3":"two"}}\'',
			CMB2_Utils::concat_attrs( $attributes, $to_exclude )
		);
	}

	public function test_ensure_array() {
		$this->assertEquals( array( 'test' ), CMB2_Utils::ensure_array( '', array( 'test' ) ) );
		$this->assertEquals( array( 'test' ), CMB2_Utils::ensure_array( false, array( 'test' ) ) );
		$this->assertEquals( array( 'test' ), CMB2_Utils::ensure_array( 0, array( 'test' ) ) );

		$this->assertEquals( array( 'test' ), CMB2_Utils::ensure_array( 'test' ) );
		$this->assertEquals( array( 'test' ), CMB2_Utils::ensure_array( array( 'test' ) ) );

		$this->assertEquals( array(), CMB2_Utils::ensure_array( new CMB2_Utils ) );
	}

	public function test_url_set() {
		$cmb2_url = str_replace(
			array( WP_CONTENT_DIR, WP_PLUGIN_DIR ),
			array( WP_CONTENT_URL, WP_PLUGIN_URL ),
			cmb2_dir()
		);

		$this->assertEquals( CMB2_Utils::url(), $cmb2_url );
	}

	public function test_array_insert() {
		$array = array(
			'one' => array( 1,2,3 ),
			'two' => array( 1,2,3 ),
			'three' => array( 1,2,3 ),
		);

		$new = array(
			'new' => array( 4, 5, 6 ),
		);

		CMB2_Utils::array_insert( $array, $new, 2 );

		$this->assertEquals( array(
			'one' => array( 1,2,3 ),
			'new' => array( 4,5,6 ),
			'two' => array( 1,2,3 ),
			'three' => array( 1,2,3 ),
		), $array );
	}

	public function test_normalize_if_numeric() {
		$tests = array(
			array( '0.1', 0.10000000000000001 ),
			array( 0.1, 0.1 ),
			array( .1, .1 ),
			array( 1, 1 ),
			array( '1', 1 ),
			array( 'one', 'one' ),
			array( '0.0', 0.0 ),
			array( 0, 0 ),
			array( '', '' ),
		);
		foreach ( $tests as $index => $test ) {
			$this->assertSame(
				$test[1],
				CMB2_Utils::normalize_if_numeric( $test[0] ),
				"Test index: $index"
			);
		}

	}

	public function test_php_to_js_dateformat() {
		$tests = array(
			array( 'l F j, Y', 'DD MM d, yy' ),
			array( 'Y-m-d', 'yy-mm-dd' ),
			array( 'm-d-Y', 'mm-dd-yy' ),
			array( 'F', 'MM' ),
			array( 'l', 'DD' ),
			array( 'F', 'MM' ),
			array( 'l jS \of F Y h:i:s A', 'DD dS &#39;o&#39;f MM yy hh:mm:ss TT' ),
			array( 'm.d.y', 'mm.dd.y' ),
			array( 'j, n, Y', 'd, m, yy' ),
			array( 'Ymd', 'yymmdd' ),
			// @todo Fix these:
			array( 'j-m-y, \it \is w Day', 'd-mm-y, &#39;i&#39;t &#39;i&#39;ss w Dtty' ),
			array( '\i\t \i\s \t\h\e jS \d\a\y.', '&#39;it&#39; &#39;is&#39; &#39;the&#39; dS &#39;day&#39;.' ),
		);
		foreach ( $tests as $index => $test ) {
			$this->assertSame(
				$test[1],
				CMB2_Utils::php_to_js_dateformat( $test[0] ),
				"Test index: $index"
			);
		}

	}

	public function test_make_valid_time_stamp() {
		date_default_timezone_set( 'America/New_York' );

		$this->assertSame(
			1658548800,
			CMB2_Utils::make_valid_time_stamp( '07/23/2022' )
		);

		$this->assertSame(
			1658548800,
			CMB2_Utils::make_valid_time_stamp( '1658548800' )
		);

		$this->assertSame(
			1658548800,
			CMB2_Utils::make_valid_time_stamp( 1658548800 )
		);

		$this->assertSame(
			22,
			CMB2_Utils::make_valid_time_stamp( 22 )
		);

	}

	public function test_get_datetime_from_value() {
		$datetime = new DateTime( '2020-04-22 19:18:00.000000', new DateTimeZone( 'Asia/Baghdad' ) );
		$serialized = 'O:8:"DateTime":3:{s:4:"date";s:26:"2020-04-22 19:18:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:12:"Asia/Baghdad";}';
		$json = '{"date":"2020-04-22 19:18:00.000000","timezone_type":3,"timezone":"Asia/Baghdad"}';
		$evil_eval = 'O:4:"Evil":2:{s:4:"hack";N;s:2:"me";R:2;}';

		$this->assertEquals( $datetime, CMB2_Utils::get_datetime_from_value( $serialized ) );
		$this->assertEquals( $datetime, CMB2_Utils::get_datetime_from_value( $json ) );
		$this->assertNull( CMB2_Utils::get_datetime_from_value( $evil_eval ) );
	}

	public function test_unserialize_datetime() {
		$datetime = new DateTime( '2020-04-22 19:18:00.000000', new DateTimeZone( 'Asia/Baghdad' ) );
		$serialized = 'O:8:"DateTime":3:{s:4:"date";s:26:"2020-04-22 19:18:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:12:"Asia/Baghdad";}';
		$evil_eval = 'O:4:"Evil":2:{s:4:"hack";N;s:2:"me";R:2;}';

		$this->assertEquals( $datetime, CMB2_Utils::unserialize_datetime( $serialized ) );
		$this->assertNull( CMB2_Utils::unserialize_datetime( $evil_eval ) );
	}

	public function test_json_to_datetime() {
		$datetime = new DateTime( '2020-04-22 19:18:00.000000', new DateTimeZone( 'Asia/Baghdad' ) );
		$json = '{"date":"2020-04-22 19:18:00.000000","timezone_type":3,"timezone":"Asia/Baghdad"}';
		$evil_eval = 'O:4:"Evil":2:{s:4:"hack";N;s:2:"me";R:2;}';

		$this->assertEquals( $datetime, CMB2_Utils::json_to_datetime( $json ) );
		$this->assertNull( CMB2_Utils::json_to_datetime( $evil_eval ) );
		$this->assertNull( CMB2_Utils::json_to_datetime( [ $json ] ) );
	}

}

class Test_CMB2_Utils_WIN extends CMB2_Utils {
	protected static function get_normalized_abspath() {
		return self::normalize_path( 'C:\xampp\htdocs\the-site-dir' );
	}

	public static function _change_to_wamp_theme_root() {
		return self::get_normalized_abspath() . '/wp-content/themes/';
	}
}
