<?php

/**
 * users table's controller.
 *
 * Used to perform operations on users table.
 *
 * @author Mario Vallejo <wappsi.desarrollomovil01@gmail.com>
 */

namespace App\Controllers;

//______________________________________________________________________________________________________________
//
//                                             Importations
//_______________________________________________________________________________________________________________

use App\Controllers\SmaPosRegister;

// use App\Helpers\JWThelpers;
// use Exception;

use App\Models\SmaCompaniesModel;
use App\Models\SmaUsersModel;
use App\Models\SmaWarehousesModel;
use App\Models\WappsiUsers;
use App\Models\WappsiUsersModel;
// use App\Models\SmaSettingsModel;
// use App\Models\SmaLoginAttempsModel;
// use App\Models\SmaPosRegisterModel;

use CodeIgniter\RESTful\ResourceController;

// test

use Config\Services;
use Exception;
use Firebase\JWT\JWT;

// require_once '/../../vendor/firebase/php-jwt/src/JWT.php';

class SmaUsers extends ResourceController
{


    /**
     * Decrypt pass from WappsiPOSApp.
     *
     * 
     *
     * @return  string
     */
    private static function decrypt($pass)
    {
        $encodedKey = Services::getEncodedPasswordKey();
        $encodedIv = Services::getEncodedPasswordIv();
        $key = base64_decode($encodedKey);
        // $iv = base64_decode($encodedIv);
        $iv = $encodedIv;

        $method = 'aes-256-cbc';
        $decrypted = openssl_decrypt($pass, $method, $key, 0, $iv);
        // echo $decrypted;

        return $decrypted;
    }

    /**
     * Hash password.
     *
     * Using stored password on db (salt is stored inside db_password), hash given password to compare
     * against db_password
     *
     * @return string 
     */
    private function getPasswordHash($password, $db_password)
    {

        $salt_length = 10;
        // get salt from hashed_password in db
        $salt = substr($db_password, 0, $salt_length);

        //with we generate password to compare against db password
        $hashed_password = $salt . substr(sha1($salt . $password), 0, -$salt_length);

        // echo $hashed_password, ' --- ', $password, '------ ', $salt;
        return $hashed_password;
    }

    /**
     *    Return hashed password string
     *    @return String
     */
    private static function hashPassword(String $password)
    {

        $salt_length = 10;
        //salt to gen password
        // print_r($password);
        $random_hex = substr(md5(uniqid(rand(), true)), 0, $salt_length);
        // get salt from random hashed_password
        $salt = substr($random_hex, 0, $salt_length);

        //pasword hash generated using wappsi method
        $hashed_password = $salt . substr(sha1($salt . $password), 0, -$salt_length);

        // echo $hashed_password, ' --- ', $password, '------ ', $salt;
        return $hashed_password;
    }

