<?php
/**
 * @package    Proxim
 * @author     Davison Pro <davisonpro.coder@gmail.com | https://davisonpro.dev>
 * @copyright  2019 Proxim
 * @version    1.0.0
 * @since      File available since Release 1.0.0
 */

namespace Proxim\Api;

use Db;
use Proxim\Database\DbQuery;
use Proxim\Configuration;
use Proxim\User\Customer;
use Proxim\Mail;
use Proxim\Tools;
use Proxim\Validate;
use Proxim\Route;
use Proxim\Util\ArrayUtils;
use Proxim\Util\DateUtils;

use Proxim\Crypto\Hashing;
use Proxim\Hook;

class Auth extends Route {
    public function login() {
		$app = $this->app;
		$payload = $app->request->post();
		$user = $app->container->user;

		if($user->isLogged()) {
			return $app->response([
				"success" => true,
				"errors" => null
			]);
		}

		$login = ArrayUtils::get($payload, 'login');
		if ( !Validate::isEmail($login) ) {
			$customer = new Customer( (int) $login);
			if (!Validate::isLoadedObject($customer)) {
				return $app->response([
					"success" => false,
					"errors" => [
						"Enter a valid email or ID"
					]
				]);
			}
			$email = $customer->email;
		} else {
			$email = $login;
		}

		$plaintextPassword = ArrayUtils::get($payload, 'plainTextPassword');
		$password = ArrayUtils::get($payload, 'password');

		if ( !$password ) {
			return $app->response([
				"success" => false,
				"errors" => [
					"password" => "Password is required"
				]
			]);
		}

		Hook::exec('actionAuthenticationBefore');

		// check if email exists so we can get the hashed password
		$user = $user->getByEmail( $email );
		if (!Validate::isLoadedObject($user)) {
			return $app->response([
				"success" => false,
				"errors" => [
					"Incorrect login or password"
				]
			]);
		}

		$passwordHash = $user->password;
		// check hashed password against plaintext and sha256 password and get the currenct password
		$passwords = array(
			$password,
			$plaintextPassword
		);

		$crypto = new Hashing();

		foreach($passwords as $pswd) {
			if($crypto->checkHash($pswd, $passwordHash)) {
				$password = $pswd;
			}
		}

		// chec user against the correct password
		$user = $user->getByEmail(
			$email,
			$password
		);

		if (!Validate::isLoadedObject($user)) {
			return $app->response([
				"success" => false,
				"errors" => [
					"Incorrect login or password"
				]
			]);
		}
		
		// @todo update from sha256 encryption before POST request
		if($plaintextPassword != $password) {
			$user->password = $crypto->hash(
				$plaintextPassword,
				PROX_COOKIE_KEY
			);
		}

		Hook::exec('actionAuthentication', ['customer' => $user]);
		$user->last_login = DateUtils::now();
		$user->update();

		$app->updateUser($user);

        return $app->response([
			"success" => true,
			"errors" => null
		]);
	}

	public function impersonateCustomer( $customerId ) {
		$app = $this->app;
		$user = $app->container->user;
		$params = $app->request->get();

		$authKey = ArrayUtils::get($params, 'authKey');

		if(!$authKey) {
			$app->setTemplate('404');
        	$app->display();
		}

		$customer = new Customer( (int) $customerId );
		if( !Validate::isLoadedObject($customer) ) {
			$app->setTemplate('404');
        	$app->display();
		}

		if($customer->secure_key != $authKey) {
			$app->setTemplate('404');
        	$app->display();
		}

		$app->updateUser($customer);

        $app->redirect('/dashboard/orders');
	}

	public function logout() {
		$app = $this->app;
		$payload = $app->request->post();
		$user = $app->container->user;

		$user->logout();

		return $app->response([
			"success" => true
		]);
	}

	public function forgotPassword() {
		$app = $this->app;
		$payload = $app->request->post();
		$user = $app->container->user;
		$crypto = new Hashing();

		$email = ArrayUtils::get($payload, 'email');
		if ( !$email || !Validate::isEmail($email) ) {
			return $app->response([
				"success" => false,
				"errors" => [
					"Enter a valid email address"
				]
			]);
		}

		$password = ArrayUtils::get($payload, 'password');
		if ( !$password || !Validate::isPasswd($password) ) {
			$errors['password'] = 'Password must be at least 6 characters';
		}

		$customer = $user->getByEmail(
			$email
		);

		if ( !Validate::isLoadedObject($customer) ) {
			return $app->response([
				"success" => false,
				"errors" => [
					"This email is not registered. Have you spelled it right?"
				]
			]);
		}

		$customer->stampResetPasswordToken();
		$customer->new_password = $crypto->hash(
			$password,
			PROX_COOKIE_KEY
		);
		$customer->update();

		$mailParams = array(
			'email' => $customer->email,
			'username' => $customer->name,
			'url' => Configuration::get('SITE_DOMAIN', $customer->site_id) . '/dashboard/reset-password/' . $customer->reset_password_token,
		);

		/** Send password reset mail */
		if (
			Mail::Send(
				'password_change',
				'Confirm password change',
				$mailParams,
				$customer->email,
				$customer->name
			)
		) {
			return $app->response([
				"success" => true,
				"errors" => null
			]);
		}

		return $app->response([
			"success" => false,
			"errors" => [
				"An error occurred while sending the email."
			]
		]);

	}

