<?php

class Account_Model extends Database_model {

	private $user_settings;
	
	function __construct() {
		parent::__construct();
		$this->level = isset($_SESSION['current_user']) ? $_SESSION['current_user']['u_level'] : 0;
		$this->user_id = isset($_SESSION['current_user']) ? $_SESSION['current_user']['u_id'] : 0;
		$this->impersonater = isset($_SESSION['impersonate']) ? $_SESSION['impersonate']['u_id'] : 0;
		$this->level_i = isset($_SESSION['impersonate']) ? $_SESSION['impersonate']['u_level'] : 0;
		$this->user_settings = FALSE;
		$this->current_user = 0;
	}

	public function __displayName($value, $item) {
		$icon = '';
		$id = $item->user_id;
		if ($this->impersonater > 0) {		// able to impersonate - add a login-as-user icon
			if (($this->level_i >= 10 || $item->level < $this->level_i) && ($id != $this->impersonater)) {
				// add impersonator icon
				$auth = hook_execute('nonce.create', FALSE, 'impersonate', $id, 3600);
				$icon = "<a class=\"placeholder\" href=\"/login?id={$id}&amp;auth={$auth}\"><i class=\"fas fa-user-secret\"></i></a>";
			} else {
				// placeholder - can't impersonate yourself, or someone who has higher level
				$icon = '<span class="placeholder"></span>';
			}
		}
		$dname = htmlspecialchars($value);
//		return $icon . "<a href=\"/account/view/{$id}\">$dname</a>";
		return $icon . $dname;
	}
	
	public function CustomFields($rep) {
		
		$rep->pagesize(25);

		$col = $rep->AddColumn();
		$col->name('name')
			->caption('Name')
			->className('dtcol-name')
			->source(function($item) {
				return trim($item->name_first . ' ' . $item->name_last);
			})
			->render([$this, '__displayName']);
		
		$col = $rep->AddColumn();
		$col->name('email')
			->caption('Email')
			->source(function($item) {
				return $item->primary_email;
			});

		$col = $rep->AddColumn();
		$col->name('created')
			->caption('Created')
			->width(115)
			->order(99)
			->render(function($value) {
				return date('Y-m-d H:i', $value);
			});

		$col = $rep->AddColumn();
		$col->name('locked')
			->caption('Locked?')
			->source(function($item) {
				if($item->is_locked==0){
					return "No";
				}else{
					return "Yes";
				}
				
			});
	}
	
	public function UserList($rep) {
		$data = $rep->Query('select u.* from users as u')->Execute();
		return array('data' => $data);
	}
	
	public function AssembleFieldlist(&$res) {
		$fieldlist = [];
		// create default fields.
		$fieldlist[] = ['type' => 'string', 'id' => 'name', 'name'=>'name', 'caption' => 'Name', 'required' => TRUE];
		$fieldlist[] = ['type' => 'string', 'id' => 'email', 'name'=>'email', 'caption' => 'E-mail', 'required' => TRUE];
//		$fieldlist[] = ['type' => 'password', 'id' => 'password', 'name'=>'password', 'caption' => 'Temporary Password'];	// force change on first login
		// add custom fields via callback
		$fieldlist = hook_execute('account.field.new', $fieldlist);
		$fieldlist[] = ['type' => 'boolean', 'id' => 'welcome', 'name'=>'welcome', 'caption' => 'Send Welcome email?'];
		$res['fieldlist'] = $fieldlist;
//		$res['password'] = hook_execute('password.generate', '');
	}

	public function createAccount($post) {
		$name = hook_execute('name.parse', FALSE, $post['name']);
		$email = $post['email'];
		if (($p = strrpos($email, '@')) !== FALSE) {
			$domain = substr($email, $p + 1);
//			$info = hook_execute('hostname.parse', FALSE, $domain);
//			if ($info !== FALSE) $domain = $info['domain'] . '.' . $info['public'];		// did not work for 10minutemail.ml
			$disposable = hook_execute('email.check', NULL, $domain);
			if ($disposable) {
				return ['result' => 3100,
						'message' => 'temporary email addresses are not acceptable',
						];
			}
		} else {
			return ['result' => 3101,
					'message' => 'email address does not appear to be valid',
					];
		}
		// set up new account
		//		add user entry => get user_id, and parent-id
		//			add email to user_source
		//		call hook to update other field entries (linux username, password)
		// send email (if checked) - includes invite link
        
		$params = new \StdClass;
		$params->user_id = 0;
		$params->level = 1;
		$params->email = $email;
		$params->source = 'internal';
		$params->source_id = md5(strtolower($email));
		$params->verified = 0;
		$params->name = $post['name'];
		$params->picture = '';
		$params->extra = $post;        // extra['username'] => create linux user
		$user_info = hook_execute('login.create', NULL, $params);
		
		return [
			'result'   =>  -9999,
			'message'  => 'account created successfully',
			'redirect' => '/account/list'
		];
	}
	
