<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;

use Symfony\Component\HttpFoundation\Response;

use Illuminate\Support\Facades\DB;
use Illuminate\Database\QueryException;
use PDOException;

class UserController extends Controller{
    public function authenticate(Request $request){
        $validator = Validator::make($request->all(), [
            'email' => 'required|string|email|max:255',
            'password' => 'required|string|min:6',
        ]);

        if($validator->fails()){
            return $this->makeResponse(
                true,
                "Se encontraron uno o más errores",
                $this->makeErrors($validator->errors()->messages()),
                400
            );
        }
        
        $credentials = $request->only('email', 'password');
        
        $pdo = DB::connection()->getPdo();
        $qry = 'SELECT id, name, rfc, role, isBlocked, email, id_unidad_negocio, id_politica_vacaciones FROM users WHERE email = :email';
        $gst = $pdo->prepare($qry);
        
        $gst->bindParam(':email', $credentials['email']);
        if ($gst->execute()) {
            //Get user
            $user = $gst->fetchObject();

            if(!$user){
                return $this->makeResponse(true, "email no registrado", [], 401);
            }

            //User isBlocked?
            if ($user->isBlocked) {
                //return response()->json(['error' => 'user_unauthorized'], 401);
                return $this->makeResponse(true, "Usuario bloqueado", [], 401);
            } else {
                $ip = $request->ip();
                $qry2 = 'UPDATE users SET last_ip = :ip, fLogin = CURRENT_TIMESTAMP WHERE email =:email';
                $gst2 = $pdo->prepare($qry2);
                $gst2->bindParam(':ip', $ip);
                $gst2->bindParam(':email', $credentials['email']);

                if (!$gst2->execute()) {
                    //return response()->json(['error' => 'error_update'], 500);
                    return $this->makeResponse(true, "No se pudo actualizar el registro del usuario", [], 500);
                }

                $qryLogs = 'INSERT INTO logs (id_user, ip, date) VALUES (:id_user, :ip, CURRENT_TIMESTAMP)';
                $gstLogs = $pdo->prepare($qryLogs);
                $gstLogs->bindParam(':id_user', $user->id);
                $gstLogs->bindParam(':ip', $ip);

                if (!$gstLogs->execute()) {
                    return $this->makeResponse(true, "No se pudo insertar el log del usuario", [], 500);
                }
            }
        } else {
            //return response()->json(['error' => 'invalid_credentials'], 400);
            return $this->makeResponse(true, "No se pudo recuperar la información del usuario", [], 500);
        }

        try {
            JWTAuth::factory()->setTTL(1440);
            if(!$token = JWTAuth::attempt($credentials)){
                return $this->makeResponse(true, "Credenciales inválidas", [], 401);
            }
        } catch (JWTException $e) {
            return $this->makeResponse(true, "No se pudo generar el token de autenticación", [], 500);
        }
        
        $user->token = $token;
        return $this->makeResponse(false, "Inicio de sesión exitoso", $user, 200);
    }

