<?php

/**
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @package fusionLib
 * @copyright Copyright (c) 2013 - 2016 fusionLib. All rights reserved.
 * @link http://fusionlib.com
 */

/**
 * flUserServerApiV1 Class
 *
 * Class to act as an authentication server for flUserAdapterRemote.
 *
 * @package fusionLib
 * @see flUserAdapterRemote
 */
class flUserServerApiV1 extends flApiService {

	/**
	 * The adapter to use for user authentication.
	 *
	 * @var flUserAdapter
	 */
	protected $adapter;

	/**
	 * The response object.
	 *
	 * @var flResponse
	 */
	protected $response;

	/**
	 * Construct the service object.
	 *
	 * @param flDb $db The database object.
	 * @param flApiServer $apiServer The API server object.
	 * @param flRequest $request The request object.
	 * @param flResponse $response The response object.
	 * @throws flExceptionForbidden
	 */
	function __construct($db, $apiServer, $request, $response) {
		parent::__construct($db, $apiServer, $request, $response);

		if(!$this->apiServer->hasPermission('core.users'))
			throw new flExceptionForbidden();

		$this->adapter = new flUserAdapter($db);
	}

	/**
	 * Handle authentication.
	 *
	 * @throws flExceptionBadRequest
	 */
	function authenticatePost() {

		// Get and validate parameters
		$ip = $this->request->getPost('ip', '');
		$username = $this->request->getPost('username', '');
		$password = $this->request->getPost('password', '');
		$by = $this->request->getPost('by', '');

		if(empty($ip) || empty($username) || empty($password) || !in_array($by, array('', 'username', 'email')))
			throw new flExceptionBadRequest();

		// Authenticate and reply
		$r = $this->adapter->authenticate($ip, $username, $password, $by === '' ? null : $by, false);
		if($r['authState'] == flUser::AUTH_OK)
			$r['roles'] = array_values($this->adapter->getRoles($r['uid']));

		$this->response->setStatusCode($r['authState'] == flUser::AUTH_OK ? 200 : 400)
			->setBody(json_encode($r))
			->sendResponse();
	}

	/**
	 * Validate the user id and get the access token, higher level functions must check the access token is valid.
	 *
	 * @throws flExceptionBadRequest
	 */
	function validatePost() {

		// Get and validate parameters
		$uid = $this->request->getPost('uid', '');
		$data = $this->request->getPost('data', '');
		$hash = $this->request->getPost('hash', '');
		if(empty($uid) || empty($hash))
			throw new flExceptionBadRequest();

		$r = $this->adapter->validate($uid, $data, $hash, false);
		if($r['authState'] == flUser::AUTH_OK)
			$r['roles'] = $this->adapter->getRoles($r['uid']);

		$this->response->setStatusCode($r['authState'] == flUser::AUTH_OK ? 200 : 400)
			->setBody(json_encode($r))
			->sendResponse();
	}

	/**
	 * Regenerate the authentication token.
	 *
	 * @throws flExceptionBadRequest
	 */
	function regeneratePost() {

		// Get and validate parameters
		$uid = $this->request->getPost('uid', '');
		if(empty($uid))
			throw new flExceptionBadRequest();

		$r = $this->adapter->regenerate($uid);
		$this->response->setStatusCode($r['authState'] == flUser::AUTH_OK ? 200 : 400)
			->setBody(json_encode($r))
			->sendResponse();
	}

	/**
	 * Change a field.
	 *
	 * @throws flExceptionBadRequest
	 */
	function changePost() {

		// Get and validate parameters
		$uid = $this->request->getPost('uid', '');
		$field = $this->request->getPost('field', '');
		$value = $this->request->getPost('value', '');
		if(empty($uid) || empty($field) || empty($value))
			throw new flExceptionBadRequest();

		$r = $this->adapter->change($uid, $field, $value);
		$this->response->setStatusCode($r ? 200 : 400)
			->setBody(json_encode(array('result' => $r)))
			->sendResponse();
	}

	/**
	 * Check for the presence of a username or email address.
	 *
	 * @throws flExceptionBadRequest
	 */
	function checkGet() {

		// Get and validate parameters
		$what = $this->request->getQuery('field', '');
		$value = $this->request->getQuery('value', '');
		if(empty($what) || empty($value))
			throw new flExceptionBadRequest();

		$r = $this->adapter->check($what, $value);
		$this->response->setStatusCode($r ? 200 : 404)
			->setBody(json_encode(array('result' => $r)))
			->sendResponse();
	}