    /**
     * Perform login operation.
     *
     * Given user and pasword on request, verify if user exist and if password is correct.
     * Generate and returns JWT for autentication on user requests.
     *
     * @return  HttpResponse
     */
    public function login()
    {

        $rules = [
            'username' => 'required',
            'password' => 'required',
        ];

        $message = [
            'username' => [
                'required' => 'Username required',
                'valid_username' => 'Nombre de usuario requerido',
            ],
            'password' => [
                'required' => 'Contraseña requerida',
            ],
        ];
        //______________________________________________________________________________________________________________
        //
        //                                           Request valitation
        //_______________________________________________________________________________________________________________

        // $json = json_decode(file_get_contents("php://input", true));
        // $json = $this->request->getJSON();
        // echo $json->username;
        // echo $this->request->getVar('username');

        if (!$this->validate($rules, $message)) {

            // if validation fails

            $response = [
                'status' => 400,
                'error' => true,
                'message' => $this->validator->getErrors(),
                'data' => [],
            ];

            return $this->respondCreated($response);
        } else {

            // we create an instance of User to validate user name
            $userModel = new WappsiUsersModel();
            $userdata = $userModel->where("username = '" . $this->request->getVar('username') . "'OR email ='" . $this->request->getVar('username') . "'")->first();
            // if empty means if not user found
            if (!empty($userdata)) {

                // if app request to override login_session

                if ($this->request->getVar('override_login') != null) {
                    if ($this->request->getVar('override_login')) {
                        $override_login = true;
                    } else {
                        $override_login = false;
                    }
                } else {
                    $override_login = false;
                }
                // echo $userdata['login_status'], '----' , $override_login, '-----' ,$userdata['login_status'];
                //check if user is already logged in

                // if not, decrypt password
                // $decryptedPassword = $this->request->getVar('password');
                $decryptedPassword = $this->decrypt($this->request->getVar('password'));

                if (isset($decryptedPassword)) {

                    // then we hash password to compare against DB
                    $hashed_password = $this->getPasswordHash($decryptedPassword, $userdata['password']);

                    // if password is equal
                    if ($hashed_password == $userdata['password']) {
                        // validate if user have company, biller and warehouse id defined.
                        // echo $userdata['login_status']==0 OR false;
                        if (($userdata['login_status'] == 1 and $override_login) or ($userdata['login_status'] == 0)) {
                            $userCompleteData = $this->getUserInformation($userdata['id']);
                            $usersDetailedData = $userCompleteData['user_data'];
                            // this validation should be replaced for if user_type is POS_user
                            // print_r($usersDetailedData);
                            if (
                                isset($usersDetailedData['warehouse_id']) && $usersDetailedData['warehouse_id'] != 0
                                && isset($usersDetailedData['seller_id']) && isset($usersDetailedData['biller_id'])
                                && $usersDetailedData['document_type_id'] != 0 && isset($usersDetailedData['document_type_id'])
                            ) {
                                // echo $key;

                                helper('jwt');
                                $user_key = getJWTUserKey();

                                $userModel->update($userdata['id'], ['jwt_key' => $user_key]);

                                $payloadData = [
                                    'user_id' => $userdata['user_data_id'],
                                    'id' => $userdata['id'],
                                    'user_key' => $userdata['jwt_key'],
                                    'company_folder' => $userdata['company_folder'],
                                    'host_url' => $userdata['host_url'],
                                    'db_group' => $userdata['db_group'],
                                    'view_right' => $usersDetailedData['view_right'],
                                ];

                                // echo $user_key;

                                helper('jwt');
                                $payload = genPayLoad($payloadData, $user_key);
                                // echo $payload['user_key'];
                                // print_r($payload);

                                helper('jwt');
                                // generate JWT
                                $token = getSignedJWTForUser($payload);
                                // print_r($token);
                                //TODO: REmove this from here
                                $usersDetailedData['token'] = $token;
                                // response data
                                $response = [
                                    'status' => 200,
                                    'error' => false,
                                    'message' => 'Usuario logueado satisfactoriamente',
                                    'access_token' => $token,
                                    'register_data' => $userCompleteData['register_data'],
                                    'permissions' => $userCompleteData['permissions'],
                                    'user_data' => $usersDetailedData,
                                ];

                                // set login_status to 1
                                $userModel->update($userdata['id'], ['login_status' => 1]);

                                return $this->respond($response, 200);
                            } else {

                                //if user is not POS_user
                                $error_detail = '';
                                if (!isset($usersDetailedData['warehouse_id']) || !($usersDetailedData['warehouse_id'] != 0)) {
                                    $error_detail = 'El warehouse_id no esta configurado correctamente.';
                                } elseif (!isset($usersDetailedData['seller_id'])) {
                                    $error_detail = 'El seller_id no esta configurado correctamente.';
                                } elseif (!isset($usersDetailedData['biller_id'])) {
                                    $error_detail = 'El biller_id no esta configurado correctamente.';
                                } elseif (!$usersDetailedData['document_type_id'] != 0 || !isset($usersDetailedData['document_type_id'])) {
                                    $error_detail = 'El document_type_id no esta configurado correctamente.';
                                }
                                $response = [
                                    'status' => 403,
                                    'error' => true,
                                    'config_error' => true,
                                    'message' => "$error_detail, consulte al administrador de sistema.",
                                ];
                                return $this->respondCreated($response);
                            }
                        } else {

                            $response = [
                                'status' => 401,
                                'error' => true,
                                'logged' => true,
                                'message' => 'El usuario ya se encuentra logueado.',
                                'data' => [],
                            ];
                            return $this->respondCreated($response);
                        }
                    } else {

                        $response = [
                            'status' => 402,
                            'error' => true,
                            'incorrect_password' => true,
                            'message' => 'Contraseña incorrecta',
                            'data' => [],
                        ];
                        return $this->respondCreated($response);
                    }
                }

                // $hashed_password = $db_password = $salt . substr(sha1($salt . $password), 0, -$this->salt_length);;

            } else {
                $response = [
                    'status' => 400,
                    'error' => true,
                    'user_not_found' => true,
                    'message' => 'Usuario no encontrado',
                    'data' => [],
                ];
                return $this->respondCreated($response);
            }
        }
    }