    function _getSupportedTimeZones(&$info) {
        $timezone_identifiers = DateTimeZone::listIdentifiers();
        foreach($timezone_identifiers as $name) {
            $info[$name] = $name;
        }
    }
	
	// to do: specify a prefix for fieldset - add to all "name" parameters
	//  and name and id to account.profile entries
	public function AccountProfile(&$res, $id, $editable, $full = FALSE /* is POST */) {
	
		// load user record
		$data = $this->Query('select * from users')
				->where(['user_id' => $id])
				->first();
        hook_execute('login.deets', $data);
		$set1 = [
			[ 'type' => 'string', 'id' => 'account_name_first',	'name' => 'account[name_first]',	'caption' => 'First Name',	'encrypted' => TRUE],
			[ 'type' => 'string', 'id' => 'account_name_last',	'name' => 'account[name_last]', 	'caption' => 'Last Name', 	'encrypted' => TRUE],
			[ 'type' => 'string', 'id' => 'account_primary_email',	'name' => 'account[primary_email]','caption' => 'Primary Email'],	// used for notifications, not for login.
		];

		$fields = [ ['name' => 'Basic Info', 'fieldlist' => $set1] ];
		$groups = hook_execute('group.enum', FALSE);
        if ($editable) {
            array_unshift($set1, [ 'type' => 'string', 'id' => 'account_level',	'name' => 'account[level]',		'caption' => 'Account Type']);	// 11 = setup, 10 = admin, 9 = staff, 1 = registered, 0 = anonymous guest
            $res['account[level]'] = isset($groups[$data->level]) ? $groups[$data->level] : $data->level;
        }
		$res['account[name_first]'] = $data->name_first;
		$res['account[name_last]'] = $data->name_last;
		$res['account[primary_email]'] = $data->primary_email;
		$res['account[is_locked]'] = $data->is_locked;
		$res['id'] = $id;
		$res['auth'] = hook_execute('nonce.create', FALSE, 'account', $id);
		
        if ($editable) {
            // Add User-based settings
            $GLOBALS['loader']->GetUserSettingsDefinition($fields);
            $this->_loadUserSettings();

            // update ids and names, load values
            foreach($fields as &$fieldset) {
                if (isset($fieldset['prefix'])) {
                    $prefix = $fieldset['prefix'];
                    foreach($fieldset['fieldlist'] as &$entry) {
                        $name = $entry['name'];
                        $entry['id'] = $prefix . '_' . $name;
                        
                        if (isset($this->user_settings[$prefix][$name])) {
                            $original = $this->user_settings[$prefix][$name];
                            if ($full) {
                                $entry['old'] = $original;
                            } else {
                                if ($entry['type'] == 'text')  $original = str_replace(["\r\n", "\n"], ["\r", "\r"], $original);
                            }
                        } else {
                            $original = '';
							if (isset($entry['default'])) {
								$original = $entry['default'];
								unset($entry['default']);
							}
                        }
                        $entryname = $entry['name'] = $prefix . '[' . $name . ']';
                        $res[$entryname] = $original;
						if (isset($entry['options']) && is_string($entry['options'])) {
							switch($entry['options']) {
								case '{timezone}':
									$entry['options'] = [];
									$this->_getSupportedTimeZones($entry['options']);
									break;
							}
							
						} elseif (isset($entry['filter'])) {
                            $options = hook_execute($entry['filter'], []);
                            $entry['options'] = [];
                            foreach($options as $item) {
                                $entry['options'][$item['key']] = $item['value'];
                            }
                            unset($entry['filter']);
                        }
                    }
                }
            }
			// can this be locked?
			$your_level = $_SESSION['impersonate']['u_level'] ?? 0;
			$res['can_lock'] = ($your_level >= 8 && !$data->is_locked) ? 1 : 0;
        }
		
		if ((int)$data->is_locked) $res['is_locked'] = 'checked';

		$fields = hook_execute('account.edit.get', $fields);

		$res['fieldset'] = $fields;
		// to do: (when working, convert fieldsets to tabs)
	}

	public function saveUserSettings($definitions /* includes current values */, $module_name, $settings) {
		$this->_loadUserSettings();
	// find the definition that matches $module_name
		$current_definition = NULL;
		foreach($definitions['fieldset'] as $defitem) {
			$prefix = isset($defitem['prefix']) ? $defitem['prefix'] : '';
			if ($prefix == $module_name) {
				$current_definition = $defitem['fieldlist'];
				break;
			}
		}

		if ($current_definition === NULL) return;

		$changed = [];
		foreach($current_definition as $field_definition) {
			$fieldname = preg_match('~\[(.*)\]~', $field_definition['name'], $match) ? $match[1] : '';
			if ($fieldname != '') {
				$value = isset($settings[$fieldname]) ? $settings[$fieldname] : NULL;
				$oldvalue = isset($this->user_settings[$module_name][$fieldname]) ? $this->user_settings[$module_name][$fieldname] : NULL;
				if ($value != $oldvalue) {
					$changed[$fieldname] = $value;
				}
			}
		}
		$changed = hook_execute('account.edit.set', $changed);

		if (count($changed)) {
			hook_execute('config.set', $changed, $module_name);
		}
	}
	