	public function checkHash( $resetHash ) {
		$app = $this->app;
		$payload = $app->request->post();
		$user = $app->container->user;

		$sql = new DbQuery();
		$sql->select('u.`customer_id`');
		$sql->from('customer', 'u');
		$sql->where('u.`reset_password_token` = \'' . pSQL($resetHash) . '\'');

		$customer_id = Db::getInstance(PROX_USE_SQL_SLAVE)->getValue($sql);

		$user = new Customer( (int) $customer_id );
		if ( !Validate::isLoadedObject($user) ) {
			return $app->response([
				"success" => false,
				"errors" => [
					"recovery hash is not valid"
				]
			]);
		} 

		if ( $user->getValidResetPasswordToken() != $resetHash ) {
			return $app->response([
				"success" => false,
				"errors" => [
					"recovery hash is not valid"
				]
			]);
		}

		return $app->response([
			"success" => true,
			"errors" => null,
			"is_active" => true,
			"needSetNewPass" => !$user->new_password ? true : false
		]);
	}
	
	public function verifyNewPassword() {
		$app = $this->app;
		$payload = $app->request->post();
		$user = $app->container->user;

		$recoveryHash = ArrayUtils::get($payload, 'recoveryHash');
		$sql = new DbQuery();
		$sql->select('u.`customer_id`');
		$sql->from('customer', 'u');
		$sql->where('u.`reset_password_token` = \'' . pSQL($recoveryHash) . '\'');

		$customer_id = Db::getInstance(PROX_USE_SQL_SLAVE)->getValue($sql);
		$user = new Customer( (int) $customer_id );
		if ( !Validate::isLoadedObject($user) ) {
			return $app->response([
				"success" => false,
				"errors" => [
					"recovery hash is expired"
				]
			]);
		} 

		if ( $user->getValidResetPasswordToken() != $recoveryHash ) {
			return $app->response([
				"success" => false,
				"errors" => [
					"recovery hash is expired"
				]
			]);
		}

		$newPassword = ArrayUtils::get($payload, 'newPassword');
		if ( !$newPassword || !Validate::isPasswd($newPassword) ) {
			return $app->response([
				"success" => false,
				"errors" => [
					"Password must be at least 6 characters"
				],
			]);
		}

		$confirmNewPassword = ArrayUtils::get($payload, 'confirmNewPassword');
		if ( $newPassword != $confirmNewPassword ) {
			return $app->response([
				"success" => false,
				"errors" => [
					"Your passwords do not match"
				],
			]);
		}

		if($confirmNewPassword) {
			$crypto = new Hashing();
			$user->password = $crypto->hash(
				$confirmNewPassword,
				PROX_COOKIE_KEY
			);
		} else {
			$user->password = $user->new_password;
		}

		$user->removeResetPasswordToken();
		$user->update();

		return $app->response([
			"success" => true,
			"errors" => null,
		]);
	}

	public function changePassword( $resetHash ) {
		$app = $this->app;
		$payload = $app->request->post();
		$user = $app->container->user;

		$sql = new DbQuery();
		$sql->select('u.`customer_id`');
		$sql->from('customer', 'u');
		$sql->where('u.`reset_password_token` = \'' . pSQL($resetHash) . '\'');

		$customer_id = Db::getInstance(PROX_USE_SQL_SLAVE)->getValue($sql);
		$user = new Customer( (int) $customer_id );
		if ( !Validate::isLoadedObject($user) ) {
			return $app->response([
				"success" => false,
				"errors" => [
					"recovery hash is expired"
				]
			]);
		} 

		if ( $user->getValidResetPasswordToken() != $resetHash ) {
			return $app->response([
				"success" => false,
				"errors" => [
					"recovery hash is expired"
				]
			]);
		}
		
		$user->password = $user->new_password;
		$user->removeResetPasswordToken();
		$user->update();

		// update user 
		$app->updateUser($user);

		return $app->response([
			"success" => true,
			"errors" => null,
		]);
	}

	public function rejectPasswordChange( $resetHash ) {
		$app = $this->app;
		$payload = $app->request->post();
		$user = $app->container->user;

		$sql = new DbQuery();
		$sql->select('u.`customer_id`');
		$sql->from('customer', 'u');
		$sql->where('u.`reset_password_token` = \'' . pSQL($resetHash) . '\'');

		$customer_id = Db::getInstance(PROX_USE_SQL_SLAVE)->getValue($sql);
		$user = new Customer( (int) $customer_id );
		if ( !Validate::isLoadedObject($user) ) {
			return $app->response([
				"success" => false,
				"errors" => [
					"recovery hash is expired"
				]
			]);
		} 

		if ( $user->getValidResetPasswordToken() != $resetHash ) {
			return $app->response([
				"success" => false,
				"errors" => [
					"recovery hash is expired"
				]
			]);
		}
		
		$user->removeResetPasswordToken();
		$user->update();

		return $app->response([
			"success" => true,
			"errors" => null,
		]);
	}
}