<?php

namespace modules\loginEmail;

class main extends \moduleMain {

	use \modules\input\traits {
			\modules\input\traits::__construct as __ConstructInput;
		}
	use \modules\database\traits {
			\modules\database\traits::__construct as __ConstructDatabase;
		}
	use \modules\output\traits {
			\modules\output\traits::__construct as __ConstructOutput;
		}

	private $model;
	private $__parent_theme;

	public function __construct() {
		parent::__construct(__DIR__);
		$this->__ConstructInput();
		$this->__ConstructDatabase();
		$this->__ConstructOutput();

	}

	public function Activate() {
		hook_add('auth.email.button', [$this, '__generate_button']);
		hook_add('auth.*.login', [$this, '__info'], 90);				// at the end of the list
		hook_add('auth.email.login', [$this, '__login_info']);			// information for login button / link /  generate auth field for user level to be used
        $this->link('/email-link/{hash}/{auth}/password',  [$this, '__forgotten_pwd']);
        $this->link('/email-link/{hash}/{auth}/login',  [$this, '__forgotten']);
        $this->link('/email-link/{hash}/{auth}',  [$this, '__verification']);
        $this->link('/email-link/{hash}',         [$this, '__verification']);	// must have a hash
		$this->model = $this->LoadModel('loginemail_model');
		$this->model->__activate();
	}

	public function Load() {
		$theme = hook_execute('module.theme', NULL, 'themeDefault');		// get the parent theme
		$this->__parent_theme = $theme;						// load the active theme, whatever that was
		$this->__parent_theme->Load();
//		Hook_add('html.button', [$this, 'Button']);
	}
	
	public function __generate_button(&$html) {
		$html = hook_execute_late('html.button', '', 'Create Account', 'far fa-check-circle', 'cc-light');
	}

	// display form, create account, process verify link, process blacklist link
	
	public function __info(&$results, $param) {
		$res = [];
		$this->__login_info($res, $param);
		$results['email'] = $res;
	}

	public function __login_info(&$info, $param) {
		// save bounce and auth in session
		$_SESSION['loginEmail'] = [
			'auth' => empty($param['auth']) ? '' : $param['auth'],
			'bounce' => $param['bounce'],
			'user_id' => empty($param['user_id']) ? 0 : $param['user_id'],
		];
		
		// no special button for the form
		$info = [
			'link' => '',
			'link2' => '/login?return=' . rawurlencode($param['bounce']),
			'button' => '',
			'theme' => 'loginEmail',			// login page has it's own theme - full screen image
			'login' => [$this, 'generateLoginPage'],
			'register' => [$this, 'generateRegistrationPage'],
			'register_post' => [$this, 'generateRegistrationPage_post'],		// POSTed data from registration page
//			'css' => $css,
		];
	}
	
	public function generateLoginPage(&$result) {
		$view = $this->__LoadView('login_view');
		$view->render_login();
		$result['template'] = 'login_view_email';
		
		$fields = [
			[
				'id' => 'email',
				'name' => 'email',
				'type' => 'email',
				'caption' => 'Email Address',
				'class'	=> 'login-field',
			], [
				'id' => 'pass',
				'name' => 'pass',
				'type' => 'password',
				'caption' => 'Password',
				'class'	=> 'login-field',
			],
		];
		
		$result['fieldlist'] = $fields;
	}

	public function generateRegistrationPage(&$result) {
		$view = $this->__LoadView('login_view');
		$view->render_registration_form();
		$view->render_registration_page();
		$result['template'] = 'register_email';
		$fields = [
			[
				'id' => 'name',
				'name' => 'name',
				'type' => 'string',
				'caption' => 'Name',
                'required' => true,
			],
			[
				'id' => 'email',
				'name' => 'email',
				'type' => 'email',
				'caption' => 'Email Address',
                'required' => true,
			],
			[
				'id' => 'pass',
				'name' => 'pass',
				'type' => 'password',
				'caption' => 'Password',
                'required' => true,
			],
		];
		$result['fieldlist'] = $fields;
	}
	
/*
	login theme
*/	
	public function run() {
		$navigation = [];
		header('Content-type: text/html; charset=utf-8');
		echo $this->render($navigation);
	}