	public function invite_popup($id, $auth) {
		return [
			'popup' => [
				'title' => 'Create Invitation',
				'template' => 'account_invitepop',
				'modal' => TRUE,
				'width' => 450,
				'id' => $id,
				'auth' => $auth,
			]
		];
	}
	
	public function invite_create($id, $email, $hostname) {
	// generate an invite link
		$random = openssl_random_pseudo_bytes(64);
		$hash = md5($random . ':invite' . time());
		
	// store in database_record (userid, date, hash, email)
		$node = new database_record($this);		// always assume this is a new record
		$node->TableName('users_invite');
		$node->ui_hash = $hash;
		$node->ui_date = time();	// expiry time?
		$node->ui_user_id = $id;
		$node->ui_email = $email;
		$node->SaveRecord();
	
	// generate and send email
		$data = ['link' => $hostname . '/account/invite/' . $hash];
		$m = new \mailobject();
		$sendresult = $m->template('account::invite')
			->to($email)
//			->css($css)
			->data($data)
			->Send();
		$result = [];
		if (is_string($sendresult)) {
			$result['result'] = 1730;
			$result['message'] = $sendresult;
		} else {
			$result['debug'] = $sendresult;
		}
		return $result;
	}
	
	public function invite_get($hash) {
		$invite = $this->Query('select * from users_invite')->where('ui_hash=%s', $hash)->first();
		return $invite;
	}

	private function _loadUserSettings($user_id = 0) {
		if (!$user_id) $user_id = $this->user_id;
		if ($user_id == $this->current_user) return;		// details for user already loaded
		
		$this->user_settings = [];
		$this->current_user = $user_id;
		
		$modules = $GLOBALS['loader']->GetModulesWithUserSettings();
		if (count($modules)) {
			foreach($modules as $module) {
				$hash['M:' . md5($module)] = $module;
			}
            $settings = $this->Query('select * from users_data')
                    ->where(['ud_user_id' => $this->current_user])
                    ->whereIn('ud_key', array_keys($hash))
                    ->get();
            if (is_object($settings)) {
                foreach($settings as $item) {
                    $itemhash = $item->ud_key;
                    $module = $hash[$itemhash];
                    $data = unserialize($item->ud_text);
                    $this->user_settings[$module] = $data;
                }
            }
			foreach($modules as $module) {
				if (!isset($this->user_settings[$module])) $this->user_settings[$module] = [];
			}
        }
	}
    
    public function UserSettingsGet(&$data, $module_name, $user_id = 0) {
		$this->_loadUserSettings($user_id);
		
		$module_info = $GLOBALS['loader']->GetModuleInfo($module_name);
		$info = $module_info['.info'];
		if (!isset($info['user_settings'])) return;	// no user settings for this module
		
		if (!isset($this->user_settings[$module_name])) return; 	// no settings for this module
		// move just the settings we know about to $data
		if (isset($info['user_settings'])) {
			foreach($info['user_settings'] as $field_def) {
				$key = $field_def['name'];
				if (array_key_exists($key, $this->user_settings[$module_name])) {
					$data[$key] = $this->user_settings[$module_name][$key];
				}
			}
		}
    }

    public function UserSettingsSet($data, $module_name) {
		$module_info = $GLOBALS['loader']->GetModuleInfo($module_name);
		$info = $module_info['.info'];
		if (isset($info['user_settings'])) {
			$changed = FALSE;
			foreach($info['user_settings'] as $field_def) {
				$key = $field_def['name'];
				if (array_key_exists($key, $data)) {		// value has changed
					$this->user_settings[$module_name][$key] = $data[$key];
					$changed = TRUE;
				}
			}
			if ($changed) {
				$hash = 'M:' . md5($module_name);
				$this->Tablename('users_data')->InsertOrUpdate([
						'ud_user_id' => $this->user_id,
						'ud_key' => $hash,
					], [
						'ud_text' => serialize($this->user_settings[$module_name]),
					]); 
			}
		}
	}
	
	public function GetUserData($user_id, $data_prefix) {
		$rows = $this->Query('select * from users_data')
				->where(['ud_user_id' => $this->current_user])
				->where('ud_key like %s', $data_prefix . '.%')
				->get();
		$result = [];
		foreach($rows as $row) {
			var_dump($row);
		}
		return $result;
	}

	public function updateuser($user_id){
		$get=$this->Tablename('users')->InsertOrUpdate([
						'user_id' => $user_id,
					], [
						'is_locked' => '1',
					]); 
	
		return true;
	}
		
}