<?php

if ( is_multisite() ) :
/**
 * Test wp_count_sites() in multisite.
 *
 * @group ms-site
 * @group multisite
 * @ticket 38071
 */
class Tests_Multisite_Count_Sites extends WP_UnitTestCase {
	protected static $site_ids;
	protected static $expected;

	protected static $different_network_id;
	protected static $different_site_ids;
	protected static $different_expected;

	public static function wpSetUpBeforeClass( $factory ) {
		self::$site_ids = array(
			'wordpress.org/'         => array( 'domain' => 'wordpress.org', 'path' => '/'        ,
												'meta' => array( 'public' => 1, 'archived' => 0, 'mature' => 0, 'spam' => 0, 'deleted' => 0 ) ),
			'wordpress.org/foo/'     => array( 'domain' => 'wordpress.org', 'path' => '/foo/'    ,
												'meta' => array( 'public' => 0, 'archived' => 0, 'mature' => 0, 'spam' => 0, 'deleted' => 0 ) ),
			'wordpress.org/foo/bar/' => array( 'domain' => 'wordpress.org', 'path' => '/foo/bar/',
												'meta' => array( 'public' => 1, 'archived' => 1, 'mature' => 1, 'spam' => 0, 'deleted' => 0 ) ),
			'wordpress.org/bif/'     => array( 'domain' => 'wordpress.org', 'path' => '/bif/'    ,
												'meta' => array( 'public' => 1, 'archived' => 0, 'mature' => 1, 'spam' => 1, 'deleted' => 0 ) ),
			'wordpress.org/baz/'     => array( 'domain' => 'wordpress.org', 'path' => '/baz/'    ,
												'meta' => array( 'public' => 1, 'archived' => 0, 'mature' => 0, 'spam' => 1, 'deleted' => 1 ) ),
			'wordpress.org/boo/'     => array( 'domain' => 'wordpress.org', 'path' => '/boo/'    ,
												'meta' => array( 'public' => 1, 'archived' => 1, 'mature' => 1, 'spam' => 1, 'deleted' => 1 ) ),
		);

		foreach ( self::$site_ids as &$id ) {
			$id = $factory->blog->create( $id );
		}
		unset( $id );

		self::$expected = (object) array (
			'public' => 6, // +1 because of example.org/ site
			'archived' => 2,
			'mature' => 3,
			'spam' => 3,
			'deleted' => 2,
			);

		self::$different_network_id = $factory->network->create( array( 'domain' => 'diff_wordpress.org', 'path' => '/' ) );

		self::$different_site_ids = array(
			'diff.wordpress.org/'         => array( 'domain' => 'diff.wordpress.org', 'path' => '/'        , 'site_id' => self::$different_network_id,
												'meta' => array( 'public' => 1, 'archived' => 0, 'mature' => 0, 'spam' => 0, 'deleted' => 0 ) ),
			'diff.wordpress.org/foo/'     => array( 'domain' => 'diff.wordpress.org', 'path' => '/foo/'    , 'site_id' => self::$different_network_id,
												'meta' => array( 'public' => 1, 'archived' => 1, 'mature' => 0, 'spam' => 0, 'deleted' => 0 ) ),
			'diff.wordpress.org/foo/bar/' => array( 'domain' => 'diff.wordpress.org', 'path' => '/foo/bar/', 'site_id' => self::$different_network_id,
												'meta' => array( 'public' => 0, 'archived' => 0, 'mature' => 1, 'spam' => 0, 'deleted' => 0 ) ),
			'diff.wordpress.org/bif/'     => array( 'domain' => 'diff.wordpress.org', 'path' => '/bif/'    , 'site_id' => self::$different_network_id,
												'meta' => array( 'public' => 1, 'archived' => 0, 'mature' => 1, 'spam' => 0, 'deleted' => 1 ) ),
			'diff.wordpress.org/baz/'     => array( 'domain' => 'diff.wordpress.org', 'path' => '/baz/'    , 'site_id' => self::$different_network_id,
												'meta' => array( 'public' => 1, 'archived' => 0, 'mature' => 0, 'spam' => 1, 'deleted' => 1 ) ),
			'diff.wordpress.org/boo/'     => array( 'domain' => 'diff.wordpress.org', 'path' => '/boo/'    , 'site_id' => self::$different_network_id,
												'meta' => array( 'public' => 1, 'archived' => 1, 'mature' => 0, 'spam' => 0, 'deleted' => 0 ) ),
		);

		foreach ( self::$different_site_ids as &$id ) {
			$id = $factory->blog->create( $id );
		}
		unset( $id );

		self::$different_expected = (object) array (
			'public' => 5,
			'archived' => 2,
			'mature' => 2,
			'spam' => 1,
			'deleted' => 2,
			);
	}