	// all items are on a single line, with separators
	private function GenerateSingleLineMenu_html($menu, $icononly = FALSE) {
		$output = '';
		foreach((array)$menu as $menu_item) {
			if ($icononly) {
				$line = $menu_item['icon'];
			} else {
				$line = $menu_item['caption'];
			}
			$class = isset($menu_item['class']) ? $menu_item['class'] : '';
			$hint = isset($menu_item['hint']) ? $menu_item['hint'] : '';
			if ($hint != '') $hint = ' title="' . htmlspecialchars($hint) . '"';
			if (!empty($menu_item['href']) && $menu_item['href'] != 'false') {	//  != '' && $menu_item['href'] !== FALSE
				if ($class != '') {
					$line = "<a class=\"$class\" href=\"{$menu_item['href']}\"$hint>$line</a>";
				} else {
					$line = "<a href=\"{$menu_item['href']}\"$hint>$line</a>";
				}
			} elseif ($class != '' || $hint != '') {
				$line = "<span class=\"$class\"$hint>$line</span>";
			}
			$output .= '<span class="menuitem-single">' . $line . '</span>';
		}
		return $output;
	}
	
	private function retrieveMenu($menuname) {
		$info = hook_execute('menu.' . $menuname, []);
		// sort by display order?
		return $info;
	}
	
	private function render($navigation, $page_data = NULL) {
        $ext = $this->getJsExtension();
		$id = $this->IncludeFile("https://code.jquery.com/jquery-3.2.1{$ext}", 'jquery');
		$id->tag('jquery');

		$base = $this->StaticUriModule(__DIR__);
		// any files included here are at the end of the list
		$this->IncludeFile($base . '/template.css');

		$title = $this->Title();
		if ($title !== NULL) {
			$dtitle = '<title>' . htmlspecialchars($title) . "</title>\n";
		} else {
			$dtitle = '';
		}

		$menu1 = $new_nav = '';

		$menu = $this->RetrieveMenu('menu2');			// profile menu
		foreach($menu as $index => $menuitem) {			// remove any items not to appear on login menu
			$classname = $menuitem['class'] ?? '';
			if ($classname == 'nologin') {
				unset($menu[$index]);
			}
		}
		$menu2 = $this->GenerateSingleLineMenu_html($menu, TRUE);
		
		$menu = $this->RetrieveMenu('menu3');			// Footer - SingleLine Menu
		$menu3 = $this->GenerateSingleLineMenu_html($menu);
		
		hook_execute('html.prepare', $this, $page_data);
		$body = $this->body();
		if ($body === null) $body = '';
		$body = $this->hook_apply_late($body);

		$random = mt_rand(1, 10);
//		$random = 10;
		$background = sprintf('bg%02d', $random);
		switch($random) {
			case 4:
			case 5:
				$attribution = 'Background Image <a href="http://www.freepik.com">Designed by Freepik</a>';
				break;
			case 6:
				$attribution = 'Background Image <a href="http://www.freepik.com">Designed by Ydlabs / Freepik</a>';
				break;

			case 7:
				$attribution = 'Photo by <a href="https://unsplash.com/@fakurian?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Milad Fakurian</a> on <a href="https://unsplash.com/photos/a-close-up-of-a-fish-seA-FPPXL-M?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>';
				break;
			case 8:
				$attribution = 'Photo by <a href="https://unsplash.com/@martz90?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Martin Martz</a> on <a href="https://unsplash.com/photos/a-blue-abstract-background-with-wavy-shapes-P66DWe8AAbo?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>';
				break;
			case 9:
				$attribution = 'Photo by <a href="https://unsplash.com/@philipsfuture?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Philip Oroni</a> on <a href="https://unsplash.com/photos/a-close-up-of-a-black-and-orange-vase-MKapncnH4nU?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>';
				break;
			case 10:
				$attribution = 'Photo by <a href="https://unsplash.com/@boliviainteligente?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">BoliviaInteligente</a> on <a href="https://unsplash.com/photos/a-colorful-abstract-background-with-wavy-lines-ZOoX0hVGB0A?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>';
				break;

			default:
				$attribution = '';
		}
		if ($attribution != '') $attribution = "<span class=\"menuitem-single attrib\">$attribution</span>";

		$yy = date('Y');
		$footer = <<<BLOCK
<div class="footer-container">
	<div class="main-col footer-line-1">
		<span class="t2">{$menu3}{$attribution}</span>
	</div>
</div>
BLOCK;
		
		hook_remove('global.done', 98);
		$inc = hook_execute('html.head', '');
		
		$favicon = $this->favIcon();
		$header_item_2 = $this->OutputGet('header_item_2', '');
		$page_class = $this->OutputGet('page_class', '');

		$html = <<<BLOCK
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="robots" content="noindex">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="MSSmartTagsPreventParsing" content="TRUE">
{$favicon}{$dtitle}{$inc}
</head>
<body data-bg="{$background}">
<div class="page-outer">
	<div class="page-inner $page_class">
		<div class="page-container">
			<div class="header-container">
				<div class="header-line-1"><div class="nav-container-0">{$menu1}</div><div class="nav-container-1">$new_nav</div></div>
				<div id="headergraphic" class="header main-col">
					<div class="nav-container-2">{$menu2}</div>
				</div>
				{$header_item_2}
			</div>
		
			<div class="body-container">
				<article class="body main-col font8">$body
				</article>
			</div>
			{$footer}
		</div>
	</div>
	<div class="clearfix"></div>
</div>
</body>
</html>
BLOCK;

		return $html;
	}
	
