<?php

// use App\Models\SmaSmaUsersModel

use App\Models\SmaSettingsModel;
use App\Models\SmaUsersModel;
use App\Models\WappsiUsersModel;
use Config\Services;

// if (!defined('BASEPATH'))
//         exit('No direct script access allowed');
use Firebase\JWT\JWT;

// use function PHPUnit\Framework\throwException;

function getJWTFromRequest($authenticationHeader): string
{
    //echo 'here';
    //print_r($authenticationHeader);
    if (is_null($authenticationHeader)) {
        //JWT is absent
        header('Content-Type: application/json; charset=utf-8');
        $res = [
            "error" => true,
            "message" => 'Missing or invalid JWT in request',
            "data" => [],
        ];
        echo json_encode($res);
        die();
    }
    //JWT is sent from client in the format Bearer XXXXXXXXX
    // echo $authenticationHeader;
    return $authenticationHeader;
}

/**
 * Returns company_folder stored in JWT
 *
 * @param string $encodedToken
 * @return string company_folder
 */
function companyURLFromJWT(string $encodedToken)
{

    $key = Services::getJWTKey();

    $decodedToken = JWT::decode($encodedToken, $key, ['HS256']);

    $url = Services::buildURL($decodedToken->host_url,$decodedToken->company_folder);
    
    return $url;

}

/**
 * Returns db_group stored in JWT
 *
 * @param string $encodedToken
 * @return string company_folder
 */
function dbGroupFromJWT(string $encodedToken)
{

    $key = Services::getJWTKey();

    $decodedToken = JWT::decode($encodedToken, $key, ['HS256']);
    
    return $decodedToken->db_group;

}




/**
 * Returns user_id stored in JWT
 *
 * @param string $encodedToken
 *
 */
function userIdFromJWT(string $encodedToken)
{

    $key = Services::getJWTKey();

    $decodedToken = JWT::decode($encodedToken, $key, ['HS256']);

    return $decodedToken->user_id;

}
/**
 * Returns view_right stored in JWT
 *
 * @param string $encodedToken
 * @return int
 *
 */
function viewRightFromJWT(string $encodedToken)
{

    $key = Services::getJWTKey();

    $decodedToken = JWT::decode($encodedToken, $key, ['HS256']);

    return $decodedToken->view_right;

}
/**
 * Returns company URL saved in JWT
 *
 * @param string $encodedToken
 * @return string
 *
 */
function companyFolderFromJWT(string $encodedToken)
{

    $key = Services::getJWTKey();

    $decodedToken = JWT::decode($encodedToken, $key, ['HS256']);

    return $decodedToken->company_folder;

}

/**
 * Returns data stored in JWT
 *
 * @param string $encodedToken
 *
 */
function dataFromJWT(string $encodedToken)
{

    $key = Services::getJWTKey();

    $decodedToken = JWT::decode($encodedToken, $key, ['HS256']);

    return $decodedToken;

}
/**
 * Refresh current token, and return new token
 *
 *
 *
 */
function refreshToken(string $authenticationHeader)
{
    // get token from request
    $oldToken = getJWTFromRequest($authenticationHeader);

    // Decode JWT to get it's data
    $oldTokenData = dataFromJWT($oldToken);
    // get expTime defined by settings, time in setting is defined in minutes, 
    // here we need time in seconds
    // $expTime = 5 * 60;
    $expTime = 60 * 60;
    
    //current server time
    $iat = time();

    // umbral to refresh token, to 1h->16.66%=~10min
    $umbral = $iat + (int) round($expTime * 0.1666);

    // get old token expiration time time
    $exp = $oldTokenData->exp;

    
    if ($umbral >= $exp) {
        $user_key = getJWTUserKey();

        $userModel = new WappsiUsersModel();

        $user_id = userIdFromJWT($oldToken);
        // change login_status and jwt_key
        $userModel->update($user_id, ['jwt_key' => $user_key]);

        $userdata = $userModel->where('id',$user_id)->first();

        $usersDataModel = new SmaUsersModel($userdata['db_group']);

        $usersDetailedData = $usersDataModel->where('id = ' . $userdata['user_data_id'])->first();

        $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'],
        ];
        
        $payLoad = genPayLoad(
            $payloadData,
            $user_key
        );

        return getSignedJWTForUser($payLoad);
    } else {
        
        return $oldToken;
    }
}