    public function getAuthenticatedUser(){
        echo "hola";
        try {
            if(!$user = JWTAuth::parseToken()->authenticate()){
                //return response()->json(['user_not_found'], 404);
                return $this->makeResponse(true, "Usuario no encontrado", [], 404);
            }
        } catch (Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {
            //return response()->json(['token_expired'], $e->getStatusCode());
            return $this->makeResponse(true, "Token expirado", [], 401);
        } catch (Tymon\JWTAuth\Exceptions\TokenInvalidException $e) {
            //return response()->json(['token_invalid'], $e->getStatusCode());
            return $this->makeResponse(true, "Token inválido", [], 401);
        } catch (Tymon\JWTAuth\Exceptions\JWTException $e) {
            //return response()->json(['token_absent'], $e->getStatusCode());
            return $this->makeResponse(true, "No se encontró el token de autenticación", [], 400);
        }

        //return response()->json(compact('user'));
        return $this->makeResponse(false, "Usuario obtenido", $user, 200);
    }

    public function register(Request $request){
        $validator = Validator::make($request->all(), [
            'name' => 'required|string|max:255',
            'numero_empleado' => 'required|string',
            'rfc' => 'required|string|max:13',
            'fecha_ingreso' => 'required|date',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => 'required|string|min:6|confirmed',
            'role' => 'required|string',
            'numero_empleado_registra' => 'required|string'
        ]);

        if ($validator->fails()) {
            //return response()->json($erroresArr, 401);
            return $this->makeResponse(
                true,
                "Se encontraron uno o más errores",
                $this->makeErrors($validator->errors()->messages()),
                401
            );
        }

        $pdo = DB::connection()->getPdo();
        $qryUsrIns = "SELECT * FROM users WHERE numero_empleado = :num";
        $gstUsrIns = $pdo->prepare($qryUsrIns);

        $usrIns = $request->all()['numero_empleado_registra'];
        $gstUsrIns->bindParam(":num", $usrIns);

        if($gstUsrIns->execute()){
            if(count($gstUsrIns->fetchAll()) < 1){
                return $this->makeResponse(true, "El usuario que registra no existe", [], 404);
            }
        }else{
            return $this->makeResponse(true, "No se pudo obtener la información del usuario que registra", [], 500);
        }

        try{
            $user = User::create([
                'name' => $request->get('name'),
                'numero_empleado' => $request->get('numero_empleado'),
                'rfc' => $request->get('rfc'),
                'fecha_ingreso' => $request->get('fecha_ingreso'),
                'email' => $request->get('email'),
                'password' => Hash::make($request->get('password')),
                'role' => $request->get('role'),
                'isBlocked' => false,
                'estatus' => 'A',
            ]);
        }catch(QueryException $e){
            $msg = trim(explode("(", $e->getMessage())[0]);
            return $this->makeResponse(true, $msg, [], 500);
        }

        //return response()->json(compact('user', 'token'), 201);
        return $this->makeResponse(false, "Usuario creado correctamente");
    }

    public function update(Request $request){
        $validator = Validator::make($request->all(), [
            'name' => 'required|string|max:255',
            'numero_empleado' => 'required|string',
            'rfc' => 'required|string|max:13',
            'fecha_ingreso' => 'required|date',
            'email' => 'required|string|email|max:255',
            'role' => 'required|string',
        ]);

        if ($validator->fails()) {
            //return response()->json($validator->errors(), 401);
            return $this->makeResponse(
                true,
                "Se encontraron uno o más errores",
                $this->makeErrors($validator->errors()->messages()),
                401
            );
        }

        $user = $request->all();
        $pdo = DB::connection()->getPdo();
        $qryVer = "SELECT * FROM users WHERE numero_empleado = :num";
        $gstVer = $pdo->prepare($qryVer);

        $gstVer->bindParam(":num", $user["numero_empleado"]);

        if($gstVer->execute()){
            if(count($gstVer->fetchAll($pdo::FETCH_ASSOC)) < 1){
                return $this->makeResponse(true, "El usuario que desea modificar no existe", [], 404);
            }
        }else{
            return $this->makeResponse(true, "No se pudo consultar el usuario que desea modificar", [], 500);
        }

        
        $qry = 'UPDATE users SET name = :name, rfc = :rfc, fecha_ingreso = :fecha_ingreso, 
                email = :email, role = :role  WHERE numero_empleado = :num';
        $gst = $pdo->prepare($qry);

        $gst->bindParam(":name", $user['name']);
        $gst->bindParam(":rfc", $user['rfc']);
        $gst->bindParam(":fecha_ingreso", $user['fecha_ingreso']);
        $gst->bindParam(":email", $user['email']);
        $gst->bindParam(":role", $user['role']);
        $gst->bindParam(":num", $user['numero_empleado']);

        try{
            if (!$gst->execute()) {
                //return response()->json(['error' => 'error_update_user'], 500);
                return $this->makeResponse(true, "No se pudo modificar el usuario", [], 500);
            }
        }catch(PDOException $e){
            return $this->makeResponse(true, $e->getMessage(), [], 500);
        }

        //return response()->json(['exito' => 'Usuario modificado'], 200);
        return $this->makeResponse(false, "Usuario modificado");
    }

    public function delete(Request $request){
        $validator = Validator::make($request->all(), [
            'numero_empleado' => 'required|string',
        ]);

        if($validator->fails()){
            return $this->makeResponse(
                true,
                "Se encontraron uno o más errores",
                $this->makeErrors($validator->errors()->messages()),
                401
            );
        }

        $pdo = DB::connection()->getPdo();
        $qry = 'UPDATE users SET estatus = "E" WHERE numero_empleado = :num';
        $gst = $pdo->prepare($qry);

        $id = $request->all()['numero_empleado'];
        $gst->bindParam(":num", $id);
        
        if (!$gst->execute()) {
            //return response()->json(['error' => 'error_delete_users'], 500);
            return $this->makeResponse(true, "No se pudo eliminar al usuario solicitado", [], 500);
        }
        //return response()->json(['error' => 'Usuario eliminado'], 200);
        return $this->makeResponse(false, "Usuario eliminado");
    }

    public function getUsers($id){
        $pdo = DB::connection()->getPdo();
        $qryVer = "SELECT * FROM users WHERE numero_empleado = :num";
        $gstVer = $pdo->prepare($qryVer);

        $gstVer->bindParam(":num", $id);

        if(!$gstVer->execute()){
            return $this->makeResponse(true, "No se pudieron obtener los datos del usuario que realiza la consulta", [], 500);
        }

        if(count($gstVer->fetchAll($pdo::FETCH_ASSOC)) < 1){
            return $this->makeResponse(true, "El usuario que realiza la consulta no existe", [], 404);
        }

        $qry = 'SELECT id, name, numero_empleado, rfc, role, fecha_ingreso, id_unidad_negocio, id_politica_vacaciones, 
            isBlocked, last_ip, email, estatus FROM users WHERE numero_empleado != :num';
        $gst = $pdo->prepare($qry);

        $gst->bindParam(":num", $id);
        
        if (!$gst->execute()) {
            //return response()->json(['error' => 'error_get_users'], 500);
            return $this->makeResponse(true, "No se pudieron obtener los usuarios de la base de datos", [], 500);
        }

        //return response()->json(['users' => $gst->fetchAll($pdo::FETCH_ASSOC)], 200);
        return $this->makeResponse(false, "Consulta exitosa", $gst->fetchAll($pdo::FETCH_ASSOC));
    }

    public function getUsersMenos($idreq, $idmen){
        $pdo = DB::connection()->getPdo();
        $qryVer = "SELECT * FROM users WHERE numero_empleado = :num";
        $gstVer = $pdo->prepare($qryVer);

        $gstVer->bindParam(":num", $idreq);

        if(!$gstVer->execute()){
            return $this->makeResponse(true, "No se pudieron obtener los datos del usuario que realiza la consulta", [], 500);
        }

        if(count($gstVer->fetchAll($pdo::FETCH_ASSOC)) < 1){
            return $this->makeResponse(true, "El usuario que realiza la consulta no existe", [], 404);
        }

        unset($gstVer);
        $gstVer = $pdo->prepare($qryVer);

        $gstVer->bindParam(":num", $idmen);

        if(!$gstVer->execute()){
            return $this->makeResponse(true, "No se pudieron obtener los datos del usuario a descartar", [], 500);
        }

        if(count($gstVer->fetchAll($pdo::FETCH_ASSOC)) < 1){
            return $this->makeResponse(true, "El usuario a descartar no existe", [], 404);
        }

        $qry = 'SELECT id, name, numero_empleado, rfc, role, fecha_ingreso, id_unidad_negocio, id_politica_vacaciones, 
            isBlocked, last_ip, email, estatus FROM users WHERE numero_empleado != :numreq AND numero_empleado != :nummen';
        $gst = $pdo->prepare($qry);

        $gst->bindParam(":numreq", $idreq);
        $gst->bindParam(":nummen", $idmen);
        
        if (!$gst->execute()) {
            //return response()->json(['error' => 'error_get_users'], 500);
            return $this->makeResponse(true, "No se pudieron obtener los usuarios de la base de datos", [], 500);
        }

        //return response()->json(['users' => $gst->fetchAll($pdo::FETCH_ASSOC)], 200);
        return $this->makeResponse(false, "Consulta exitosa", $gst->fetchAll($pdo::FETCH_ASSOC));
    }

    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;
    }
}
