<?php

class SyncUsers {

	private $_fromStorage;
	private $_toStorage;
	private $_errors = array();

	public function __construct($fromStorage, $toStorage) {
		$this->_fromStorage = $fromStorage;
		$this->_toStorage = $toStorage;
	}

	/**
	 * Sync user.
	 * 
	 * @returns bool or -int if error @see getSyncUserErrorMsg($errCode);
	 */
	public function syncUser($usrLogin, $syncGroups = false, $syncDisabled = false) {
		$usrFrom = $this->_fromStorage->getUser($usrLogin);
		$usrTo = $this->_toStorage->getUser($usrLogin);

		if (V::get('DBG_SU', 0, $_GET, 'int') > 0) {
			echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">usrFrom (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($usrFrom);echo'</pre>';
			echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">usrTo (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($usrTo);echo'</pre>';
		}

		if (!$usrFrom) {
			$this->setError(1, "User {$usrLogin} not exists in fromStorage", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
			return false;
		}
		else if (!$usrTo) {

			$created = $this->_toStorage->createUser($usrFrom);

			if (!$created) {
				$errors = $this->_toStorage->getRawErrorsList();
				foreach ($errors as $vErr) {
					$this->setError($vErr->code, $vErr->msg, $vErr->dbgMsg);
				}
				$this->setError(1, "Error: create user {$usrLogin} from fromStorage to toStorage", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
				return false;
			}

		}
		else {// $usrFrom && $usrTo

			$updateData = array();
			if ($usrFrom->name != $usrTo->name) $updateData['name'] = $usrFrom->name;
			if ($usrFrom->email != $usrTo->email) $updateData['email'] = $usrFrom->email;
			if ($usrFrom->phone != $usrTo->phone) $updateData['phone'] = $usrFrom->phone;
			if ($usrFrom->homeEmail != $usrTo->homeEmail) $updateData['homeEmail'] = $usrFrom->homeEmail;
			if ($usrFrom->homePhone != $usrTo->homePhone) $updateData['homePhone'] = $usrFrom->homePhone;
			if ($usrFrom->employeeType != $usrTo->employeeType) $updateData['employeeType'] = $usrFrom->employeeType;

			if ($this->_fromStorage->isPasswordChanged($usrLogin)) $updateData['password'] = $usrFrom->password;

			$updated = $this->_toStorage->updateUser($usrLogin, $updateData);
			if (!$updated) {
				$errors = $this->_toStorage->getRawErrorsList();
				foreach ($errors as $vErr) {
					$this->setError($vErr->code, $vErr->msg, $vErr->dbgMsg);
				}
				$this->setError(1, "TODO: update user {$usrLogin} from fromStorage to toStorage", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
				return false;
			}

		}

		if ($syncGroups) {
			$synced = $this->syncUserGroups($usrLogin);
			if (!$synced) {
				$this->setError(1, "Error: sync groups for user '{$usrLogin}' from fromStorage to toStorage", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
				return false;
			}
		}

		if ($syncDisabled) {
			$synced = $this->syncDisabled($usrLogin);
			if (!$synced) {
				$this->setError(1, "Error: sync groups for user '{$usrLogin}' from fromStorage to toStorage", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
				return false;
			}
		}

		$this->_fromStorage->setSyncUserDate($usrLogin);
		$this->_toStorage->setSyncUserDate($usrLogin);

		return true;
	}

	public function getSyncUserTodoList($usrLogin, $syncGroups = false, $syncDisabled = false) {
		$syncTodoList = array();
		$usrFrom = $this->_fromStorage->getUser($usrLogin);
		$usrTo = $this->_toStorage->getUser($usrLogin);

		if (!$usrFrom) {
			$syncTodoList[] = "User {$usrLogin} not exists in fromStorage";
			return $syncTodoList;
		}
		else if (!$usrTo) {
			$syncDisabled = false;
			$syncTodoList[] = "Create user {$usrLogin} in toStorage";
		}
		else {// $usrFrom && $usrTo

			$updateData = array();
			if ($usrFrom->name != $usrTo->name) $updateData['name'] = $usrFrom->name;
			if ($usrFrom->email != $usrTo->email) $updateData['email'] = $usrFrom->email;
			if ($usrFrom->phone != $usrTo->phone) $updateData['phone'] = $usrFrom->phone;
			if ($usrFrom->homeEmail != $usrTo->homeEmail) $updateData['homeEmail'] = $usrFrom->homeEmail;
			if ($usrFrom->homePhone != $usrTo->homePhone) $updateData['homePhone'] = $usrFrom->homePhone;
			if ($usrFrom->employeeType != $usrTo->employeeType) $updateData['employeeType'] = $usrFrom->employeeType;
			if ($this->_fromStorage->isPasswordChanged($usrLogin)) $updateData['password'] = '*****';

			foreach ($updateData as $key => $val) {
				$syncTodoList[] = "Update {$key}: {$val}";
			}
		}

		if ($syncDisabled) {
			
			$usrFromDisabeld = $this->_fromStorage->isDisabled($usrFrom);
			$usrToDisabeld = $this->_toStorage->isDisabled($usrTo);

			if ($usrFromDisabeld === null || $usrToDisabeld === null) {
				$syncTodoList[] = "Error: isDisabled '{$usrLogin}' not set in fromStorage or toStorage";
				return $syncTodoList;
			}

			if ($usrFromDisabeld !== $usrToDisabeld) {
				$syncTodoList[] = "Set isDisabled '{$usrLogin}' to " . (($usrFromDisabeld)? '1' : '0');
			}
		}

		if ($syncGroups) {
			$groupsFrom = $this->_fromStorage->getUserGroups($usrLogin);
			$groupsTo = $this->_toStorage->getUserGroups($usrLogin);

			$groupsTodo = array();
			foreach ($groupsTo as $kUid => $vName) {
				$groupsTodo[$kUid] = false;
			}
			foreach ($groupsFrom as $kUid => $vGroup) {
				if (isset($groupsTodo[$kUid])) {
					unset($groupsTodo[$kUid]);
				} else {
					$groupsTodo[$kUid] = true;
				}
			}

			if (!empty($groupsTodo)) {
				foreach ($groupsTodo as $kGroupID => $vBool) {
					if ($vBool) {
						$syncTodoList[] = "Add user '{$usrLogin}' to group {$kGroupID}";
					}
					else {
						$syncTodoList[] = "Remove user '{$usrLogin}' from group {$kGroupID}";
					}
				}
			}
		}
		return $syncTodoList;
	}

	public function syncDisabled($usrLogin, $usrFrom = null, $usrTo = null) {
		if (!$usrFrom) $usrFrom = $this->_fromStorage->getUser($usrLogin);
		if (!$usrTo) $usrTo = $this->_toStorage->getUser($usrLogin);
		if (!$usrFrom || !$usrTo) {
			$this->setError(1, "Error: user '{$usrLogin}' not exists in fromStorage or toStorage", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
			return false;
		}

		$usrFromDisabeld = $this->_fromStorage->isDisabled($usrFrom);
		$usrToDisabeld = $this->_toStorage->isDisabled($usrTo);

		if ($usrFromDisabeld === null || $usrToDisabeld === null) {
			$this->setError(1, "Error: isDisabled '{$usrLogin}' not set in fromStorage or toStorage", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
			return false;
		}

		if ($usrFromDisabeld !== $usrToDisabeld) {
			$synced = $this->_toStorage->setDisabled($usrLogin, $usrFromDisabeld);
			if (!$synced) {
				$this->setError(1, "Error: sync isDisabled '{$usrLogin}' failed ", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
				return false;
			}
		}

		return true;
	}

	public function syncUserGroups($usrLogin, $usrFrom = null, $usrTo = null) {
		if (!$usrFrom) $usrFrom = $this->_fromStorage->getUser($usrLogin);
		if (!$usrTo) $usrTo = $this->_toStorage->getUser($usrLogin);
		if (!$usrFrom || !$usrTo) {
			$this->setError(1, "Error: user '{$usrLogin}' not exists in fromStorage or toStorage", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
			return false;
		}

		$groupsFrom = $this->_fromStorage->getUserGroups($usrLogin);
		$groupsTo = $this->_toStorage->getUserGroups($usrLogin);

		if (V::get('DBG_SU', 0, $_GET, 'int') > 0) {
			echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">groupsFrom (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($groupsFrom);echo'</pre>';
			echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">groupsTo (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($groupsTo);echo'</pre>';
		}

		/**
		 * $groupsTodo - groups todo list:
		 *   'com.apple.access_mail' => true - add to this group
		 *   'com.apple.access_mail' => false - remove from this group
		 */
		$groupsTodo = array();
		foreach ($groupsTo as $kUid => $vName) {
			$groupsTodo[$kUid] = false;
		}
		foreach ($groupsFrom as $kUid => $vGroup) {
			if (isset($groupsTodo[$kUid])) {
				unset($groupsTodo[$kUid]);
			} else {
				$groupsTodo[$kUid] = true;
			}
		}

		if (V::get('DBG_SU', 0, $_GET, 'int') > 0) {
			echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">groupsTodo (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($groupsTodo);echo'</pre>';
		}

		if (!empty($groupsTodo)) {
			foreach ($groupsTodo as $kGroupID => $vBool) {
				if ($vBool) {
					$added = $this->_toStorage->addUserGroup($usrLogin, $groupsFrom[$kGroupID]);
					if (!$added) {
						$this->setError(1, "Error: user '{$usrLogin}' add to group '{$kGroupID}' in toStorage", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
					}
				}
				else {
					$removed = $this->_toStorage->removeUserGroup($usrLogin, $groupsTo[$kGroupID]);
					if (!$removed) {
						$this->setError(1, "Error: user '{$usrLogin}' remove from group '{$kGroupID}/{$groupsTo[$kGroupID]->name}' in toStorage", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
					}
				}
			}
		}

		if ($this->hasErrors()) {
			return false;
		}
		return true;
	}

	public function getSyncGroupTodoList($groupID, $syncNestedGroups = false) {
		$syncTodoList = array();
		$groupFrom = $this->_fromStorage->getGroup($groupID);
		$groupTo = $this->_toStorage->getGroup($groupID);

		if (!$groupFrom) {
			$syncTodoList[] = "Group {$groupID} not exists in fromStorage";
			return $syncTodoList;
		}
		else if (!$groupTo) {
			$syncTodoList[] = "Create group {$groupID} in toStorage";
		}
		else {
			$updateData = array();
			if (empty($groupTo->realName) && $groupFrom->realName != $groupTo->realName) {
				$updateData['realName'] = $groupFrom->realName;
			}
			foreach ($updateData as $key => $val) {
				$syncTodoList[] = "Update {$key}: {$val}";
			}
		}

		if ($syncNestedGroups) {
			if (!empty($groupTo->nestedGroups) && !empty($groupFrom->nestedGroups)) {
				$groupsTodo = array();
				if (!empty($groupTo->nestedGroups)) {
					foreach ($groupTo->nestedGroups as $kUid => $vGroup) {
						$groupsTodo[$kUid] = false;
					}
				}
				if (!empty($groupFrom->nestedGroups)) {
					foreach ($groupFrom->nestedGroups as $kUid => $vGroup) {
						if (isset($groupsTodo[$kUid])) {
							unset($groupsTodo[$kUid]);
						} else {
							$groupsTodo[$kUid] = true;
						}
					}
				}

				if (!empty($groupsTodo)) {
					foreach ($groupsTodo as $kGroupID => $vBool) {
						if ($vBool) {
							$syncTodoList[] = "Add group '{$kGroupID}' to group '{$groupID}' in toStorage";
						}
						else {
							$syncTodoList[] = "Remove group '{$kGroupID}' from group '{$groupID}' in toStorage";
						}
					}
				}
			}
		}

		return $syncTodoList;
	}

	/**
	 * Sync user.
	 * 
	 * @returns bool or -int if error @see getSyncUserErrorMsg($errCode);
	 */
	public function syncGroup($groupID, $syncNestedGroups = false) {
		$groupFrom = $this->_fromStorage->getGroup($groupID);
		$groupTo = $this->_toStorage->getGroup($groupID);

		if (V::get('DBG_SU', 0, $_GET, 'int') > 0) {
			echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">groupFrom (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($groupFrom);echo'</pre>';
			echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">groupTo (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($groupTo);echo'</pre>';
		}

		if (!$groupFrom) {
			$this->setError(1, "Group {$groupID} not exists in fromStorage", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
			return false;
		}
		else if (!$groupTo) {

			$created = $this->_toStorage->createGroup($groupFrom);

			if (!$created) {
				$errors = $this->_toStorage->getRawErrorsList();
				foreach ($errors as $vErr) {
					$this->setError($vErr->code, $vErr->msg, $vErr->dbgMsg);
				}
				$this->setError(1, "Error: create group {$groupID} from fromStorage to toStorage", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
				return false;
			}

		}
		else {// $groupFrom && $groupTo

			$updateData = array();
			if (empty($groupTo->realName) && $groupFrom->realName != $groupTo->realName) {
				$updateData['realName'] = $groupFrom->realName;
			}

			//if ($groupFrom->employeeType != $groupTo->employeeType) $updateData['employeeType'] = $groupFrom->employeeType;
			$updated = $this->_toStorage->updateGroup($groupTo, $updateData);

			if (!$updated) {
				$errors = $this->_toStorage->getRawErrorsList();
				foreach ($errors as $vErr) {
					$this->setError($vErr->code, $vErr->msg, $vErr->dbgMsg);
				}
				$this->setError(1, "TODO: update group {$groupID} from fromStorage to toStorage", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
				return false;
			}
		}

		if ($syncNestedGroups) {
			$synced = $this->syncNestedGroups($groupID, $groupFrom, $groupTo);
			if (!$synced) {
				$this->setError(1, "Error: sync nested groups for group '{$groupID}' from fromStorage to toStorage", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
				return false;
			}
		}

		$this->_fromStorage->setSyncGroupDate($groupID);
		$this->_toStorage->setSyncGroupDate($groupID);

		return true;
	}

	public function syncNestedGroups($groupID, $groupFrom = null, $groupTo = null) {
		if (!$groupFrom) $groupFrom = $this->_fromStorage->getGroup($groupID);
		if (!$groupTo) $groupTo = $this->_toStorage->getGroup($groupID);

		if (V::get('DBG_SU', 0, $_GET, 'int') > 0) {
			echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">groupFrom (hasNestedGroups:'.(!empty($groupFrom->nestedGroups)).') (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($groupFrom);echo'</pre>';
			echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">groupTo (hasNestedGroups:'.(!empty($groupTo->nestedGroups)).') (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($groupTo);echo'</pre>';
		}

		if (empty($groupFrom->nestedGroups) && empty($groupTo->nestedGroups)) {
			// nothing to do
			return true;
		}
		else {

			/*
					[nestedGroups] => Array(
							[2981] => stdClass Object(
									[primaryKey] => 2981
									[type] => STANOWISKO
									[realName] => [2981] Kierownik ds. Rozwoju Biznesu
									[zasobID] => 2981
									[zasobDESC] => Kierownik ds. Rozwoju Biznesu
			*/

			/**
			 * $groupsTodo - groups todo list:
			 *   'com.apple.access_mail' => true - add to this group
			 *   'com.apple.access_mail' => false - remove from this group
			 */
			$groupsTodo = array();
			if (!empty($groupTo->nestedGroups)) {
				foreach ($groupTo->nestedGroups as $kUid => $vGroup) {
					$groupsTodo[$kUid] = false;
				}
			}
			if (!empty($groupFrom->nestedGroups)) {
				foreach ($groupFrom->nestedGroups as $kUid => $vGroup) {
					if (isset($groupsTodo[$kUid])) {
						unset($groupsTodo[$kUid]);
					} else {
						$groupsTodo[$kUid] = true;
					}
				}
			}

			if (V::get('DBG_SU', 0, $_GET, 'int') > 0) {
				echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">groupsTodo (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($groupsTodo);echo'</pre>';
			}

			if (!empty($groupsTodo)) {
				foreach ($groupsTodo as $kGroupID => $vBool) {
					if ($vBool) {
						$added = $this->_toStorage->addNestedGroup($groupID, $groupFrom->nestedGroups[$kGroupID]);
						if (!$added) {
							$this->setError(1, "Error: group '{$kGroupID}' add to group '{$groupID}' in toStorage", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
						}
					}
					else {
						$removed = $this->_toStorage->removeNestedGroup($groupID, $groupTo->nestedGroups[$kGroupID]);
						if (!$removed) {
							$this->setError(1, "Error: group '{$kGroupID}' remove from group '{$groupID}' in toStorage", '(' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . ')');
						}
					}
				}
			}
		}

		if ($this->hasErrors()) {
			return false;
		}
		return true;
	}


	private function setError($code, $msg, $dbgMsg) {
		$this->_errors[] = (object)array('code'=>$code, 'msg'=>$msg, 'dbgMsg'=>$dbgMsg);
	}

	public function hasErrors() {
		return !empty($this->_errors);
	}

	public function getErrorsMsgList() {
		$msgList = array();
		foreach ($this->_errors as $vErr) {
			$msgList[] = "Error {$vErr->code}: {$vErr->msg}";
		}
		return $msgList;
	}

	public function getErrorsMsgListWithDbg() {
		$msgList = array();
		foreach ($this->_errors as $vErr) {
			$msgList[] = "Error {$vErr->code}: {$vErr->msg} (DBG:{$vErr->dbgMsg})";
		}
		return $msgList;
	}

}
