<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Carbon;

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

use Exception;
use PDOException;

use Jenssegers\Agent\Agent;

class LoginController extends Controller{
    private $responseController;
    private $encryptionController;
    private $functionsController;
    private $secretKey = "ydl27x22cNsNY0z6o3Fr6XZoUvsX0QMZx6MaiwN+KCnM6APS4Xbb7GDfudOYD5uD/r8TzQElh4d4HIal5Os0XA==";
    private $publicKey = "zOgD0uF22+xg37nTmA+bg/6/E80BJYeHeByGpeTrNFw=";

    public function __construct(){
        $this->responseController = new ResponseController();
        $this->encryptionController = new EncryptionController();
        $this->functionsController = new FunctionsController;
    }

    public function login(Request $request){
        DB::enableQueryLog();
        $validator = Validator::make($request->all(), [
            'email' => 'required|string|email',
            'password' => 'required|string',
            'linea' => 'required|integer|max: 2',
            'latitude' => 'required|numeric|between:-90,90',
            'longitude' => 'required|numeric|between:-180,180',
            'accuracy' => 'required|numeric|between:0,1000000',
            'city' => 'string|max:100',
            'state' => 'required|string|max:100',
            'country' => 'required|string|max:50',
        ]);

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

        $login = $request->all();
        //Se obtiene la dirección IP de la solicitud
        $ipv = $request->ip();
        $v4 = filter_var($ipv, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? true : false;

        //Se obtiene el dispositivo utilizado
        $agent = new Agent();
        $device = 'Desconocido';
        if($agent->isDesktop()) $device = 'PC';
        else if($agent->device()) $device = $agent->device();

        //Se obtiene el SO utilizado y la versión del mismo
        $platform = $agent->platform() ? $agent->platform() : 'Desconocido';
        $version = 'N/A';
        if($platform != 'Desconocido') $version = $agent->version($platform);

        //Se obtiene el navegador web utilizado
        $browser = $agent->browser() ? $agent->browser() : 'Desconocido';

        //Se consulta el correo electrónico enviado
        $usr = DB::table('S002V01TUSUA')->where('USUA_COEL', '=', $login['email'])->first();

        if(is_null($usr)){
            return $this->responseController->makeResponse(true, "El correo electrónico no está registrado.", [], 404);
        }else if($usr->USUA_ESTA != 'Activo'){
            $statusStr = strtolower($usr->USUA_ESTA);
            return $this->responseController->makeResponse(true, "El usuario se encuentra $statusStr, por favor contacte al administrador para solucionarlo.", [], 401);
        }
        
        $now = Carbon::now('America/Mexico_city');
        $nowStr = $now->toDateTimeString();

        $contra = $login['password'];
        $contra = $this->encryptionController->decrypt($contra);
        if(!$contra){
            return $this->responseController->makeResponse(true, 'La contraseña no fue encriptada correctamente.', [], 400);
        }

        $usrContra = $usr->USUA_CONT;
        if(!Hash::check($contra, $usrContra)){
            $attempts = $usr->USUA_ININ + 1;
            $status = $attempts >= 10 ? 'Inactivo' : 'Activo';

            DB::table('S002V01TUSUA')->where('USUA_IDUS', '=', $usr->USUA_IDUS)->update([
                "USUA_ININ" => $attempts,
                "USUA_ESTA" => $status
            ]);

            return $this->responseController->makeResponse(true, "La contraseña es incorrecta, intento $attempts de 10.", [], 401);
        }

        DB::table('S002V01TUSUA')->where('USUA_IDUS', '=', $usr->USUA_IDUS)->update([
            "USUA_ININ" => 0,
            "USUA_ESTA" => 'Activo'
        ]);

        $iat = $now->timestamp;
        $cad = $now->addDay()->timestamp;

        $ulco = DB::table('S002V01TBIAC')->insertGetId([
            'BIAC_NULI' => $login['linea'],
            'BIAC_IDUS' => $usr->USUA_IDUS,
            'BIAC_FECO' => $nowStr,
            'BIAC_IPV4' => $v4 ? $ipv : null,
            'BIAC_IPV6' => !$v4 ? $ipv : null,
            'BIAC_LATI' => $login['latitude'],
            'BIAC_LONG' => $login['longitude'],
            'BIAC_PREC' => $login['accuracy'],
            'BIAC_CIUD' => $login['city'],
            'BIAC_ESTA' => $login['state'],
            'BIAC_PAIS' => $login['country'],
            'BIAC_DISP' => $device,
            'BIAC_SIOP' => $platform,
            'BIAC_VSOP' => $version,
            'BIAC_NAVE' => $browser,
        ]);
        
        DB::table('S002V01TUSUA')->where('USUA_COEL', '=', $login['email'])->update(['USUA_ULCO' => $ulco]);

        //Antes de crear el token revisamos los permisos de su perfil
        $profile = DB::table('S002V01TPERF')->where('PERF_IDPE', '=', $usr->USUA_PERF)->get()->first();
        $permissions = $this->encryptionController->encrypt($profile->PERF_PERM);

        $payload = [
            "iss" => $login['email'],
            "aud" => "dominio.syp.mx",
            "iat" => $iat,
            "cad" => $cad
        ];

        $token = JWT::encode($payload, $this->secretKey, 'EdDSA');

        //Antes de realizar el return obtenemos todos los querys ejecutados en esta consulta
        $querys = DB::getQueryLog();
        $name = $this->functionsController->joinName($usr->USUA_NOMB, $usr->USUA_APPA, $usr->USUA_APMA);

        //Se registra la acción realizada
        $idac = $this->functionsController->registerActivity(
            $login['linea'],
            'LOGIN',
            '-',
            '-',
            'Registro',
            "El usuario $name (" . $usr->USUA_IDUS . ") inició sesión.",
            $usr->USUA_IDUS,
            $nowStr
        );

        $this->functionsController->registerLog($querys, $usr->USUA_IDUS, $nowStr, $idac, $login['linea']);

        return $this->responseController->makeResponse(false, "EXITO.", [
            "IDUSUARIO" => $this->encryptionController->encrypt($usr->USUA_IDUS),
            "NOMBREUSUARIO" => $this->encryptionController->encrypt($usr->USUA_NOMB),
            "CORREO" => $this->encryptionController->encrypt($usr->USUA_COEL),
            "PERFIL" => $this->encryptionController->encrypt($usr->USUA_PERF),
            "PERMISOS" => $permissions,
            "TOKEN" => $token,
        ]);
    }

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

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

        $tokenInfo = $request->all();
        try{
            $decoded = JWT::decode($tokenInfo['token'], new Key($this->publicKey, 'EdDSA'));
        }catch(Exception $e){
            return $this->responseController->makeResponse(false, "Token inválido", [
                "validToken" => false
            ]);
        }

        $usr = DB::table('S002V01TUSUA')->where('USUA_COEL', '=', $decoded->iss)->first();
        if(is_null($usr)){
            return $this->responseController->makeResponse(false, "El usuario que generó el token no está registrado en la base.", [
                "validToken" => false
            ]);
        }

        if($decoded->aud != "dominio.syp.mx"){
            return $this->responseController->makeResponse(false, "El token enviado fue generado en un sitio diferente.", [
                "validToken" => false
            ]);
        }

        $now = Carbon::now('America/Mexico_city')->timestamp;
        if($now > $decoded->cad){
            return $this->responseController->makeResponse(false, "Token expirado.", [
                "validToken" => false
            ]);
        }

        $nowStr = Carbon::now('America/Mexico_city')->toDateTimeString();
        $name = $this->functionsController->joinName($usr->USUA_NOMB, $usr->USUA_APPA, $usr->USUA_APMA);

        //Se registra la acción realizada
        $idac = $this->functionsController->registerActivity(
            $tokenInfo['linea'],
            'LOGIN',
            '-',
            '-',
            'Registro',
            "El usuario $name (" . $usr->USUA_IDUS . ") inició sesión.",
            $usr->USUA_IDUS,
            $nowStr
        );

        $actions = DB::getQueryLog();
        $this->functionsController->registerLog($actions, $usr->USUA_IDUS, $nowStr, $idac, $tokenInfo['linea']);
        return $this->responseController->makeResponse(false, "Token válido.", [
            "validToken" => true
        ]);
    }

    public function createPasword(Request $request){
        $timestamp = Carbon::now('America/Mexico_city')->timestamp;
        $id = $this->functionsController->generateID('José Luis Brito Nava', $timestamp);
        var_dump(Hash::make('ITTEC2022'));
        
        $uuid = $this->functionsController->uuidv5('1546058f-5a25-4334-85ae-e68f2a44bbaf', 'jose.b@ittec.mx');
        return $this->responseController->makeResponse(false, $id, []);
    }

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

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

        $info = $request->all();
        $strDec = $this->encryptionController->decrypt($info['string']);

        if(!$strDec){
            return $this->responseController->makeResponse(true, 'La cadena enviada no fue encriptada correctamente', [], 404);
        }

        $shorterStr = $this->encryptionController->shortEnc($strDec);
        $shorterStr = str_replace("+", "=P=", $shorterStr);
        $shorterStr = str_replace("/", "=S=", $shorterStr);

        return $this->responseController->makeResponse(false, 'EXITO', ['encrypted' => $shorterStr]);
    }
}