function validateJWTFromRequest(string $encodedToken)
{

    $key = Services::getJWTKey();

    $reload = false;

    // echo $key.'xd';
    try {

        $decodedToken = JWT::decode($encodedToken, $key, ['HS256']);

        // print_r($decodedToken);
        // $refresh_token=$decoded->data->refresh_token;

        $wappsiUsersModel = new WappsiUsersModel();

        $query = ['id' => $decodedToken->user_id, 'jwt_key' => $decodedToken->user_key];
        $userdata = $wappsiUsersModel->where($query)->first();
        // print_r($userdata);
        // $errorMessage = 'Token invalido, al parecer se ha iniciado sesión en otro dispositivo';

        if (empty($userdata)) {
            $reload = true;
            header('Content-Type: application/json; charset=utf-8');
            echo json_encode(array(
                "message" => "Token expirado, se ha iniciado sesión en otro dispositivo",
                "error" => true,
                // "error_message"   => $errorMessage,
                'reload' => $reload,
                'data' => []
            ));
            die();
        } else {
            if ($userdata['login_status'] == 0) {
                $reload = true;
                // $errorMessage = 'Token invalido, sesión cerrada por usuario.';
                // throw new Exception($errorMessage);
                header('Content-Type: application/json; charset=utf-8');
                echo json_encode(array(
                    "message" => "Token invalido, sesión cerrada por usuario",
                    "error" => true,
                    // "error_message"   => $errorMessage,
                    'reload' => $reload,
                    'data' => []
                ));
                die();
            }
        }

    } catch (Exception $e) {

        if ($e->getMessage() == "Expired token") {
            // para cerrar sesión

            $reload = true;

            try {
                list($header, $payload, $signature) = explode(".", $encodedToken);

                $payload = json_decode(base64_decode($payload));
                $wappsiUsersModel = new WappsiUsersModel();

                $query = ['id' => $decodedToken->user_id, 'jwt_key' => $decodedToken->user_key];
                $userdata = $wappsiUsersModel->where($query)->first();
                $error2 = "";
                if(!empty($payload->id??"")){
                    $wappsiUsersModel->update($payload->id, ['login_status' => 0]);
                }

            } catch (\Throwable$th) {
                //throw $th;
                $error2 = $th;
            }
            header('Content-Type: application/json; charset=utf-8');
            echo json_encode(array(
                "message" => "Token de sesión expirado",
                "error_message" => "'$error2'",
                "error" => true,
                'reload' => $reload,
                'data' => []
            ));

            die();

        } else {

            // set response code
            http_response_code(401);
            header('Content-Type: application/json; charset=utf-8');
            // show error message
            echo json_encode(array(
                'message' => 'Acceso denegado, ' . $e->getMessage(),
                "error" => true,
                'reload' => $reload,
                'data' => []
            ));
            die();
        }
    }

}

//______________________________________________________________________________________________________________
//
//                                       GENERATE PAYLOAD
//_______________________________________________________________________________________________________________

function genPayLoad($payloadData, $user_key)
{
    $expTime = 60 * 60;
    // $expTime = 5*60;
    // print_r($expTime);
    // echo $duration;

    $iat = time(); // current timestamp value
    // $nbf = $iat;
    $exp = $iat + $expTime;
    // print_r([$iat,$exp]);
    $payload = array(
        // "iss" => "The_claim",
        // "aud" => "The_Aud",
        "iat" => $iat, // issued at
        // "nbf" => $nbf, //not before in seconds
        "exp" => $exp, // expire time in seconds
        "user_id" => $payloadData['id'],
        "view_right" => $payloadData['view_right'],
        'company_folder' => $payloadData['company_folder'],
        'host_url' => $payloadData['host_url'],
        'id' => $payloadData['id'],
        'db_group' => $payloadData['db_group'],
        "user_key" => $user_key,
    );
    return $payload;

}

function getSignedJWTForUser($payload)
{
    // $issuedAtTime = time();
    $key = Services::getJWTKey();
    // echo $key;
    $jwt = JWT::encode($payload, $key);
    return $jwt;
}
//______________________________________________________________________________________________________________
//
//                                Generates a ramdom string of length 10
//                       Will be stored in db.sma_users.jwt_key and in payload in a JWT
//_______________________________________________________________________________________________________________
function getJWTUserKey($length = 10)
{
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;
}