	final function login() {
		$email = trim($this->post('email', ''));
		$password = trim($this->post('pass', ''));
		
		$userinfo = $this->_userByEmail($email);
		if ($userinfo !== NULL && $email != '') {
			if (empty($userinfo->verified)) {
				return ['result' => 1109,
						'message' => 'This account needs to be verified before you can log in'];
			}
			// check password matches
			$requested = crypt($password, $userinfo->password_hash);
			if ($requested != $userinfo->password_hash) {
				return ['result' => 1108,
						'message' => 'invalid email address or password'];
			}
			$userinfo->original_password = $password;
		} else {
			return ['result' => 1107,
					'message' => 'invalid email address or password'];
		}
		
		// all good - login and redirect
		hook_execute('login', 0, $userinfo);			// log in as user

		$uri = $_SESSION['loginEmail']['bounce'];
		if (substr($uri, 0, 6) != '/login') {
			$this->Redirect($uri);
		} else {
			die("\nCannot redirect to login\n");
		}
		
	}
	
	function _userByEmail($email) {
		if (empty($this->model_user)) {
			$this->model_user = $this->LoadModel('user_model', 'pkpCore');
		}
		$param = new \StdClass;
		$param->source = 'internal';
		$param->source_id =md5(strtolower($email)); 
		return  $this->model_user->FromLogin($param, FALSE);
	}

	
	
	final function forgot_password() {
		$view = $this->__LoadView('login_view');
		$view->template_forgotten();
		$result = [];
		$fields = [
			[
				'id' => 'email',
				'name' => 'email',
				'type' => 'email',
				'caption' => 'Email Address',
                'required' => true,
			]
		];
		$result['fieldlist'] = $fields;
   		if ($this->__INPUT->isPost()) {
            $email = $this->post('email');
            $hostname = $this->hostname();
            $user = $this->_userByEmail($email);
			if ($user !== NULL) {
				$source_id = md5(strtolower($email));
				$userhash = md5($source_id);
				$this->model->UserId($user->us_user_id)
							->ForgottenPasswordEmail($email, $userhash, $hostname);
			}
            $result['template'] = 'done_forgotten';
        } else {
            $result['template'] = 'register_forgotten';
        }

        
		return $result;
	}

	final function register() {
		$result = [];
		if ($this->__INPUT->isPost()) {
			$this->generateRegistrationPage_post($result);
			return $result;
		}
		
		$this->generateRegistrationPage($result);
		$result['postlink'] = '/~/loginEmail/register';
		return $result;
		
	}

	public function generateRegistrationPage_post(&$result, $user_id = 0) {
		$name = trim($this->post('name', ''));
		$email = trim($this->post('email', ''));
		$pass = trim($this->post('pass', ''));
       

		// check if all fields have value - return field errors if so
        $hasError = 0;
        foreach(['name', 'email', 'pass'] as $fieldname) {
            if ($$fieldname == '') {                // note '$$'
                $hasError++;
                $result['field'][$fieldname] = ['state' => 1, 'message' => '?'];
            }
        }
        if ($hasError) return;
        
        // does it look like an email address?
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $result['field']['email'] = ['state' => 1, 'message' => 'This does not look like an email address'];
            return;
        }

		// check if using disposable email - return field error if so
		if (($p = strrpos($email, '@')) !== FALSE) {
			$domain = substr($email, $p + 1);
			$found = hook_execute('email.check', FALSE, $email);
			if ($found) {
				$result['field']['email'] = ['state' => 1, 'message' => 'Disposable email addresses are not permitted'];
				return;
			}
		}
        
		// email address already used? - return field error if so
		$userinfo = $this->_userByEmail($email);
		if ($userinfo !== NULL) {
			if (!empty($userinfo->verified)) {
				$result = [
					'result' => 1001,
					'message' => 'An account already exists for this email address',
				];
				return;
			}

			if (!$user_id) $user_id = $userinfo->us_user_id;
		}
        $password_hash = $this->model->PasswordCrypt($pass);