    public function getUserInformation($user_id)
    {
        $userModel = new WappsiUsersModel();
        $userdata = $userModel->where("id", $user_id)->first();
        $usersDataModel = new SmaUsersModel($userdata['db_group']);

        $usersDetailedData = $usersDataModel->where('id = ' . $userdata['user_data_id'])->first();
        // this validation should be replaced for if user_type is POS_user
        // print_r($usersDetailedData);

        // instance of company and warehouse models to get names
        $company = new SmaCompaniesModel($userdata['db_group']);

        $warehouse = new SmaWarehousesModel($userdata['db_group']);
        // get names
        $sellerData = $company->where('id', $usersDetailedData['seller_id'])->first();
        $billerData = $company->where('id', $usersDetailedData['biller_id'])->first();
        $companyData = $company->where('id', $usersDetailedData['company_id'])->first();

        // echo $userdata['company_id'];
        $warehouseData = $warehouse->where('id', $usersDetailedData['warehouse_id'])->first();

        //______________________________________________________________________________________________________________
        //
        //                                       VERIFY REGISTER STATUS
        //_______________________________________________________________________________________________________________

        $register_open = SmaPosRegister::verify($usersDetailedData['id'], $userdata['db_group'],);

        $permissions = SmaPermissions::getGroupPermissions($usersDetailedData['group_id'], $userdata['db_group'],);

        $usersDetailedData['warehouse_name'] = $warehouseData['name'] ?? 'Sin nombre';
        $usersDetailedData['biller_name'] = $billerData['name'] ?? $billerData['company'] ?? 'Sin nombre';
        $usersDetailedData['company_name'] = $usersDetailedData['company'] ?? $companyData['company'] ?? $companyData['name'];
        $usersDetailedData['seller_name'] = $sellerData['name'] ?? $sellerData['company'] ?? 'Sin nombre';

        return [
            'register_data' => $register_open,
            'permissions' => $permissions,
            'user_data' => $usersDetailedData,
            'user' => $userdata,
        ];
    }

    /**
     * Create a new user given user_data, if error return false if not return true
     * @param array $user_data
     * @return int|bool
     */
    public static function createUser($user_data, $db_group)
    {
        try {
            $user_model = new SmaUsersModel($db_group);

            // decrypt password
            $password = SmaUsers::decrypt($user_data['password']);
            // echo 'here';
            // print_r($user_data);
            // print_r($password);

            $user_data['password'] = SmaUsers::hashPassword($password);
            // return id of insert

            $user_id = $user_model->insert($user_data, true);

            return $user_id;
        } catch (\Throwable $th) {
            //throw $th;
            return false;
        }
    }

    //______________________________________________________________________________________________________________
    //
    //                                           GET TOKEN DATA
    //_______________________________________________________________________________________________________________

    public function getTokenData()
    {
        // key of JWT
        $key = Services::getJWTKey();
        $authHeader = $this->request->getServer('HTTP_AUTHORIZATION');
        helper('jwt');
        $token = getJWTFromRequest($authHeader);
        // $authHeader = $authHeader->getValue();

        try {
            // echo  $key, '---------' ,$token;
            $decoded = JWT::decode($token, $key, ['HS256']);

            if ($decoded) {
                $userCompleteData = $this->getUserInformation($decoded['id']);
                $response = [
                    'status' => 200,
                    'error' => false,
                    'message' => 'User data retrived',
                    'access_token' => $token,
                    'register_data' => $userCompleteData['register_data'],
                    'permissions' => $userCompleteData['permissions'],
                    'user_data' => $userCompleteData['user_data'],
                ];
                return $this->respondCreated($response);
            }


        } catch (Exception $ex) {
            return $this->failUnauthorized();
        }
    }

    /**
     * Perroms logout operation for an autenticated user.
     *
     * Takes JWT form request and logout user.
     *
     * @return HttpResponse
     */
    public function logout()
    {
        $authHeader = $this->request->getServer('HTTP_AUTHORIZATION');
        helper('jwt');
        $token = getJWTFromRequest($authHeader);

        // $token = $authHeader;

        try {
            // echo  $key, '---------' ,$token;
            helper('jwt');
            $decoded = dataFromJWT($token);

            // print_r($decoded);

            if ($decoded) {
                $userModel = new WappsiUsersModel();

                // change login_status and jwt_key
                if (isset($decoded->user_id) && !empty($decoded->user_id)) {
                    $userModel->update($decoded->user_id, ['login_status' => 0, 'jwt_key' => '']);
                }

                $response = [
                    'status' => 200,
                    'error' => false,
                    'message' => 'Cerrar sesión exitoso',
                    'data' => [],
                ];
                return $this->respondCreated($response);
            }
        } catch (Exception $ex) {

            return $this->respondNoContent('Token corrupto o expirado');
        }
    }