	/**
	 * Get the information on a user.
	 *
	 * @throws flExceptionBadRequest
	 */
	function infoGet() {

		// Get and validate parameters
		$uid = $this->request->getQuery('uid', '');
		if(empty($uid))
			throw new flExceptionBadRequest();

		$r = $this->adapter->getInfo($uid);
		$this->response->setStatusCode($r ? 200 : 404)
			->setBody(json_encode($r ? $r : array('result' => false)))
			->sendResponse();
	}

	/**
	 * Block a user.
	 *
	 * @throws flExceptionBadRequest
	 */
	function blockPost() {

		// Get and validate parameters
		$uid = $this->request->getPost('uid', '');
		$blockedBy = $this->request->getPost('blockedBy', '');
		$days = $this->request->getPost('days', '');
		$reason = $this->request->getPost('reason', '');
		if(!preg_match('/^\d+$/', $uid) || !preg_match('/^\d+$/', $blockedBy) || !preg_match('/^\d+$/', $days))
			throw new flExceptionBadRequest();

		$r = $this->adapter->block($uid, $blockedBy, $days, $reason);
		$this->response->setStatusCode($r ? 200 : 400)
			->setBody(json_encode(array('result' => $r)))
			->sendResponse();
	}

	/**
	 * Get the information about a block on a user.
	 *
	 * @throws flExceptionBadRequest
	 */
	function blockinfoGet() {

		// Get and validate parameters
		$uid = $this->request->getQuery('uid', '');
		if(!preg_match('/^\d+$/', $uid))
			throw new flExceptionBadRequest();

		$r = $this->adapter->getBlockInfo($uid);
		$this->response->setStatusCode($r ? 200 : 404)
			->setBody(json_encode($r ? $r : array('result' => false)))
			->sendResponse();
	}

	/**
	 * Create a user within the system.
	 *
	 * @throws flExceptionBadRequest
	 */
	function createPost() {
		if(!$this->apiServer->hasPermission('core.usersadd'))
			throw new flExceptionForbidden();

		// Get and validate parameters
		$username = $this->request->getPost('username', '');
		$displayName = $this->request->getPost('displayname', '');
		$email = $this->request->getPost('email', '');
		$password = $this->request->getPost('password', '');
		$state = $this->request->getPost('state', '');
		$tz = $this->request->getPost('tz', '');
		$bio = $this->request->getPost('bio', '');
		if(empty($email) || empty($password) || empty($state) || empty($tz))
			throw new flExceptionBadRequest();

		$r = $this->adapter->create($username == '' ? null : $username, $displayName, $email, $password, $state, $tz, null, $bio);
		$this->response->setStatusCode($r ? 201 : 400)
			->setBody(json_encode(array('uid' => $r)))
			->sendResponse();
	}

	/**
	 * Delete the user.
	 *
	 * @throws flExceptionBadRequest
	 */
	function deletePost() {
		if(!$this->apiServer->hasPermission('core.usersdel'))
			throw new flExceptionForbidden();

		// Get and validate parameters
		$uid = $this->request->getPost('uid', '');
		if(!preg_match('/^\d+$/', $uid))
			throw new flExceptionBadRequest();

		$r = $this->adapter->delete($uid);
		$this->response->setStatusCode($r ? 200 : 404)
			->setBody(json_encode(array('result' => $r)))
			->sendResponse();
	}

	/**
	 * Get the names of the roles assigned to the user.
	 *
	 * @throws flExceptionBadRequest
	 */
	function rolesGet() {

		// Get and validate parameters
		$uid = $this->request->getQuery('uid', '');
		if(!preg_match('/^\d+$/', $uid))
			throw new flExceptionBadRequest();

		$this->response->setStatusCode(200)
			->setBody(json_encode(array_values($this->adapter->getRoles($uid))))
			->sendResponse();
	}

	/**
	 * Set the roles for a user.
	 *
	 * @throws flExceptionBadRequest
	 */
	function rolesPost() {
		if(!$this->apiServer->hasPermission('core.usersroles'))
			throw new flExceptionForbidden();

		// Get and validate parameters
		$uid = $this->request->getPost('uid', '');
		$roles = json_decode($this->request->getPost('roles', ''));
		if($roles === null || !preg_match('/^\d+$/', $uid) || !is_array($roles))
			throw new flExceptionBadRequest();

		$r = $this->adapter->setRoles($uid, $roles);
		$this->response->setStatusCode($r ? 200 : 400)
			->setBody(json_encode($r ? array_values($this->adapter->getRoles($uid)) : array('result' => false)))
			->sendResponse();
	}
}