		// create unverified account
		$params = new \StdClass;
		$params->user_id = $user_id;
		$params->level = 1;
		$params->email = $email;
		$params->source = 'internal';
		$params->source_id = md5(strtolower($email));
		$params->verified = 0;
		$params->name = $name;
		$params->password_hash = $password_hash;
		
		$user_info = hook_execute('login.create', NULL, $params);
		$uid = property_exists($user_info, 'user_id') ? $user_info->user_id : $user_info->us_user_id;
		$userhash = $user_info->us_hash;
		$this->model->UserID($uid);
		$sendresult = $this->_SendVerification($email, $userhash, $_SESSION['loginEmail']['bounce'] ?? '/');

		if (is_string($sendresult)) {		// unable to send - display the error message
			$result['result'] = 1530;
			$result['message'] = $sendresult;
		} else {
		// redirect to confirmation page
			$result['redirect'] = '/email-link/' . $userhash;
		}
	}
	
	protected function _SendVerification($email, $userhash, $uri = '/') {
		$code = $this->model->GenerateAuthenticationCode($uri);
		$company = $this->model->getCompany();
		$automated_sender = $this->model->getSender();
		
		// send verification email
        $hostname = $this->hostname();
		$data = [
			'authcode' => $code,
			'link'     => $hostname . '/email-link/' . $userhash . '/' . $code,
			'company'  => $company, 
		];
		$m = new \MailObject();
		$sendresult = $m->template('loginEmail::confirmation')
			->to($email)
			->from($automated_sender)
//			->css($css)
			->data($data)
			->Send();
		return $sendresult;
	}
    
    public function __verification($params) {
		$view = $this->__LoadView('login_view');
		$view->render_verification();
		$hash = isset($params['hash']) ? $params['hash'] : '';
		if ($this->__INPUT->isPost()) {
			$auth = trim($this->post('auth', ''));
		} else {
			$auth = isset($params['auth']) ? $params['auth'] : '';
		}
		if ($auth == 'resend') {
			$result = [];
			list($user_id, $email, $bounce) = $this->model->retrieveResendDetails($hash);
			$this->model->UserID($user_id);
			$sendresult = $this->_SendVerification($email, $hash, $bounce);
			
			if (is_string($sendresult)) {		// unable to send - display the error message
				$result['result'] = 1530;
				$result['message'] = $sendresult;
			} else {
			// redirect to confirmation page
				$result['redirect'] = '/email-link/' . $hash;
			}
			// process result
			return $result;
		}
		
		$returncode = $this->model->VerifyAddress($hash, $auth);
		
		$result = [];
		if (is_numeric($returncode)) {
			if ($returncode) {
				$result['result'] = $returncode;
				$result['message'] = $this->model->errormessage($returncode);
			}
			$result['template'] = 'register_verify';
			$result['hash'] = $hash;
			$result['auth'] = $auth;
		} elseif (is_string($returncode)) {
			$result['redirect'] = $returncode;
		}
		return $result;
    }
 
    private function _forgotten($params, $isLogin) {
   		$hash = isset($params['hash']) ? $params['hash'] : '';
        $auth = isset($params['auth']) ? $params['auth'] : '';
        $returncode = $this->model->VerifyAddress($hash, $auth, TRUE);		// also marks address as verified
		
		if (is_numeric($returncode)) {		// display an error page
			$message = $this->model->errormessage($returncode);
			$this->HttpStatus(400);
			$info = $this->HttpStatus();
			$info->message($message);
			$info->errnum($returncode);
			return NULL;
		}

		// hash is ok  - login and redirect
		$userinfo = $this->model->userdata();
		hook_execute('login', 0, $userinfo);			// log in as user
        session_write_close();
		
		//** delete the entry from the verification table
		
        if ($isLogin) {
            return ['redirect' => $returncode];
        }

       // requesting password change
		//   go to change password page, but without the need to enter old password
 		$view = $this->__LoadView('login_view');
		$view->template_changepassword();
		$result['template'] = 'register_password';
		$result['us_hash'] = $userinfo->us_hash;
		$result['email'] = $userinfo->email;
        return $result;
    }
 
    public function __forgotten_pwd($params) {
        return $this->_forgotten($params, FALSE);
    }
    public function __forgotten($params) {
        return $this->_forgotten($params, TRUE);
    }
	
	final function change_password() {
		$pass1 = $this->post('password', '');
		$pass2 = $this->post('password_verify', '');
		$hash = $this->post('us_hash', '');
        
 		if (empty($this->model_user)) {
			$this->model_user = $this->LoadModel('user_model', 'pkpCore');
		}
        $userdata = $this->model_user->LoadUser('internal', $hash, 0);
        $this->model->UserData($userdata);
		return $this->model->ChangePassword($pass1, $pass2);
	}


}