<?php

namespace Hura8\System\Model;


use Hura8\Database\iConnectDB;
use Hura8\System\IDGenerator;

class AuthModel
{

    /* @var iConnectDB $db */
    protected $db;

    private $tb_login = '';
    private $tb_access_code = '';

    private $tb_onetime_key = '';

    public function __construct($tb_login, $tb_access_code)
    {
        $this->tb_login         = $tb_login;
        $this->tb_access_code   = $tb_access_code;
        $this->db = get_db('', ENABLE_DB_DEBUG);
    }


    private const ACCESS_CODE_LENGTH = 30;
    const ONE_TIME_KEY_LENGTH = 15;


    public function checkOneTimeKey($auth_key) {
        $db_response = $this->db->select(
            $this->tb_onetime_key,
            ['user_id', 'user_name', 'client_id', 'create_time'],
            [
                'auth_key' => ["=", $auth_key],
            ],
            '',
            1
        );

        if($db_response->getCode()) {
            return null;
        }

        $info = $db_response->getData();

        if($info) {

            // used ONCE and delete the key
            $this->db->runQuery("DELETE FROM `".$this->tb_onetime_key."` WHERE `user_id` = ? ", ['s'], [$info['user_id']]);

            return $info;
        }

        return false;
    }


    // auth key allows users to export data (i.e. export to excel) for offline use
    public function createNewOneTimeKey($user_id) {

        // and make a new one
        $auth_key    = IDGenerator::createStringId(self::ONE_TIME_KEY_LENGTH);

        $this->db->insert($this->tb_onetime_key, [
            'user_id'       => $user_id,
            'auth_key'      => $auth_key,
            'create_time'   => CURRENT_TIME,
        ]);

        return $auth_key;
    }


    // for all subsequent requests, API need to provide access-code in the request's header
    // the server will verify the code
    public function checkAccessCode($access_code) {
        return $this->db->select(
            $this->tb_access_code,
            ['user_id', 'create_time'],
            [
                'access_code' => ["=", $access_code],
            ],
            '',
            1
        );
    }


    public function deleteAllAccessCode($user_id) {
        $this->db->runQuery("DELETE FROM `".$this->tb_access_code."` WHERE `user_id` = ? ", ['s'], [$user_id]);
    }


    protected function createNewAccessCode($user_id) {

        // when use login here, delete all other access code
        $this->deleteAllAccessCode($user_id);

        // and make a new one
        $access_code    = IDGenerator::createStringId(self::ACCESS_CODE_LENGTH);
        $user_device    = $_SERVER['HTTP_USER_AGENT'] ?? 'unknown';

        $db_response = $this->db->insert($this->tb_access_code, [
            'user_id'       => $user_id,
            'access_code'   => $access_code,
            'user_device'   => substr($user_device, 0, 150),
            'create_time'   => CURRENT_TIME,
        ]);

        return $access_code;
    }

    /**
     * allow to login via mobile
     * - step 1: sms an OTP code to mobile number
     * - step 2: verify OTP code (mobile number and otp code sent along in the form)
     */

    public function checkLoginViaMobile($mobile, $otp) {
        // todo
    }

    /**
     * @description An OTP code is sent to user's email. This method helps user need not remember the password
     * @param $email string
     * @param $otp string
     */

    public function checkLoginByOTP($user_id, $otp) {
        $info = $this->db->select(
            $this->tb_login,
            [],
            [
                'user_id' => ["=", $user_id],
                'login_otp' => ["=", $otp],
            ],
            '',
            1
        );

        if($info) {
            // return to browser
            return array(
                'user_id'       => $user_id,
                'access_code'   => $this->createNewAccessCode($user_id),
            );
        }

        return false;
    }


    public function createLoginOTP($user_id) {
        // check email exist
        $info = $this->db->select(
            $this->tb_login,
            [],
            [
                'user_id' => ["=", $user_id],
            ],
            '',
            1
        );

        if($info) {
            $otp = IDGenerator::createStringId(6);
            $this->db->update(
                $this->tb_login,
                [
                    'login_otp' => $otp
                ],
                [
                    'user_id' => $info['user_id'],
                ]
            );

            return $otp;
        }

        return false;
    }


    public function createOrUpdatePassword($user_id, $new_password) {
        $query = $this->db->runQuery(
            "SELECT `user_id` FROM `".$this->tb_login."` WHERE `user_id` = ? LIMIT 1 ",
            ['d'], [ $user_id ]
        );

        if($this->db->fetchAssoc($query)) {
            return $this->updatePassword($user_id, $new_password);
        }

        return $this->createPassword($user_id, $new_password);
    }


    protected function createPassword($user_id, $new_password) {
        return $this->db->insert(
            $this->tb_login,
            [
                'user_id' => $user_id,
                'password_hash' => $this->hashPassword($new_password),
                'create_time' => CURRENT_TIME,
                'create_by' => '',
            ]
        );
    }


    protected function updatePassword($user_id, $new_password) {
        return $this->db->update(
            $this->tb_login,
            [
                'password_hash' => $this->hashPassword($new_password),
                'last_update' => CURRENT_TIME,
                'last_update_by' => ADMIN_NAME,
            ],
            [
                'user_id' => $user_id,
            ]
        );
    }


    /**
     * @param $user_id int
     * @param $password string
     * @return array|false
     */
    public function checkLogin($user_id, $password) {

        $info = $this->db->select(
            $this->tb_login,
            [ 'password_hash'],
            [
                'user_id' => ["=", $user_id],
            ],
            '',
            1
        );

        //test password
        if($info && $this->verifyHash($password, $info['password_hash'])) {

            $this->updateUserLogin($user_id);

            // return to browser
            return array(
                'user_id'       => $user_id,
                'access_code'   => $this->createNewAccessCode($user_id),
            );
        }

        return false;
    }


    private function updateUserLogin($user_id){
        return $this->db->update(
            $this->tb_login,
            [
                'last_login_time' => CURRENT_TIME,
                'last_login_ip' => USER_IP,
                'last_login_device' => '',
                'last_login_session_id' => \Hura8\System\Security\Session::id() ?: '',
                'last_login_browser' => USER_AGENT,
            ],
            [
                'user_id'   => $user_id,
            ]
        );
    }

    /**
     * @param $str string
     * @return string
     */
    private function hashPassword($str) {
        return password_hash($str, PASSWORD_BCRYPT, array('cost' => 12 ));
    }

    /**
     * 15-04-2016 verify string with given hash
     * @param $str_to_verify string
     * @param $hash string
     * @return boolean
     */
    private function verifyHash($str_to_verify, $hash) {
        return (password_verify($str_to_verify, $hash));
    }


}
