<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use JWTAuth;
use JWTFactory;
use App\Http\Controllers\EncController;
use Tymon\JWTAuth\Exceptions\JWTException;
use Illuminate\Support\Carbon;

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

class IAMController extends Controller{
    public function login(Request $request){
        //Se recupera y valida la información en el formulario de la petición
        $validator = Validator::make($request->all(), [
            'email' => 'required|string|email|max:255',
            'password' => 'required|string|min:8',
            'ip_address' => 'required|ipv4',
        ]);

        if($validator->fails()){
            return $this->makeResponse(
                true,
                "ERR_IAM_USU_LOG000: No se enviaron los campos requeridos.",
                $this->makeErrors($validator->errors()->messages()),
                400
            );
        }
        //var_dump(Hash::make("ITTEC2022"));
        //Se procede a buscar la información del usuario
        $credentials = $request->only('email', 'password', 'ip_address');
        $pdo = DB::connection()->getPdo();
        $qry = "SELECT USUA_IDUS, USUA_RFCE, PERF_TIPE, USUA_ININ, USUA_ESTA, USUA_NOMB, USUA_APPA, USUA_APMA, USUA_COEL, USUA_CONT, USUA_RFCU, USUA_POLI, EMPR_RASO  
        FROM S001V01TUSUA INNER JOIN S001V01TPERF ON PERF_IDPE = USUA_PERF INNER JOIN S001V01TEMPR ON USUA_RFCE = EMPR_RFCE WHERE USUA_COEL = :coel AND 
        USUA_ESTA != 'Eliminado'";
        $gst = $pdo->prepare($qry);
        $gst->bindParam(":coel", $credentials['email']);
        //Se valida que se pueda ejecutar la consulta
        if(!$gst->execute()){
            return $this->makeResponse(true, "ERR_IAM_SER_LOG001: No se pudo consultar la información del usuario.", [], 500);
        }
        //Después de ejecutar la consulta se valida que el usuario exista
        $usuario = $gst->fetchObject();
        if(!$usuario){
            return $this->makeResponse(true, "ERR_IAM_USU_LOG002: El correo electrónico no está registrado.", [], 404);
        }

        if($usuario->USUA_ESTA == "Inactivo"){
            return $this->makeResponse(true, "ERR_IAM_USU_LOG003: El usuario se encuentra bloqueado.", [], 404);
        }
        //Si el usuario existe se valida su contraseña
        if(!Hash::check($credentials['password'], $usuario->USUA_CONT)){
            $intentosIncorrectos = $usuario->USUA_ININ;
            $intentosIncorrectos++;
            $qryININ = "UPDATE S001V01TUSUA SET USUA_ININ = :inin WHERE USUA_IDUS = :idus";
            $gstININ = $pdo->prepare($qryININ);

            $gstININ->bindParam(":inin", $intentosIncorrectos);
            $gstININ->bindparam(":idus", $usuario->USUA_IDUS);

            if(!$gstININ->execute()){
                return $this->makeResponse(true, "ERR_IAM_SER_LOG004: No se pudo actualizar el número de intentos incorrectos.", [], 500);
            }

            if($intentosIncorrectos == 10){
                $qryBlock = "UPDATE S001V01TUSUA SET USUA_ESTA = 'Inactivo' WHERE USUA_IDUS = :idus";
                $gstBlock = $pdo->prepare($qryBlock);
                $gstBlock->bindParam(":idus", $usuario->USUA_IDUS);
                if(!$gstBlock->execute()){
                    return $this->makeResponse(true, "ERR_IAM_SER_LOG005: No se pudo actualizar el estatus del usuario.", [], 500);
                }
                return $this->makeResponse(true, "ERR_IAM_USU_LOG006: Usuario bloqueado.", [], 401);
            }

            return $this->makeResponse(true, "ERR_IAM_USU_LOG007: La contraseña es incorrecta, intento $intentosIncorrectos de 10.", [], 401);
        }
        $qryININRest = "UPDATE S001V01TUSUA SET USUA_ININ = 0 WHERE USUA_IDUS = :idus";
        $gstININRest = $pdo->prepare($qryININRest);

        $gstININRest->bindparam(":idus", $usuario->USUA_IDUS);

        if(!$gstININRest->execute()){
            return $this->makeResponse(true, "ERR_IAM_SER_LOG008: No se pudo restaurar el número de intentos.", [], 500);
        }
        //Si el usuario ingresó sus credenciales correctamente procedemos a registrar su login
        $qryLog  = "INSERT INTO S001V01TBIAC (BIAC_RFCE, BIAC_IDUS, BIAC_IPUS, BIAC_FEHO, BIAC_FEAR) ";
        $qryLog .= "VALUES (:rfce, :idus, :ipus, :feho, CURRENT_TIMESTAMP)";
        $gstLog = $pdo->prepare($qryLog);
        //Se obtiene la fecha actual
        $now = Carbon::now()->timezone('America/Mexico_City')->toDateTimeString();
        //Se realiza el bind de los valores en el query
        $gstLog->bindParam(":rfce", $usuario->USUA_RFCE);
        $gstLog->bindParam(":idus", $usuario->USUA_IDUS);
        $gstLog->bindParam(":ipus", $credentials['ip_address']);
        $gstLog->bindParam(":feho", $now);
        //Se verifica que se haya podido ejecutar la consulta
        if(!$gstLog->execute()){
            return $this->makeResponse(true, "ERR_IAM_SER_LOG009: No se pudo ingresar la información del log", [], 500);
        }
        //Si se ejecutó la consulta entonces generamos el token del usuario
        $currentDate = Carbon::now();
        $nowUnix = $currentDate->timestamp;
        $tomorrowUnix = $currentDate->addDay()->timestamp;
        $customClaims = ['idus' => $usuario->USUA_IDUS, 'coel' => $usuario->USUA_COEL, 'cont' => $usuario->USUA_CONT];
        JWTAuth::factory()->setTTL(1440);
        $payload = JWTFactory::make($customClaims);
        $token = JWTAuth::encode($payload);
        $tokenArr = (array) $token;
        $tokenStr = "";

        $secretKey = "pUaQ2tLc2qitTB7i0nNPgr9nimVEHZyQpt/8DT75c7M/5QOZAMmC53D1JNmlQLIjte0uZaOFcvgLLgxEnwMWwA==";
        $publicKey = "P+UDmQDJgudw9STZpUCyI7XtLmWjhXL4Cy4MRJ8DFsA=";
        $aud = str_replace("v1/public/api/login", "", url()->current());
        $payload = array(
            "iss" => $usuario->USUA_IDUS,
            "aud" => $aud,
            "iat" => $nowUnix,
            //"nbf" => $tomorrowUnix
            "cad" => $tomorrowUnix
        );
        $jwt = JWT::encode($payload, $secretKey, 'EdDSA');

        foreach($tokenArr as $val){ $tokenStr = $val; }
        //Encriptamos los datos sensibles con sodium
        $encriptacion = new EncController();
        //Se crea el objeto de retorno
        $user = (object) [
            "IDUSUARIO" => $encriptacion->encriptar($usuario->USUA_IDUS),
            "RFCEMPRESA" => $encriptacion->encriptar($usuario->USUA_RFCE),
            "RFCUSUARIO" => $encriptacion->encriptar($usuario->USUA_RFCU),
            "CORREO" => $encriptacion->encriptar($usuario->USUA_COEL),
            "NOMBRE" => $this->nombreCompleto($usuario->USUA_NOMB, $usuario->USUA_APPA, $usuario->USUA_APMA),
            "PERFIL" => $encriptacion->encriptar($usuario->PERF_TIPE),
            "POLITICA" => $encriptacion->encriptar($usuario->USUA_POLI),
            "ESTATUS" => $encriptacion->encriptar($usuario->USUA_ESTA),
            "RAZONSOCIAL" => $encriptacion->encriptar($usuario->EMPR_RASO),
            //"TOKEN" => $tokenStr
            "TOKEN" => $jwt
        ];
        //Se regresa la información al usuario
        return $this->makeResponse(false, "EXITO", $user);
    }

    public function contra(Request $request){
        $contra = "TME700618RC7_timken";
        var_dump(Hash::make($contra));
    }

    private function makeResponse($error, $msg, $response = [], $code = 200){
        $respuesta = json_encode([
            "error" => $error,
            "msg" => $msg,
            "response" => $response
        ]);

        return response($respuesta, $code)->header('Content-Type', 'application/json');
    }

    private function makeErrors($erroresObj){
        $erroresArr = array();

        foreach ($erroresObj as $key => $value) {
            foreach ($value as $key0 => $value0) {
                if(array_key_exists($key, $erroresArr)){
                    $val = $erroresArr[$key] . "|" . $value0;
                    $erroresArr[$key] = $val;
                }else{
                    $erroresArr[$key] = $value0;
                }
            }
        }

        return $erroresArr;
    }

    private function nombreCompleto($nom, $apP, $apM){
        $apP = $apP == "-" ? "" : $apP;
        $apM = is_null($apM) ? "" : $apM;
        return trim($nom . " " . $apP . " " . $apM);
    }
}