<?php

header('Content-Type: application/json');

class app_base_class {

    /** @property db_standalone $db */
    public $db = null;
    public $header_data = array();
    public $_body = array();
    public $login_data = array();

    //
    public function __construct() {
        $this->db = new db_standalone(null, DB_HOST, DB_USER, DB_PASS, DB_NAME);
        $this->db->query_prepare_init("set global sql_mode = ''");  //to stop strict mode
        //
        if (CMS__APP_TYPE == 'api')
            define('CMS__APP_EXPIRY_MINUTE', 14400/* 10 days */);
        else
            define('CMS__APP_EXPIRY_MINUTE', null);
    }

    //this method needed to prevent multi requests at same time, like from app. if add invoice called multi times
    //to don't insert same invoice multi time
    public function init_app_unique() {
        if (empty($_REQUEST['action']))
            return;
        //for api now we exclude it as maybe as api call it multi times as same time in case of website developer
        //call the api if website visibtor make specific request like 2 visitors create invoice at same time.
        $app_unique_methods = array(
            'distribution' => array('add_customers', 'add_invoices', 'add_requests', 'add_payments', 'add_damages', 'add_visits', 'add_sales')
            , 'attendance' => array('add_attendance', 'add_fingerprint')
            , 'sales_crm' => array('insert_customer_note', 'start_customer_visit', 'update_customer_visit', 'end_customer_visit')
        );
        //
        if (!empty($app_unique_methods[CMS__APP_TYPE]) && in_array($_REQUEST['action'], $app_unique_methods[CMS__APP_TYPE])) {
            $this->db->query_execute("insert into `tbl_record_unique` (`user_id`, `record_type`, `record_action`, `record_time`)
                values (?,?,?,?)", array($this->get_login_user_id(), CMS__APP_TYPE, $_REQUEST['action'], date('Y-m-d H:i:s')));
            //here we stop below in case of error as already in the db class we die..
            //return $this->die_error('Another Request for same action already running..');
        }
    }

    //after finish request delete the saved record for unique id so can process new request after that
    //we put it in destruct so when finish script or on any error so we delete the record.
    public function __destruct() {
        if (!empty($_REQUEST['action']))
            $this->db->query_execute("delete from `tbl_record_unique` where `user_id` = ? and `record_type` = ?
                and `record_action` = ? ", array($this->get_login_user_id(), CMS__APP_TYPE, $_REQUEST['action']));
    }

    //
    public function die_error($error_msg) {
        header("HTTP/1.0 500 Internal Server Error");
        die(json_encode(array('error_info' => $error_msg)));
    }

    //
    public function set_body_data($body_data) {
        $this->_body = $body_data;
        return $this;
    }

    /**
     * Functions related to login & related data
     * ====================================================================>
     */
    public function set_login_data($login_data) {
        $this->login_data = $login_data;
        return $this;
    }

    //
    public function get_login_data($key = null, $sub_key = null) {
        if (empty($key))
            return $this->login_data;
        if (func_num_args() > 1)
            return ((isset($this->login_data[$key]) && isset($this->login_data[$key][$sub_key])) ? $this->login_data[$key][$sub_key] : null);
        return (isset($this->login_data[$key]) ? $this->login_data[$key] : null);
    }

    //
    public function get_login_user_id() {
        return $this->get_login_data('User_ID');
    }

    //
    public function login() {
        $app_type = CMS__APP_TYPE;
        if (empty($this->_body['username']) || empty($this->_body['password']))
            $this->die_error('please send username and password.');
        //from app unique_id
        $app_unique_id = empty($this->_body['unique_id']) ? null : $this->_body['unique_id'];
        $username = $this->_body['username'];
        $password = (empty($this->_body['password']) ? (empty($this->_body['encrypted_password']) ? '' : $this->_body['encrypted_password']) : md5(sha1(md5($this->_body['password']))));
        //1: distribution | 2:attendance | 3:api | 4:sales_crm |
        $app_type_arr = array('distribution' => 1, 'attendance' => 2, 'api' => 3, 'sales_crm' => 4);
        if (isset($app_type_arr[$app_type]))
            $user_app_type = $app_type_arr[$app_type];
        else
            $user_app_type = $app_type_arr['distribution'];
        //
        $stmt = $this->db->query_execute("SELECT u.* FROM tbl_common_users u
                WHERE BINARY User_Name = BINARY ? AND BINARY User_Password = BINARY ? AND u.Status_ID = 1 
                and u.is_app = '{$user_app_type}' ", array($username, $password));
        if ($this->db->query_result_count($stmt) <= 0)
            $this->die_error('wrong username or passsword.');
        $user_row = $this->db->query_fetch($stmt);
        //for distribution & attendance & sales_crm user is unique so must be used in unique device
        if ($app_type != 'api') {
            //here we check if app unique in user row empty then this user is first time login we allow it
            //if not empty it must be same as the app unique id send from the app. device
            if (empty($app_unique_id) || (!empty($user_row['app_unique_id']) && $user_row['app_unique_id'] != $app_unique_id))
                $this->die_error('username is related to another device.');
            //check if this device already related to another user
            $query_unique = $this->db->query_execute('select User_Name from tbl_common_users where app_unique_id = ? and User_ID <> ?', array($app_unique_id, $user_row['User_ID']));
            if ($this->db->query_result_count($query_unique) > 0) {
                $user_name = $this->db->query_fetch($query_unique)['User_Name'];
                $this->die_error('This device is related to ' . $user_name . '.');
            }
            $user_row['is_new_device'] = 0;
            if (empty($user_row['app_unique_id'])) {
                $user_row['is_new_device'] = 1;
                $user_row['app_unique_id'] = $app_unique_id;
            }
            $firebase_token = empty($this->_body['firebase_token']) ? null : $this->_body['firebase_token'];
            $this->db->query_execute('update tbl_common_users set app_unique_id = ?, token = ?, last_login_time = ? where User_ID = ? '
                    , array($app_unique_id, $firebase_token, date('Y-m-d H:i:s'), $user_row['User_ID']));
        }
        //
        if (!empty($user_row['json_data'])) {
            $user_row['json_data'] = json_decode($user_row['json_data'], true);
            if ($user_row['json_data'] === false || json_last_error() !== JSON_ERROR_NONE || !is_array($user_row['json_data']))
                $user_row['json_data'] = array();
            foreach ($user_row['json_data'] as $key => $val)
                $user_row['json__' . $key] = $val;
        } else
            $user_row['json_data'] = array();
        //
        //get the permissions for the app.
        $user_row['app_permissions'] = $this->get_role_app_permissions($app_type, $user_row['Role_ID']);
        //
        $usr_login_arr = Person::get_login_row(" and u.User_ID = '{$user_row['User_ID']}' ");
        $user_row['user_login_arr'] = $usr_login_arr;
        //need it for accounting in distribution & web api
        $stmt = $this->db->query_execute("select * from tbl_users_accounting_accounts where User_ID = ? order by User_Account_ID desc limit 1", array($user_row['User_ID']));
        $row = $this->db->query_fetch($stmt);
        if (empty($row) || empty($row['serial_prefix']))
            $this->die_error('Must Fill [Serial Prefix, Depot, Price Class, User Cash Account, User Check Account] in User Account.');
        /**
         * 
         */
        if (empty($row['class_id'])) //here if zero we send it empty since we check it inside app.
            $row['class_id'] = '';
        if ($app_type == 'distribution') {
            //get the default depot if this user is not related with specific one
            $stmt = $this->db->query_execute("select * from tbl_default_settings where setting_name in ('default_depot','pricing_class_id','app_print_is_hide_vat','app_print_is_hide_header','app_allow_all_customers','app_auto_product_on_scan_barcode','app_allow_minimum_class')");
            while ($tmp_row = $this->db->query_fetch($stmt)) {
                if ($tmp_row['setting_name'] == 'default_depot')
                    $row['default_depot_id'] = $tmp_row['setting_value'];
                elseif ($tmp_row['setting_name'] == 'pricing_class_id')
                    $row['default_class_id'] = $tmp_row['setting_value'];
                elseif (substr($tmp_row['setting_name'], 0, 4) == 'app_')
                    $row[substr($tmp_row['setting_name'], 4)] = $tmp_row['setting_value'];
            }
            //
            if (empty($row['depot_id']))
                $row['depot_id'] = $row['default_depot_id'];
            $row['is_allowed_request'] = '1';
            if ($row['depot_id'] == $row['default_depot_id'])
                $row['is_allowed_request'] = '0';
            //here don't overwrite $row class_id if empty, since if user class empty we take customer class id and if it is empty we take thiis default class id
            //get the default price class, it is the minimum id --- now we stop below and get it from above default settings table
//            $stmt = $this->db->query_execute("select Class_ID from tbl_pricing_class where type=0 order by Class_ID asc limit 1");
//            $tmp_row = $this->db->query_fetch($stmt);
//            $row['default_class_id'] = $tmp_row['Class_ID'];
            //
            if (empty($row['depot_id']) || (empty($row['class_id']) && empty($row['default_class_id'])) || empty($row['cash_account_id']) || empty($row['cheque_account_id']))
                $this->die_error('Must Fill [Serial Prefix, Depot, Price Class, User Cash Account, User Check Account] in User Account.');
            //
            $def_curr_row = $this->get_default_currency();
            $user_row['default_currency_id'] = $def_curr_row['Currency_ID'];
            $user_row['default_currency_name'] = $def_curr_row['Currency_Name'];
            $user_row['default_currency_symbol'] = $def_curr_row['Currency_Symbol'];
        }
        /**
         * 
         */
        elseif ($app_type == 'api') {
            $stmt = $this->db->query_execute("select * from tbl_web_api_users where app_user_id = ? order by user_id desc limit 1", array($user_row['User_ID']));
            $wb_usr_row = $this->db->query_fetch($stmt);
            if (empty($wb_usr_row))
                $this->die_error('Must Fill Data in Web API User.');
            foreach ($wb_usr_row as $key => $val) {
                if ($key == 'other_data') {
                    $row['api__' . $key] = empty($val) ? array() : json_decode($val, true);
                    if (empty($row['api__' . $key]))
                        $row['api__' . $key] = array();
                }
                //if depot, price class filled in web api then take it else take the user account else if empty take default
                elseif ($key == 'depot_id' || $key == 'class_id') {
                    if (!empty($val))
                        $row[$key] = $val;
                } else
                    $row['api__' . $key] = $val;
            }
            //get the default depot if this user is not related with specific one
            $stmt = $this->db->query_execute("select * from tbl_default_settings where setting_name in ('default_depot' ,'pricing_class_id'
                    , 'product_param1_desc', 'product_param2_desc', 'product_param3_desc', 'product_param4_desc')");
            while ($tmp_row = $this->db->query_fetch($stmt)) {
                if ($tmp_row['setting_name'] == 'default_depot')
                    $row['default_depot_id'] = $tmp_row['setting_value'];
                elseif ($tmp_row['setting_name'] == 'pricing_class_id')
                    $row['default_class_id'] = $tmp_row['setting_value'];
                elseif (preg_match('/^product_param\d+\_desc$/', $tmp_row['setting_name']) && !empty($tmp_row['setting_value']))
                    $row[$tmp_row['setting_name']] = $tmp_row['setting_value'];
            }
            if (empty($row['depot_id']))
                $row['depot_id'] = $row['default_depot_id'];
            if (empty($row['class_id']))
                $row['class_id'] = $row['default_class_id'];
            $row['default_class_id'] = $row['class_id'];
            //
            if (empty($row['depot_id']) || empty($row['class_id']) || empty($row['cash_account_id']) || empty($row['cheque_account_id']))
                $this->die_error('Must Fill [Serial Prefix, Depot, Price Class, User Cash Account, User Check Account] in User Account & Web API.');
            //
            $stmt = $this->db->query_execute("select p.Currency_ID, Currency_Name, p.is_ttc, if(Currency_Symbol='', Currency_Code, Currency_Symbol) as Currency_Symbol 
                    from tbl_pricing_class p
                        inner join tbl_common_currency c on p.Currency_ID = c.Currency_ID 
                    where Class_ID = ? ", array($row['default_class_id']));
            $curr_row = $this->db->query_fetch($stmt);
            $user_row['default_currency_id'] = $curr_row['Currency_ID'];
            $user_row['default_currency_name'] = $curr_row['Currency_Name'];
            $user_row['default_currency_symbol'] = $curr_row['Currency_Symbol'];
            $user_row['default_class_is_ttc'] = $curr_row['is_ttc'];
        }
        /**
         * 
         */
        elseif ($app_type == 'attendance') {
            $stmt = $this->db->query_execute("select * from tbl_default_settings where setting_name in ('app_att_max_enroll_count', 'app_att_success_verification_score', 'app_att_required_location')");
            while ($tmp_row = $this->db->query_fetch($stmt)) {
                if (substr($tmp_row['setting_name'], 0, 4) == 'app_')
                    $row[substr($tmp_row['setting_name'], 4)] = $tmp_row['setting_value'];
            }
        }
        //
        $org_stmt = $this->db->query_execute("SELECT o.*, CONCAT(ct.Name,', ',p.Organization_Street) AS Org_Address
                            FROM tbl_organization o
                                LEFT JOIN tbl_organization_postal_address p ON p.Organization_ID = o.Organization_ID
                                LEFT JOIN tbl_all_cities ct ON p.Organization_City = ct.ID
                            order by Organization_ID asc limit 1");
        $org_row = $this->db->query_fetch($org_stmt);
        $row['org_name'] = $org_row['Organization_Name'];
        $row['org_phone'] = $org_row['Organization_Phone'];
        $row['org_address'] = $org_row['Org_Address'];
        $row['org_email'] = $org_row['Organization_Email'];
        $row['org_cr'] = $org_row['Organization_Commercial_Registry'];
        $row['org_upload_url'] = upload_url;
        //
        $user_row = array_merge($row, $user_row);
        $user_row['cms__trusto_version'] = trusto_VERSION;
        $user_row['cms__app_version'] = CMS__APP_VERSION;
        $user_row['cms__login_time'] = date('Y-m-d H:i:s');
        //
        $exp_after_in_sec = null;
        if (defined('CMS__APP_EXPIRY_MINUTE') && CMS__APP_EXPIRY_MINUTE > 0)
            $exp_after_in_sec = CMS__APP_EXPIRY_MINUTE * 60;
        $access_token = my_jwt::my_encode($user_row, $exp_after_in_sec);
        //
        die(json_encode(array('data' => array('access_token' => $access_token, 'user_data' => $user_row))));
    }

    //update firebase for send notifications
    public function update_firebase_token() {
        $firebase_token = empty($this->_body['firebase_token']) ? null : $this->_body['firebase_token'];
        $this->db->query_execute('update tbl_common_users set token = ? where User_ID = ? '
                , array($firebase_token, $this->get_login_user_id()));
        die(json_encode(array('msg' => 'msg_firebase_token_update_success')));
    }

    //check if token is still valid or expired
    public function check_token_expiry() {
        $data = array('is_valid' => true);
        $login_time = $this->get_login_data('cms__login_time');
        //
        if (defined('CMS__APP_EXPIRY_MINUTE') && CMS__APP_EXPIRY_MINUTE > 0 && (empty($login_time) || (strtotime($login_time) + (CMS__APP_EXPIRY_MINUTE * 60)) < strtotime('now')))
            $data['is_valid'] = false;
        die(json_encode($data));
    }

    //
    public function renew_token() {
        if (empty($this->_body['app_unique_id']))
            $this->_body['app_unique_id'] = $this->get_login_data('app_unique_id');
        if (empty($this->_body['username']))
            $this->_body['username'] = $this->get_login_data('username');
        if (empty($this->_body['password']))
            $this->_body['encrypted_password'] = $this->get_login_data('User_Password');
        //
        $this->login();
    }

    //
    public function change_password() {
        if (empty($this->_body['new_password']))
            $this->die_error('msg_fill_new_password');
        $new_password = md5(sha1(md5($this->_body['new_password'])));
        $user_id = $this->get_login_user_id();
        $stmt = $this->db->query_execute("update `tbl_common_users` set User_Password = ? 
                where User_ID = ? ", array($new_password, $user_id));
        die(json_encode(array('msg' => 'msg_change_password_success')));
    }

    //we need this ft. to get for login user app. related permissions
    public function get_role_app_permissions($app_type, $role_ids) {
        $role_prmsns = array();
        $stmt = $this->db->query_execute("select Permission_ID from tbl_common_permission where Permission_Name = '{$app_type}' ");
        $row = $this->db->query_fetch($stmt);
        if (empty($row))
            return $role_prmsns;
        $main_prmsn_id = $row['Permission_ID'];
        $all_prmsns = array();
        Permissions::getChildPermissions($main_prmsn_id, $all_prmsns, false);
        if (empty($all_prmsns))
            return $role_prmsns;
        $stmt = $this->db->query_execute("select distinct Permission_ID from tbl_common_role_permission where Role_ID in ({$role_ids}) 
                            and Permission_ID in (" . implode(',', array_keys($all_prmsns)) . ") ");
        while ($row = $this->db->query_fetch($stmt))
            $role_prmsns[] = $all_prmsns[$row['Permission_ID']];
        return $role_prmsns;
    }

    public function check_app_permission($prmsn) {
        return (in_array($prmsn, $this->get_login_data('app_permissions')));
    }

    //we need this ft. as needed like logged id, current year and others inside some method
    public function fill_trusto_session() {
        if (isset($this->is_trusto_session) && $this->is_trusto_session == true)
            return;
        //we simulate the call of login() inside 'libs\Common\Person.php'
        $row = $this->get_login_data('user_login_arr');
//        //we keep it for now for already logged in users, but we can stop it
//        if (empty($row)) {
//            $row = array();
//            $row[0] = $this->get_login_user_id();
//            $row[1] = $this->get_login_data('User_Name');
//            $row[2] = ''; //password
//            $row[3] = $this->get_login_data('User_Name'); //first name
//            $row[4] = 'App.'; //last name
//            $row[5] = $this->get_login_data('Role_ID');
//            $row[6] = $this->get_login_data('User_Full_Name');
//            $row[7] = $this->get_login_data('control_role_id');
//            $row[8] = $this->get_login_data('limited_access');
//            $row[11] = $this->get_login_data('login_device');
//            $row['Language_Code'] = 'en';
//        }
        $serialized_permission = new Permissions($row['User_ID'], true);
        $row['permissions'] = $serialized_permission->getPermissions();
        Session::create($row);
        $this->is_trusto_session = true;
    }

    /**
     * Common Protected Internal Used Functions
     * ====================================================================>
     */
    protected function get_default_currency() {
        $stmt = $this->db->query_execute("select setting_value as Currency_ID, curr.Currency_Name,
            if(curr.Currency_Symbol='', curr.Currency_Code, curr.Currency_Symbol) as Currency_Symbol 
            from tbl_default_settings 
                inner join tbl_common_currency as curr on setting_value = curr.Currency_ID where setting_name='local_currency'");
        $row = $this->db->query_fetch($stmt);
        if (empty($row['Currency_ID']))
            $row['Currency_ID'] = 84;
        return $row;
    }

    //
//    protected function get_default_uom($prod_id) {
//        static $def_uom_ids = array();
//        if (!isset($def_uom_ids[$prod_id])) {
//            $stmt = $this->db->query_execute("select UOM_ID from tbl_stock_products where Product_ID = ? ", array($prod_id));
//            $pro_row = $this->db->query_fetch($stmt);
//            $def_uom_ids[$prod_id] = $pro_row['UOM_ID'];    //we save them if use it again
//        }
//        return $def_uom_ids[$prod_id];
//    }
    //
    protected function select_query($query, $bind_arr = null) {
        $stmt = $this->db->query_execute($query, $bind_arr);
        $arr = array();
        while ($row = $this->db->query_fetch($stmt))
            $arr[] = $row;
        return $arr;
    }

    //
    protected function insert_header($table, $fields_arr) {
        $query_header = null;
        foreach ($fields_arr as $field => $app_field)
            $query_header .= (empty($query_header) ? '' : ', ') . "`" . (is_numeric($field) ? $app_field : $field) . "`";
        $query_header = "insert into `{$table}` ({$query_header}) ";
        return $query_header;
    }

    //
    protected function insert_query($table, $fields_arr, $data_arr) {
        $query_header = $this->insert_header($table, $fields_arr) . " values ";
        //
        $query_body = null;
        $i = 0;
        foreach ($data_arr as $data_row) {
            $query_body .= ($i == 0 ? '' : ',');
            $query_body .= ' (';
            $j = 0;
            foreach ($fields_arr as $app_field)
                $query_body .= ($j++ == 0 ? '' : ', ') . " '" . $this->db->escape($data_row[$app_field]) . "'";
            $query_body .= ')';
            $i++;
            if ($i >= 200) {
                $this->db->query_execute($query_header . ' ' . $query_body);
                $query_body = null;
                $i = 0;
            }
        }
        if (!empty($query_body))
            $this->db->query_execute($query_header . ' ' . $query_body);
    }

}
