<?php

class User_Model extends Database_Model {
	
	public function __construct() {
		parent::__construct();
		$this->tablename('users');
	}

	public function LevelPresent($level) {
//		$this->tablename('users');
		$data = $this->Query('select user_id from users')
						->where('level >= %d', $level)
						->first();
		if ($data === NULL) return FALSE;
		//$this->DbErr();
		return !$data->empty();
	}
	
	public function createUser($level, $name, $email = '') {
		$data = new database_record($this);
		$data->tablename('users');
		$data->level = $level;
		$p = strpos($name . ' ', ' ');
		$data->name_first = substr($name, 0, $p);
		$data->name_last = trim(substr($name, $p + 1));
		$data->created = time();
		if ($email != '') $data->primary_email = $email;
		$data->autoinc('user_id')->SaveRecord();
		return $data->user_id;		// return autoinc value
	}
	
	// Insert or Update
	public function AddLogin($user_id, $param) {
	
		$source_hash = md5($param->source_id);
		$payload = [
			'us_identifier' => $param->source_id,
			'us_user_id' => $user_id,
			'email' => $param->email,
			'email_hash' => md5($param->email),
		];
		if (property_exists($param, 'verified')) {
			$payload['verified'] = empty($param->verified) ? 0 : 1;
		}
		if (isset($param->name)) {
			$p = strpos($param->name . ' ', ' ');
			$payload['name_first'] = substr($param->name, 0, $p);
			$payload['name_last'] = trim(substr($param->name, $p + 1));
		}
		if (isset($param->picture)) {
			$payload['picture'] = $param->picture;
		}
		if (isset($param->password_hash)) {
			$payload['password_hash'] = $param->password_hash;
		}
		$this->tablename('users_source')->Debug(TRUE);
		$this->InsertOrUpdate([
			'us_source' => $param->source,
			'us_hash' => $source_hash
		], $payload	);
		$this->DbErr();
		return $this->FromLogin($param);
	}

	public function checkExist($param, $exclude_email = FALSE){
		$data =$this->query('select user_id from users')
					->where('primary_email=%s',$param->email)
					->first();	
		return !$data->empty() ? 1 : 0;
	}

	// are the supplied credentials valid?
	public function FromLogin($param, $exclude_email = FALSE) {
        if ($param->source == 'email' && $exclude_email) return 0;  		// should this be 'internal'
        $source_hash = isset($param->source_id) ? md5($param->source_id) : NULL;
		//if (isset($param->user_id)) {
//			$res = $this->LoadUser(NULL, NULL, $param->user_id);			// this does not return enough info
//		} else {
		$res = $this->LoadUser($param->source ?? NULL, $source_hash, $param->user_id ?? NULL);
		if (empty($res) && isset($param->email)) {		// user may be already in the database - just not using the current authentication method
			$data = $this->Query('select user_id, level from users ')		// this does not return enough info
						->where(['primary_email' => $param->email])
						->first();
			if (!$data->empty()) {
				$res = $this->AddLogin($data->user_id, $param);
			}
		}
		//}
/*		echo "--------------------------------------\n";
		var_dump($param);
		var_dump($exclude_email);
		var_dump($res);
		echo "--------------------------------------\n"; // */
		return $res;
	}
    
    public function LoadUser($source, $hash, $user_id) {
		if (!is_null($source)) {
			$data = $this->Query('select s.*,u.level from users_source as s left join users as u on s.us_user_id=u.user_id')
						->where(['us_source' => $source,
								'us_hash' => $hash])
						->first();
		} else {
			$data = $this->Query('select user_id, level from users ')		// this does not return enough info
						->where(['user_id' => $user_id])
						->first();
		}
		$this->DBErr();
		if ($data->empty()) {
			return NULL;
		} else {
			return $data;
		}
    }
	
	public function Impersonate($id) {
		// load user info
		$user_info = $this->query('select * from users')
					->where('user_id=%d', $id)
					->first();
		if ($user_info->empty()) return FALSE;		// user does not exist (used old link?)
		// set impersonation flag
		$user_info->x_impersonate = TRUE;

		// login
		if ($user_info !== NULL) {
			hook_execute('login', 0, $user_info);			// log in as user $user_id
		}
		return TRUE;		// login ok, redirect.
		
	}

	// include linux name if flag set?
	public function GetActiveUserList() {
		$users = $this->query('select * from users')
					->hook_query('query.user.list')
					->where('level>0')
					->OrderBy('name_last')
					->OrderBy('name_first')
					->get();
		$result = [];
		foreach($users as $user) {
			$name = $user->name_last;
			if ($name == '') {
				$name = $user->name_first;
			} else {
				$name .= ', ' . $user->name_first;
			}
			$node = [ 'key' => $user->user_id, 'value' => $name ];
			if (!empty($user->linux_username)) $node['linux_username'] = $user->linux_username;
			$result[] = $node;
		}
		return $result;
	}
	
	public function getUserInfo($user_id) {
		$postcode = $country = '';
		$data = $this->Query('select * from users ')		// this does not return enough info
					->where(['user_id' => $user_id])
					->first();
		$name = trim($data->name_first . ' ' . $data->name_last);
		$email = $data->primary_email;
		
		// now fetch authentication sources
		$rows = $this->Query('select us_source,password_hash from `users_source`')
					->Where('us_user_id=%d', $user_id)
					->Get();
		$sources = [];
		foreach($rows as $row) {
			$sources[] = [
				'source'	=> $row->us_source,
				'password'	=> $row->password_hash,
			];
		}
		return [ $name, $email, $postcode, $country, $data->name_first, $data->name_last, (int) $data->level, $sources ];
	}
	
	public function accountHasPassword($user_id) {
		$sql = <<<'BLOCK'
select * from users 
	left join users_source as s 
		on us_source="internal" and us_user_id=user_id

BLOCK;
		$row = $this->Query($sql)
					->where(['user_id' => $user_id])
					->first();
					
		return !empty($row->password_hash);
	}

    public function PasswordCrypt($password, $salt = '') {
        if ($salt == '') {
            if (function_exists('random_bytes')) {
                $salt = random_bytes(16);
            } else {
                $salt = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);
            }
            $record = unpack('H*salt', $salt);
            $salt = $record['salt'];
            if (CRYPT_BLOWFISH == 1) {
                $salt = '$2a$07$' . $salt . '$';
            } elseif (CRYPT_MD5 == 1) {
                $salt = '$1$' . $salt .'$';
            } else {
                $salt = $salt;
            }
        }
        return crypt($password, $salt);
    }

	public function setAccountPassword($user_id, $password) {
		[ $name, $email, $postcode, $country, $name_first, $name_last ] = $this->getUserInfo($user_id);
		$email = strtolower($email);
		$identifier = md5($email);
		$this->TableName('users_source')
			->InsertOrUpdate([
				'us_source'		=> 'internal',
				'us_user_id'	=> $user_id,
			], [
				'us_hash'		=> md5($identifier),
				'us_identifier'	=> $identifier,
				'password_hash' => $this->PasswordCrypt($password),
				'email'			=> $email,
				'email_hash'	=> $identifier,
				'name_first'	=> $name_first,
				'name_last'		=> $name_last,
			], [
				'verified'		=> 0,
			]);
		$this->DbErr();
	}
}