	public static function wpTearDownAfterClass() {
		// just in case
		self::switch_network( 1 );

		foreach( self::$site_ids as $id ) {
			wpmu_delete_blog( $id, true );
		}

		foreach( self::$different_site_ids as $id ) {
			wpmu_delete_blog( $id, true );
		}

		self::delete_network ( self::$different_network_id );

		wp_update_network_site_counts();
	}

	public function test_count_sites() {
		$counts = wp_count_sites();

		$this->assertEquals( self::$expected, $counts );
	}

	public function test_filtered_count_sites() {
		add_filter( 'wp_count_sites', array( __CLASS__, 'modify_counts' ), 10, 2 );

		$counts = wp_count_sites();

		$this->assertEquals( self::modify_counts( self::$expected, get_current_network_id() ), $counts );
	}

	public function test_different_network_count_sites() {
		$counts = wp_count_sites( self::$different_network_id );

		$this->assertEquals( self::$different_expected, $counts );
	}

	public function test_filtered_different_network_count_sites() {
		add_filter( 'wp_count_sites', array( __CLASS__, 'modify_counts' ), 10, 2 );

		$counts = wp_count_sites( self::$different_network_id );

		$this->assertEquals( self::modify_counts( self::$different_expected, self::$different_network_id ), $counts );
	}

	public function test_switched_network_count_sites() {
		self::switch_network( self::$different_network_id );

		$counts = wp_count_sites();

		$this->assertEquals( self::$different_expected, $counts );

		self::switch_network( 1 );
	}

	public function test_filtered_switched_network_count_sites() {
		self::switch_network( self::$different_network_id );

		add_filter( 'wp_count_sites', array( __CLASS__, 'modify_counts' ), 10, 2 );

		$counts = wp_count_sites();

		$this->assertEquals( self::modify_counts( self::$different_expected, self::$different_network_id ), $counts );

		self::switch_network( 1 );
	}

	/**
	 * Filter the site counts
	 *
	 *	if counts are for the default network then subtract 1 from all counts
	 *	if counts are for a different network then add 1 to all counts
	 *
	 * @param array $counts
	 * @param int $network_id
	 * @return array
	 *
	 * @filter wp_count_sites
	 */
	public static function modify_counts( $counts, $network_id ) {
		$diff = ( 1 === $network_id ? -1 : +1 );

		$mod_counts = clone $counts ;
		foreach ( get_object_vars( $counts ) as $status => $count ) {
			$mod_counts->{$status} = max ( $count + $diff, 0 );
		}

		return $mod_counts;
	}
	
	/*
	 * I've never worked with multi-network setups before and was shocked
	 * to find that there is no real API support in core for multi-networks
	 * (@link https://core.trac.wordpress.org/ticket/29411).
	 *
	 * The functions below appear to be the BARE MINIMUM multi-network support
	 * necessary for the tests in this file to run correctly and leave the
	 * environment "clean" after they run.
	 */

	/**
	 * minimalist network switching
	 *
	 * @param int $network_id
	 */
	public static function switch_network( $network_id ) {
		global $current_site, $wpdb;

		// so that get_current_network_id() will do the right thing
		$current_site->id = $network_id;
		
		// so that wp_update_network_site_counts() will do the right thing
		$wpdb->siteid = $network_id;
	}

	/**
	 * minimalist network deletion
	 *
	 * @param int $network_id
	 */
	public static function delete_network( $network_id ) {
		global $wpdb;

		$sql  = "DELETE FROM {$wpdb->site} WHERE id = %d";
		$sql = $wpdb->prepare( $sql, $network_id );
		$wpdb->query( $sql );

		$sql  = "DELETE FROM {$wpdb->sitemeta} WHERE site_id = %d";
		$sql = $wpdb->prepare( $sql, $network_id );
		$wpdb->query( $sql );
	}
}

endif;