    /**
     * Refresh user JWT.
     * 
     * Refresh user JWT with a given .
     *
     * @return  HttpResponse
     */
    public function refreshToken()
    {
        $authHeader = $this->request->getServer('HTTP_AUTHORIZATION');
        helper('jwt');
        $token = refreshToken($authHeader);

        $response = [
            'status' => 200,
            'error' => false,
            'message' => 'Token actualizado',
            'token' => $token,
        ];
        return $this->respondCreated($response);
    }

    /**
     * Refresh user JWT.
     * 
     * Refresh user JWT with a given refresh token.
     *
     * @return  HttpResponse
     */
    public function refreshAccessToken()
    {
        $authHeader = $this->request->getServer('HTTP_AUTHORIZATION');
        helper('jwt');
        $token = refreshToken($authHeader);

        $response = [
            'status' => 200,
            'error' => false,
            'message' => 'Token actualizado',
            'token' => $token,
        ];
        return $this->respondCreated($response);
    }

    /**
     * Verify if username already exist on db.
     * 
     * @return HttpResponse
     */
    public function verifyUserName()
    {
        $rules = [
            'username' => 'required',
        ];

        $message = [
            'username' => [
                'required' => 'Nombre de usuario requerido',
            ],
        ];


        if (!$this->validate($rules, $message)) {

            // if validation fails

            $response = [
                'status' => 400,
                'error' => true,
                'message' => $this->validator->getErrors(),
                'data' => [],
            ];

            return $this->respondCreated($response);
        } else {

            $res = $this->verifyUserN($this->request->getVar('username'));
            $response = [];
            if (!$res) {
                $response = [
                    'status' => 400,
                    'error' => true,
                    'message' => "Nombre de usuario en uso",
                    'data' => [],
                ];
            } else {
                $response = [
                    'status' => 200,
                    'error' => false,
                    'message' => "Nombre de usuario disponible",
                    'data' => [],
                ];
            }

            return $this->respondCreated($response);
        }
    }


    /**
     * Verify if company have an asigned user
     *  
     * @return HttpResponse
     */
    public function verifyUserExist()
    {
        $rules = [
            'company_id' => 'required',
        ];

        $message = [
            'company_id' => [
                'required' => 'Id de el tercero requerido',
            ],
        ];
        //______________________________________________________________________________________________________________
        //
        //                                           Request valitation
        //_______________________________________________________________________________________________________________

        // $json = json_decode(file_get_contents("php://input", true));
        // $json = $this->request->getJSON();

        // echo $this->request->getVar('username');

        if (!$this->validate($rules, $message)) {

            // if validation fails

            $response = [
                'status' => 400,
                'error' => true,
                'message' => $this->validator->getErrors(),
                'data' => [],
            ];

            return $this->respondCreated($response);
        } else {

            $company_id = $this->verifyCompanyUserExist($this->request->getVar('company_id'), $this->dbGroupFromRequest(),);
            $response = [];
            if ($company_id != false) {
                $response = [
                    'status' => 200,
                    'error' => false,
                    'message' => "Usuario asignado encontrado",
                    'data' => [],
                ];
            } else {
                $response = [
                    'status' => 200,
                    'error' => true,
                    'message' => "Sin usuario asignado",
                    'data' => [],
                ];
            }

            return $this->respondCreated($response);
        }
    }

    /**
     * Verify if username already exist on db
     * 
     * @return boolean
     */
    public static function verifyUserN(String $username)
    {
        $user_model = new SmaUsersModel();
        $res = $user_model->getSelectedRows(["username" => $username]);
        // print_r($res);
        if (empty($res)) {
            return true;
        } else {
            return false;
        }
    }


    /**
     * Get user data for a given user_id.
     * 
     * @return array
     */
    public static function getUserData(String $userId, String $dbGroup)
    {
        $wappsiUsersModel = new WappsiUsersModel();

        $query = ['id' => $userId];
        $userdata = $wappsiUsersModel->where($query)->first();
        $user_model = new SmaUsersModel($dbGroup);
        $res = $user_model->find($userdata['user_data_id']);
        // print_r($res);
        return $res;
    }


    /**
     * Verify if company_id is already asigned to an user.
     *
     * 
     *
     * @return  int|boolean
     */
    public static function verifyCompanyUserExist(String $company_id, String $dbGroup)
    {
        $user_model = new SmaUsersModel($dbGroup);
        try {
            $user_info = $user_model->select('id')
                ->where(["company_id" => $company_id])->first();
            $user_id = $user_info['id'];
        } catch (\Throwable $th) {
            $user_id = false;
        }

        if (empty($user_id)) {

            return false;
        } else {
            return $user_id;
        }
    }
}
