<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Carbon;

use App\Http\Controllers\CorreoController;
use App\Http\Controllers\FunctionsController;

use PDOException;

class MESMSVController extends Controller
{
    public function createOld(Request $request){
        $validator = Validator::make($request->all(), [
            'rfc_empresa' => 'required|string',
            'numero_empleado' => 'required|string|max:50',
            'fecha_inicio' => 'required|date',
            'fecha_final' => 'required|date',
            'numero_dias' => 'required|integer|max:31'
        ]);

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

        $solicitud = $request->all();

        //Se valida que las fechas pedidas sean mayores a la actual
        $fIni = new Carbon($solicitud['fecha_inicio']);
        $fFin = new Carbon($solicitud['fecha_final']);
        $hoy = Carbon::now()->timezone('America/Mexico_City');
        $pdo = DB::connection()->getPdo();

        $qryDiasDisp = "SELECT PEVA_VAGO FROM S001V01TPEVA INNER JOIN S001V01TSOVA ON SOVA_IDSO = PEVA_IDSO WHERE SOVA_IDUS = :idus ORDER BY PEVA_IDPE DESC LIMIT 1";
        $gstDiasDisp = $pdo->prepare($qryDiasDisp);
        $gstDiasDisp->bindParam(":idus", $solicitud['numero_empleado']);

        if(!$gstDiasDisp->execute()){
            return $this->makeResponse(true, "ERR_MCOMSV_SER_CRE001: No se pudo consultar los días disponibles de vacaciones del usuario", [], 500);
        }

        $diasDisp = $gstDiasDisp->fetchObject();

        if($diasDisp && $diasDisp->PEVA_VAGO < intval($solicitud['numero_dias'])){
            return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE002: Los días solicitados exceden los disponibles", [], 401);
        } 

        $qrySolicitudPorTomar = "SELECT SOVA_IDSO FROM S001V01TSOVA WHERE SOVA_IDUS = :idus AND (SOVA_ESTA = 'Pendiente' OR SOVA_ESTA = 'Aprobado') AND 
        SOVA_FEFI > CURRENT_DATE ORDER BY SOVA_IDSO DESC LIMIT 1";
        $gstSolicitudPorTomar = $pdo->prepare($qrySolicitudPorTomar);
        $gstSolicitudPorTomar->bindParam(":idus", $solicitud['numero_empleado']);

        if(!$gstSolicitudPorTomar->execute()){
            return $this->makeResponse(true, "ERR_MCOMSV_SER_CRE003: No se pudo consultar las vacaciones anteriores del usuario", [], 500);
        }

        $solicitudPorTomar = $gstSolicitudPorTomar->fetchObject();

        if($solicitudPorTomar){
            return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE004: No se puede crear un nuevo periodo vacacional porque aún hay uno no vencido", [], 401);
        }

        if($hoy->gt($fIni)) return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE005: La fecha inicial es menor a la actual", [], 400);
        if($hoy->gt($fFin)) return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE006: La fecha final es menor a la actual", [], 400);
        //Se valida que la fecha final se mayor a la inicial
        if($fIni->gt($fFin)) return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE007: La fecha inicial es mayor a la final", [], 400);
        //Se calcula la diferencia en días
        $diff = $fFin->diffInDays($fIni);
        //Se buscan los días feriados
        $year = $fIni->year;
        $qryDiasF = "SELECT * FROM S001V01TDIFE WHERE DIFE_ANIO = :anio AND DIFE_ESTA != 'Eliminado'";
        $gstDiasF = $pdo->prepare($qryDiasF);

        $gstDiasF->bindParam(":anio", $year);

        if(!$gstDiasF->execute()){
            return $this->makeResponse(true, "ERR_MCOMSV_SER_CRE008: No se pudieron obtener los días feriados del año solicitado", [], 500);
        }

        $diasF = $gstDiasF->fetchAll($pdo::FETCH_ASSOC);
        $arrDiasF = array();
        //Se comprueba que no haya ningún día feriado entre las fechas de la solicitud
        foreach($diasF as $diaF){
            $strDiaF = $diaF['DIFE_ANIO'] . "-" . $diaF['DIFE_MESX'] . "-" . $diaF['DIFE_DIAX'];
            $dateDiaF = new Carbon($strDiaF);
            if($dateDiaF->gte($fIni) && $dateDiaF->lte($fFin)){
                $diff--;
            }
        }
        //Se consulta la política del usuario y sus días de descanso
        $qryPolDiDe = "SELECT POLI_DVAA, POLI_DIDE, USUA_FEIN FROM S001V01TUSUA LEFT JOIN S001V01TPOLI ON USUA_POLI = POLI_IDPO WHERE USUA_IDUS = :idus";
        $gstPolDiDe = $pdo->prepare($qryPolDiDe);

        $gstPolDiDe->bindParam(":idus", $solicitud['numero_empleado']);

        if(!$gstPolDiDe->execute()){
            return $this->makeResponse(true, "ERR_MCOMSV_SER_CRE009: No se pudo obtener la política de vacaciones del usuario", [], 500);
        }

        $politica = $gstPolDiDe->fetchObject();

        if(!$politica){
            return $this->makeResponse(true, "ERR_MCOMSV_SER_CRE010: El usuario no existe", [], 500);
        }

        $fechaIngreso = new Carbon($politica->USUA_FEIN);
        $diasVacaciones = json_decode($politica->POLI_DVAA, true);
        $diasDescanso = json_decode($politica->POLI_DIDE, true);
        $diasIngEsp = ["Mo" => "Lu", "Tu" => "Ma", "We" => "Mi", "Th" => "Ju", "Fr" => "Vi", "Sa" => "Sa", "Su" => "Do"];
        $diaInicioVacaciones = new Carbon($solicitud['fecha_inicio']);
        //Descontamos los días de descanso
        $diffDiasDesc = $diff;
        for($i = 0; $i <= $diff; $i++){
            $dayName = $diaInicioVacaciones->format('l');
            $dayKey = substr($dayName, 0, 2);
            $dayEsp = $diasIngEsp[$dayKey];

            $descansa = $diasDescanso[$dayEsp];
            if($descansa == "Si") $diffDiasDesc--;
            $diaInicioVacaciones->addDay();
        }
        $diffDiasDesc++;
        //Se valida que el número de días de la solicitud coincida con el calculado
        if($diffDiasDesc != $solicitud['numero_dias']) return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE011: Los días solicitados no coinciden con los calculados", [], 400);
        //Calculamos cuantos días de vacaciones tiene el usuario según su politica
        $diasTotalesVacaciones = 0;
        $diffYears = $hoy->diffInYears($fechaIngreso);
        foreach($diasVacaciones as $key=>$val){
            if(str_contains($key, "-")){
                $limites = explode("-", $key);
                for($i = intval($limites[0]); $i <= intval($limites[1]); $i++){
                    if($diffYears >= $i) $diasTotalesVacaciones = $diasTotalesVacaciones + intval($val);
                }
            }else{
                if($diffYears >= $key) $diasTotalesVacaciones = $diasTotalesVacaciones + intval($val);
            }
        }
        //Consultamos cuantas vacaciones ha tenido el usuario
        $qryVacGoz = "SELECT PEVA_VAPE, PEVA_VAGO FROM S001V01TPEVA WHERE PEVA_EMPL = :empl";
        $gstVacGoz = $pdo->prepare($qryVacGoz);
        $gstVacGoz->bindParam(":empl", $solicitud['numero_empleado']);

        if(!$gstVacGoz->execute()){
            return $this->makeResponse(true, "ERR_MCOMSV_SER_CRE012: No se pudo consultar los periodos vacacionales tomados por el usuario", [], 500);
        }

        $periodos = $gstVacGoz->fetchAll($pdo::FETCH_ASSOC);
        foreach($periodos as $periodo){
            $diasRestar = $periodo['PEVA_VAPE'] - $periodo['PEVA_VAGO'];
            $diasTotalesVacaciones = $diasTotalesVacaciones - $diasRestar;
        }
        //Se valida que el número de días sea menor a los disponibles
        if($diasTotalesVacaciones < $solicitud['numero_dias']) return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE013: Los días solicitados son mayores a los disponibles", [], 400);
        //Obtenemos la plantilla para el envío del correo
        $qryPlanCor = "SELECT COPL_ASUN, COPL_CONT FROM S001V01TCOPL WHERE COPL_PORT = 'MESMSV' AND COPL_NOPL = 'Solicitud Vacaciones'";
        $gstPlanCor = $pdo->prepare($qryPlanCor);

        if(!$gstPlanCor->execute()){
            return $this->makeResponse(true, "ERR_MCOMSV_SER_CRE014: No se pudo consultar la plantilla del correo", [], 500);
        }

        $plantilla = $gstPlanCor->fetchObject();
        //Obtenemos el correo electrónico del jefe directo y de los administradores
        $qryCorreos = "SELECT USUA_COEL FROM S001V01TUSUA LEFT JOIN S001V01TORGA ON ORGA_JEDI = USUA_IDUS WHERE ORGA_IDUS = :idus 
                UNION SELECT USUA_COEL FROM S001V01TUSUA WHERE USUA_PERF = 1";
        $gstCorreos = $pdo->prepare($qryCorreos);
        $gstCorreos->bindParam(":idus", $solicitud['numero_empleado']);

        if(!$gstCorreos->execute()){
            return $this->makeResponse(true, "ERR_MCOMSV_SER_CRE015: No se pudieron obtener los correos de los administradores y el jefe directo", [], 500);
        }

        $correos = array();

        foreach($gstCorreos->fetchAll($pdo::FETCH_ASSOC) as $correo){
            $correos[] = $correo['USUA_COEL'];
        }

        //Se procede a registrar la solicitud
        $qryInsSol = "INSERT INTO S001V01TSOVA (SOVA_RFCE, SOVA_IDUS, SOVA_FEIN, SOVA_FEFI, SOVA_NUDI, SOVA_USRE, SOVA_FERE, SOVA_FEAR) 
                        VALUES (:rfce, :idus, :fein, :fefi, :nudi, :usre, :fere, CURRENT_TIMESTAMP)";
        $gstInsSol = $pdo->prepare($qryInsSol);

        $encriptacion = new EncController();
        $rfce = $encriptacion->desencriptar($solicitud['rfc_empresa']);
        $fere = $hoy->toDateTimeString();

        $gstInsSol->bindParam(":rfce", $rfce);
        $gstInsSol->bindParam(":idus", $solicitud['numero_empleado']);
        $gstInsSol->bindParam(":fein", $solicitud['fecha_inicio']);
        $gstInsSol->bindParam(":fefi", $solicitud['fecha_final']);
        $gstInsSol->bindParam(":nudi", $solicitud['numero_dias']);
        $gstInsSol->bindParam(":usre", $solicitud['numero_empleado']);
        $gstInsSol->bindParam(":fere", $fere);

        if(!$gstInsSol->execute()){
            return $this->makeResponse(true, "ERR_MCOMSV_SER_CRE016: No se pudo ingresar la solicitud", [], 500);
        }

        $empresas = [
            "TME700618RC7" => "TIMKEN"
        ];

        $uriSist = "https://sys.qasirh.ittec.mx/?".$empresas[$rfce]."/mesmav;data=";
        /* $uriSist = "https://ferrariventilatori.mx/vacaciones/#/mesmav;data="; */

        $lastID = $pdo->lastInsertId();

        $arrAprobar = [
            'id_solicitud' => $lastID,
            'estatus' => 'Aprobado'
        ];

        $arrRechazar = [
            'id_solicitud' => $lastID,
            'estatus' => 'Rechazado'
        ];

        $aprobar = $encriptacion->encriptar(json_encode($arrAprobar));
        $rechazar = $encriptacion->encriptar(json_encode($arrRechazar));

        $bindSubject = [
            "%nuvac%" => $lastID,
        ];

        $bindBody = [
            "%idus%" => $solicitud['numero_empleado'],
            "%idsol%" => $lastID,
            "%fini%" => $solicitud['fecha_inicio'],
            "%ffin%" => $solicitud['fecha_final'],
            "%ndias%" => $solicitud['numero_dias'],
            "%uriaprobar%" => $uriSist . $aprobar,
            "%urirechazar%" => $uriSist . $rechazar
        ];

        $correoController = new CorreoController();
        return $this->makeResponse(false, $correoController->enviarCorreo($plantilla, $correos, $bindSubject, $bindBody, "EXITO: Solicitud insertada"));
    }

    public function create(Request $request){
        $validator = Validator::make($request->all(), [
            'rfc_empresa' => 'required|string',
            'numero_empleado' => 'required|string|max:50',
            'fecha_inicio' => 'required|date',
            'fecha_final' => 'required|date',
            'numero_dias' => 'required|integer|max:31'
        ]);

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

        $solicitud = $request->all();

        //Se valida que las fechas pedidas sean mayores a la actual
        $fIni = new Carbon($solicitud['fecha_inicio']);
        $fFin = new Carbon($solicitud['fecha_final']);
        $hoy = Carbon::now()->timezone('America/Mexico_City');
        $pdo = DB::connection()->getPdo();
        $encController = new EncController();
        $rfce = $encController->desencriptar($solicitud['rfc_empresa']);

        if($hoy->gt($fIni)) return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE001: La fecha inicial es menor a la actual", [], 400);
        if($hoy->gt($fFin)) return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE002: La fecha final es menor a la actual", [], 400);
        //Se valida que la fecha final se mayor a la inicial
        if($fIni->gt($fFin)) return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE003: La fecha inicial es mayor a la final", [], 400);

        //Se calcula la diferencia en días entre las fechas ingresadas
        $diff = $fFin->diffInDays($fIni) + 1;

        //Se buscan los días feriados
        $qryDife = "SELECT DIFE_DIAX, DIFE_MESX, DIFE_ANIO FROM S001V01TDIFE WHERE DIFE_RFCE = :rfce AND DIFE_ESTA != 'Eliminado'";
        $gstDife = $pdo->prepare($qryDife);
        $gstDife->bindParam(":rfce", $rfce);

        try{
            $gstDife->execute();
        }catch(PDOException $e){
            return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE004: Hubo un error al obtener los días feriados.", [], 500);
        }

        $dife = $gstDife->fetchAll($pdo::FETCH_ASSOC);

        //Se descuentan los días feriados
        foreach($dife as $dia){
            $diaStr = "$dia[DIFE_ANIO]-" . ($dia['DIFE_MESX'] < 10 ? "0$dia[DIFE_MESX]" : "$dia[DIFE_MESX]") . "-" 
            . ($dia['DIFE_DIAX'] < 10 ? "0$dia[DIFE_DIAX]" : "$dia[DIFE_DIAX]");
            $diaDate = new Carbon($diaStr);
            if($diaDate->gte($fIni) && $diaDate->lte($fFin)){
                $diff--;
            }
        }

        //Consultamos la política de vacaciones del usuario
        $qryPoli = "SELECT S001V01TPOLI.*, S001V01TUSUA.* FROM S001V01TUSUA INNER JOIN S001V01TPOLI ON USUA_POLI = POLI_IDPO WHERE USUA_IDUS = :idus AND POLI_RFCE = :rfce";
        $gstPoli = $pdo->prepare($qryPoli);

        $gstPoli->bindParam(":idus", $solicitud['numero_empleado']);
        $gstPoli->bindParam(":rfce", $rfce);

        try{
            $gstPoli->execute();
        }catch(PDOException $e){
            return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE005: Hubo un error al obtener la política de vacaciones del usuario.", [], 500);
        }

        $politica = $gstPoli->fetchObject();
        if(!$politica) return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE006: El usuario solicitado no existe.", [], 404);

        //Parseamos los días de descanso del usuario
        $diasDescanso = json_decode($politica->POLI_DIDE, true);
        $diasIngEsp = ["Mo" => "Lu", "Tu" => "Ma", "We" => "Mi", "Th" => "Ju", "Fr" => "Vi", "Sa" => "Sa", "Su" => "Do"];

        //Iteramos los el número de días solicitados para descontar los días de descanso
        $fInicio = new Carbon($solicitud['fecha_inicio']);
        $diasVac = $diff;
        for($i = 0; $i < $diff; $i++){
            $dayName = $fInicio->format('l');
            $keyDay = substr($dayName, 0, 2);
            $diaEsp = $diasIngEsp[$keyDay];

            $descansa = $diasDescanso[$diaEsp];
            if($descansa == "Si") $diasVac--;

            $fInicio->addDay();
        }

        //Antes de calcular el descuento de días de la solicitud se verifica que el usuario no tenga periodos vacacionales pendientes
        $qrySolicitudPorTomar = "SELECT SOVA_IDSO FROM S001V01TSOVA WHERE SOVA_IDUS = :idus AND (SOVA_ESTA = 'Pendiente' OR SOVA_ESTA = 'Aprobado') AND 
        SOVA_FEFI > CURRENT_DATE ORDER BY SOVA_IDSO DESC LIMIT 1";
        $gstSolicitudPorTomar = $pdo->prepare($qrySolicitudPorTomar);
        $gstSolicitudPorTomar->bindParam(":idus", $solicitud['numero_empleado']);

        if(!$gstSolicitudPorTomar->execute()){
            return $this->makeResponse(true, "ERR_MCOMSV_SER_CRE007: No se pudo consultar las vacaciones anteriores del usuario.", [], 500);
        }

        $solicitudPorTomar = $gstSolicitudPorTomar->fetchObject();

        if($solicitudPorTomar){
            return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE008: No se puede crear un nuevo periodo vacacional porque aún hay uno no vencido.", [], 401);
        }

        //Se verifica que el número de días solicitudos sea igual al calculado
        if($solicitud['numero_dias'] != $diasVac) return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE009: El número de días solicitados es diferente al calculado.", [], 400);

        //Se procede hacer el cálculo del descuento de los días de vacaciones
        $solicitudAingresar = array();
        $dvaa = json_decode($politica->POLI_DVAA, true);
        $arrDVAA = array();

        foreach($dvaa as $key=>$value){
            if(str_contains($key, "-")){
                $limites = explode("-", $key);
                for($i = intval($limites[0]); $i <= intval($limites[1]); $i++){
                    $arrDVAA[$i] = intval($value);
                }
            }else{
                $arrDVAA[$key] = intval($value);
            }
        }

        //CASO 1: El usuario tiene menos de 90 días laborando
        $fechaIngreso = new Carbon($politica->USUA_FEIN);
        $diasLabor = $hoy->diffInDays($fechaIngreso);
        $aniosLabor = $hoy->diffInYears($fechaIngreso);

        if($diasLabor < 90 && $aniosLabor == 0){
            return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE010: El usuario no tiene días de labor suficientes para crear una solicitud.", [], 401);
        }

        //CASO 2: El usuario es de nuevo ingreso pero tiene más de 90 días
        if($diasLabor >= 90 && $aniosLabor == 0){
            $periodoSolicitud = $fechaIngreso->toDateString() . "|" . $fechaIngreso->addYear()->toDateString();
            //Calculamos los días EAYG
            $diasEAYG = ($arrDVAA[1] / 365) * $diasLabor;
            $diasEAYG = intval($diasEAYG);

            //Se buscan los periodos vacacionales tomados para hacer el descuento a los días disponibles
            $qryPeva = "SELECT PEVA_VAPE, PEVA_VAGO FROM S001V01TPEVA WHERE PEVA_EMPL = :empl AND PEVA_RFCE = :rfce AND PEVA_PEVA = :peva";
            $gstPeva = $pdo->prepare($qryPeva);

            $gstPeva->bindParam(":empl", $solicitud['numero_empleado']);
            $gstPeva->bindParam(":rfce", $rfce);
            $gstPeva->bindParam(":peva", $periodoSolicitud);

            try{
                $gstPeva->execute();
            }catch(PDOException $e){
                return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE011: Hubo un error al consultar los periodos vacacionales tomados por el usuario.", [], 500);
            }

            $peva = $gstPeva->fetchAll($pdo::FETCH_ASSOC);

            foreach($peva as $per){
                $diasTomados = $per['PEVA_VAPE'] - $per['PEVA_VAGO'];
                $diasEAYG -= $diasTomados;
            }

            if($diasEAYG < $solicitud['numero_dias']){
                return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE012: El número de días solicitados es mayor a los disponibles por Earn As You Go.", [], 401);
            }
            
            //Si la solicitud es correcta se procede a ingresarla a la base
            $ulPEVA = end($peva);
            $solicitudAingresar[] = [
                "PEVA_PEVA" => $periodoSolicitud,
                "PEVA_FETO" => $fIni->toDateString() . "|" . $fFin->toDateString(),
                "PEVA_VAPE" => !$ulPEVA ? $arrDVAA[1] : $ulPEVA['PEVA_VAGO'],
                "PEVA_VAGO" => !$ulPEVA ? $arrDVAA[1] - $solicitud['numero_dias'] : $ulPEVA['PEVA_VAGO'] - $solicitud['numero_dias'],
                "SOVA_FEIN" => $fIni->toDateString(),
                "SOVA_FEFI" => $fFin->toDateString(),
                "SOVA_NUDI" => $solicitud['numero_dias'],
                "SOVA_EAYG" => "Si",
            ];
            
            goto ingresarSolicitud;
        }

        //CASO 3: El usuario tiene 1 año de labor
        if($aniosLabor == 1){
            $periodoSolicitud = $fechaIngreso->toDateString() . "|" . $fechaIngreso->addYear()->toDateString();
            $diasVacDisp = $arrDVAA[1];

            //Se buscan los periodos vacacionales tomados para hacer el descuento a los días disponibles
            $qryPeva = "SELECT PEVA_VAPE, PEVA_VAGO FROM S001V01TPEVA WHERE PEVA_EMPL = :empl AND PEVA_RFCE = :rfce AND PEVA_PEVA = :peva";
            $gstPeva = $pdo->prepare($qryPeva);

            $gstPeva->bindParam(":empl", $solicitud['numero_empleado']);
            $gstPeva->bindParam(":rfce", $rfce);
            $gstPeva->bindParam(":peva", $periodoSolicitud);

            try{
                $gstPeva->execute();
            }catch(PDOException $e){
                return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE013: Hubo un error al consultar los periodos vacacionales tomados por el usuario.", [], 500);
            }

            $peva = $gstPeva->fetchAll($pdo::FETCH_ASSOC);

            foreach($peva as $per){
                $diasTomados = $per['PEVA_VAPE'] - $per['PEVA_VAGO'];
                $diasVacDisp -= $diasTomados;
            }

            $diffEAYG = $hoy->diffInDays($fechaIngreso);
            if($diffEAYG >= 90){
                //CASO 3.1: El usuario tiene EAYG
                $diasEAYG = ($arrDVAA[2] / 365) * $diffEAYG;
                $diasEAYG = intval($diasEAYG);
                $periodoEAYG = $fechaIngreso->toDateString() . "|" . $fechaIngreso->addYear()->toDateString();

                //Se buscan los periodos vacacionales tomados para hacer el descuento a los días disponibles
                $qryPevaEAYG = "SELECT PEVA_VAPE, PEVA_VAGO FROM S001V01TPEVA WHERE PEVA_EMPL = :empl AND PEVA_RFCE = :rfce AND PEVA_PEVA = :peva";
                $gstPevaEAYG = $pdo->prepare($qryPevaEAYG);

                $gstPevaEAYG->bindParam(":empl", $solicitud['numero_empleado']);
                $gstPevaEAYG->bindParam(":rfce", $rfce);
                $gstPevaEAYG->bindParam(":peva", $periodoEAYG);

                try{
                    $gstPevaEAYG->execute();
                }catch(PDOException $e){
                    return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE014: Hubo un error al consultar los periodos vacacionales tomados por el usuario.", [], 500);
                }

                $pevaEAYG = $gstPevaEAYG->fetchAll($pdo::FETCH_ASSOC);

                foreach($pevaEAYG as $per){
                    $diasTomados = $per['PEVA_VAPE'] - $per['PEVA_VAGO'];
                    $diasEAYG -= $diasTomados;
                }

                $diasTotales = $diasVacDisp + $diasEAYG;

                if($diasTotales < $solicitud['numero_dias']){
                    return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE015: El número de días solicitados es mayor a los disponibles.", [], 401);
                }

                $ulPEVA = end($peva);
                
                if($solicitud['numero_dias'] > $diasVacDisp){
                    $diasEAYGtomados = $solicitud['numero_dias'] - $arrDVAA[1];

                    $solicitudAingresar[] = [
                        "PEVA_PEVA" => $periodoSolicitud,
                        "PEVA_FETO" => $fIni->toDateString() . "|" . $fFin->toDateString(),
                        "PEVA_VAPE" => $diasVacDisp,
                        "PEVA_VAGO" => 0,
                        "SOVA_FEIN" => $fIni->toDateString(),
                        "SOVA_FEFI" => $fFin->toDateString(),
                        "SOVA_NUDI" => $solicitud['numero_dias'],
                        "SOVA_EAYG" => "No",
                    ];

                    $solicitudAingresar[] = [
                        "PEVA_PEVA" => $periodoEAYG,
                        "PEVA_FETO" => $fIni->toDateString() . "|" . $fFin->toDateString(),
                        "PEVA_VAPE" => $arrDVAA[2],
                        "PEVA_VAGO" => $arrDVAA[2] - $diasEAYGtomados,
                        "SOVA_FEIN" => $fIni->toDateString(),
                        "SOVA_FEFI" => $fFin->toDateString(),
                        "SOVA_NUDI" => $solicitud['numero_dias'],
                        "SOVA_EAYG" => "Si",
                    ];
                }else{
                    $solicitudAingresar[] = [
                        "PEVA_PEVA" => $periodoSolicitud,
                        "PEVA_FETO" => $fIni->toDateString() . "|" . $fFin->toDateString(),
                        "PEVA_VAPE" => !$ulPEVA ? $arrDVAA[1] : $ulPEVA['PEVA_VAGO'],
                        "PEVA_VAGO" => !$ulPEVA ? $arrDVAA[1] - $solicitud['numero_dias'] : $ulPEVA['PEVA_VAGO'] - $solicitud['numero_dias'],
                        "SOVA_FEIN" => $fIni->toDateString(),
                        "SOVA_FEFI" => $fFin->toDateString(),
                        "SOVA_NUDI" => $solicitud['numero_dias'],
                        "SOVA_EAYG" => "No",
                    ];
                }
                goto ingresarSolicitud;
            }else{
                //CASO 3.2: El usuario no tiene EAYG
                if($diasVacDisp < $solicitud['numero_dias']){
                    return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE016: El número de días solicitados es mayor a los disponibles.", [], 401);
                }

                $ulPEVA = end($peva);
                $solicitudAingresar[] = [
                    "PEVA_PEVA" => $periodoSolicitud,
                    "PEVA_FETO" => $fIni->toDateString() . "|" . $fFin->toDateString(),
                    "PEVA_VAPE" => !$ulPEVA ? $arrDVAA[1] : $ulPEVA['PEVA_VAGO'],
                    "PEVA_VAGO" => !$ulPEVA ? $arrDVAA[1] - $solicitud['numero_dias'] : $ulPEVA['PEVA_VAGO'] - $solicitud['numero_dias'],
                    "SOVA_FEIN" => $fIni->toDateString(),
                    "SOVA_FEFI" => $fFin->toDateString(),
                    "SOVA_NUDI" => $solicitud['numero_dias'],
                    "SOVA_EAYG" => "No",
                ];
                
                goto ingresarSolicitud;
            }
        }

        //CASO 4: El usuario tiene más de un año de labor
        if($aniosLabor > 1){
            //Se itera el número de años laborados para obtener los periodos normales
            $periodosArr = array();
            $diasTotales = 0;
            for($i = 1; $i <= $aniosLabor; $i++){
                $periodoStr = $fechaIngreso->toDateString() . "|" . $fechaIngreso->addYear()->toDateString();
                $diasDisp = $arrDVAA[$i];
                //Se buscan los periodos vacacionales tomados para hacer el descuento a los días disponibles
                $qryPeva = "SELECT PEVA_VAPE, PEVA_VAGO FROM S001V01TPEVA WHERE PEVA_EMPL = :empl AND PEVA_RFCE = :rfce AND PEVA_PEVA = :peva";
                $gstPeva = $pdo->prepare($qryPeva);

                $gstPeva->bindParam(":empl", $solicitud['numero_empleado']);
                $gstPeva->bindParam(":rfce", $rfce);
                $gstPeva->bindParam(":peva", $periodoSolicitud);

                try{
                    $gstPeva->execute();
                }catch(PDOException $e){
                    return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE017: Hubo un error al consultar los periodos vacacionales tomados por el usuario.", [], 500);
                }

                $peva = $gstPeva->fetchAll($pdo::FETCH_ASSOC);
                foreach($peva as $per){
                    $diasTomados = $per['PEVA_VAPE'] - $per['PEVA_VAGO'];
                    $diasDisp -= $diasTomados;
                }

                $caducidad = $hoy->diffInMonths($fechaIngreso);
                if($caducidad <= 18){
                    $diasTotales += $diasDisp;
                    $periodosArr[] = [
                        "periodo" => $periodoStr,
                        "diasDisp" => $diasDisp,
                        "eayg" => "No",
                        "ulPeva" => end($peva)
                    ];
                }
            }
            //Después de obtener los periodos se añade el periodo EAYG si está disponible
            $diffEAYG = $hoy->diffInDays($fechaIngreso);
            if($diffEAYG >= 90){
                $periodoEAYG = $fechaIngreso->toDateString() . "|" . $fechaIngreso->addYear()->toDateString();
                $diasEAYG = ($arrDVAA[$aniosLabor + 1] / 365) * $diffEAYG;
                $diasEAYG = intval($diasEAYG);
                
                $qryPevaEAYG = "SELECT PEVA_VAPE, PEVA_VAGO FROM S001V01TPEVA WHERE PEVA_EMPL = :empl AND PEVA_RFCE = :rfce AND PEVA_PEVA = :peva";
                $gstPevaEAYG = $pdo->prepare($qryPevaEAYG);

                $gstPevaEAYG->bindParam(":empl", $solicitud['numero_empleado']);
                $gstPevaEAYG->bindParam(":rfce", $rfce);
                $gstPevaEAYG->bindParam(":peva", $periodoEAYG);

                try{
                    $gstPevaEAYG->execute();
                }catch(PDOException $e){
                    return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE018: Hubo un error al consultar los periodos vacacionales tomados por el usuario.", [], 500);
                }

                $pevaEAYG = $gstPevaEAYG->fetchAll($pdo::FETCH_ASSOC);
                $diasPeriodoSig = $arrDVAA[$aniosLabor + 1];
                foreach($pevaEAYG as $per){
                    $diasTomados = $per['PEVA_VAPE'] - $per['PEVA_VAGO'];
                    $diasEAYG -= $diasTomados;
                    $diasPeriodoSig -= $diasTomados;
                }

                $diasTotales += $diasEAYG;

                $periodosArr[] = [
                    "periodo" => $periodoEAYG,
                    "diasDisp" => $diasPeriodoSig,
                    "eayg" => "Si",
                    "ulPeva" => end($pevaEAYG)
                ];
            }
            //Se comprueba que los días totales no sean menores a los solicitados
            if($diasTotales < $solicitud['numero_dias']){
                return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE019: El número de días solicitados es mayor a los disponibles.", [], 401);
            }
            //Se recorre el arreglo de los periodos
            $index = 0;
            $cont = 0;
            $diasTom = 0;
            $diasRest = 0;
            do{
                $cont++;

                if($diasTom == 0){
                    $diasRest = $periodosArr[$index]["diasDisp"];
                }else if($diasTom == $periodosArr[$index]["diasDisp"]){
                    $index++;
                    $diasRest = $periodosArr[$index]["diasDisp"];
                    $diasTom = 0;
                }

                $diasTom++;

                $solicitudAingresar[$index] = [
                    "PEVA_PEVA" => $periodosArr[$index]["periodo"],
                    "PEVA_FETO" => $fIni->toDateString() . "|" . $fFin->toDateString(),
                    "PEVA_VAPE" => $periodosArr[$index]["diasDisp"],
                    "PEVA_VAGO" => $periodosArr[$index]["diasDisp"] - $diasTom,
                    "SOVA_FEIN" => $fIni->toDateString(),
                    "SOVA_FEFI" => $fFin->toDateString(),
                    "SOVA_NUDI" => $solicitud['numero_dias'],
                    "SOVA_EAYG" => $periodosArr[$index]["eayg"],
                ];

                $diasRest--;
            }while($cont < $solicitud['numero_dias']);
        }
        
        //Ingresamos la solicitud a la base
        ingresarSolicitud:
        $eayg = "No";
        $fere = $hoy->toDateTimeString();
        foreach($solicitudAingresar as $sol){ $eayg = $sol['SOVA_EAYG']; }
        $qryInsSol = "INSERT INTO S001V01TSOVA (SOVA_RFCE, SOVA_IDUS, SOVA_FEIN, SOVA_FEFI, SOVA_NUDI, SOVA_EAYG, SOVA_USRE, SOVA_FERE, SOVA_FEAR) VALUES (:rfce, 
        :idus, :fein, :fefi, :nudi, :eayg, :usre, :fere, CURRENT_TIMESTAMP)";
        $gstInsSol = $pdo->prepare($qryInsSol);

        $gstInsSol->bindParam(":rfce", $rfce);
        $gstInsSol->bindParam(":idus", $solicitud['numero_empleado']);
        $gstInsSol->bindParam(":fein", $solicitud['fecha_inicio']);
        $gstInsSol->bindParam(":fefi", $solicitud['fecha_final']);
        $gstInsSol->bindParam(":nudi", $solicitud['numero_dias']);
        $gstInsSol->bindParam(":eayg", $eayg);
        $gstInsSol->bindParam(":usre", $solicitud['numero_empleado']);
        $gstInsSol->bindParam(":fere", $fere);

        try{
            $gstInsSol->execute();
        }catch(PDOException $e){
            return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE020: Hubo un error al ingresar la solicitud a la base de datos.", [], 500);
        }

        $lastID = $pdo->lastInsertId();
        foreach($solicitudAingresar as $sol){
            $qryInsPEVA = "INSERT INTO S001V01TPEVA (PEVA_IDSO, PEVA_RFCE, PEVA_EMPL, PEVA_PEVA, PEVA_FETO, PEVA_VAPE, PEVA_VAGO) VALUES (:idso, :rfce, 
            :empl, :peva, :feto, :vape, :vago)";
            $gstInsPEVA = $pdo->prepare($qryInsPEVA);

            $gstInsPEVA->bindParam(":idso", $lastID);
            $gstInsPEVA->bindParam(":rfce", $rfce);
            $gstInsPEVA->bindParam(":empl", $solicitud['numero_empleado']);
            $gstInsPEVA->bindParam(":peva", $sol['PEVA_PEVA']);
            $gstInsPEVA->bindParam(":feto", $sol['PEVA_FETO']);
            $gstInsPEVA->bindParam(":vape", $sol['PEVA_VAPE']);
            $gstInsPEVA->bindParam(":vago", $sol['PEVA_VAGO']);

            try{
                $gstInsPEVA->execute();
            }catch(PDOException $e){
                return $this->makeResponse(true, "ERR_MCOMSV_USU_CRE021: Hubo un error al ingresar los periodos vacacionales a la base de datos.", [], 500);
            }
        }

        //Obtenemos la plantilla para el envío del correo
        $qryPlanCor = "SELECT COPL_ASUN, COPL_CONT FROM S001V01TCOPL WHERE COPL_PORT = 'MESMSV' AND COPL_NOPL = 'Solicitud Vacaciones'";
        $gstPlanCor = $pdo->prepare($qryPlanCor);

        if(!$gstPlanCor->execute()){
            return $this->makeResponse(true, "ERR_MCOMSV_SER_CRE022: No se pudo consultar la plantilla del correo", [], 500);
        }

        $plantilla = $gstPlanCor->fetchObject();
        //Obtenemos el correo electrónico del jefe directo y de los administradores
        $qryCorreos = "SELECT USUA_COEL FROM S001V01TUSUA LEFT JOIN S001V01TORGA ON ORGA_JEDI = USUA_IDUS WHERE ORGA_IDUS = :idus 
                UNION SELECT USUA_COEL FROM S001V01TUSUA WHERE USUA_PERF = 1";
        $gstCorreos = $pdo->prepare($qryCorreos);
        $gstCorreos->bindParam(":idus", $solicitud['numero_empleado']);

        if(!$gstCorreos->execute()){
            return $this->makeResponse(true, "ERR_MCOMSV_SER_CRE023: No se pudieron obtener los correos de los administradores y el jefe directo", [], 500);
        }

        $correos = array();

        foreach($gstCorreos->fetchAll($pdo::FETCH_ASSOC) as $correo){
            $correos[] = $correo['USUA_COEL'];
        }

        $empresas = [
            "TME700618RC7" => "TIMKEN"
        ];

        $functionsController = new FunctionsController();

        $uriSist = "https://sys.qasirh.ittec.mx/?".$empresas[$rfce]."/mesmav;data=";

        $arrAprobar = [
            'id_solicitud' => $lastID,
            'estatus' => 'Aprobado'
        ];

        $arrRechazar = [
            'id_solicitud' => $lastID,
            'estatus' => 'Rechazado'
        ];

        $aprobar = $encController->encriptar(json_encode($arrAprobar));
        $rechazar = $encController->encriptar(json_encode($arrRechazar));

        $bindSubject = [
            "%nuvac%" => $lastID,
        ];

        $bindBody = [
            "%nous%" => $functionsController->joinName($politica->USUA_NOMB, $politica->USUA_APPA, $politica->USUA_APMA),
            "%idus%" => $solicitud['numero_empleado'],
            "%idsol%" => $lastID,
            "%fini%" => $functionsController->orderDate($solicitud['fecha_inicio']),
            "%ffin%" => $functionsController->orderDate($solicitud['fecha_final']),
            "%ndias%" => $solicitud['numero_dias'],
            "%uriaprobar%" => $uriSist . $aprobar,
            "%urirechazar%" => $uriSist . $rechazar
        ];

        $correoController = new CorreoController();
        return $this->makeResponse(false, $correoController->enviarCorreo($plantilla, $correos, $bindSubject, $bindBody, "EXITO: Solicitud insertada", $empresas[$rfce]));
    }

    public function update(Request $request){
        $validator = Validator::make($request->all(), [
            'numero_empleado' => 'required|string|max:50',
            'fecha_inicio' => 'required|date',
            'fecha_final' => 'required|date',
            'numero_dias' => 'required|integer|max:31',
            'id_solicitud' => 'required|integer'
        ]);

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

        $solicitud = $request->all();

        //Se valida que las fechas pedidas sean mayores a la actual
        $fIni = new Carbon($solicitud['fecha_inicio']);
        $fFin = new Carbon($solicitud['fecha_final']);
        $hoy = Carbon::now()->timezone('America/Mexico_City');
        $pdo = DB::connection()->getPdo();

        if($hoy->gt($fIni)) return $this->makeResponse(true, "ERROR_USU(MCOMSV001): La fecha inicial es menor a la actual", [], 400);
        if($hoy->gt($fFin)) return $this->makeResponse(true, "ERROR_USU(MCOMSV002): La fecha final es menor a la actual", [], 400);
        //Se valida que la fecha final se mayor a la inicial
        if($fIni->gt($fFin)) return $this->makeResponse(true, "ERROR_USU(MCOMSV003): La fecha inicial es mayor a la final", [], 400);
        //Se calcula la diferencia en días
        $diff = $fFin->diffInDays($fIni);
        //Se buscan los días feriados
        $year = $fIni->year;
        $qryDiasF = "SELECT * FROM S001V01TDIFE WHERE DIFE_ANIO = :anio AND DIFE_ESTA != 'Eliminado'";
        $gstDiasF = $pdo->prepare($qryDiasF);

        $gstDiasF->bindParam(":anio", $year);

        if(!$gstDiasF->execute()){
            return $this->makeResponse(true, "ERROR_SER(MCOMSV000): No se pudieron obtener los días feriados del año solicitado", [], 500);
        }

        $diasF = $gstDiasF->fetchAll($pdo::FETCH_ASSOC);
        $arrDiasF = array();
        //Se comprueba que no haya ningún día feriado entre las fechas de la solicitud
        foreach($diasF as $diaF){
            $strDiaF = $diaF['DIFE_ANIO'] . "-" . $diaF['DIFE_MESX'] . "-" . $diaF['DIFE_DIAX'];
            $dateDiaF = new Carbon($strDiaF);
            if($dateDiaF->gte($fIni) && $dateDiaF->lte($fFin)){
                $diff--;
            }
        }
        //Se valida que el número de días de la solicitud coincida con el calculado
        if($diff != $solicitud['numero_dias']) return $this->makeResponse(true, "ERROR_USU(MCOMSV004): Los días solicitados no coinciden con los calculados", [], 400);
        //Se procede a registrar la solicitud
        $qryInsSol = "UPDATE S001V01TSOVA SET SOVA_FEIN = :fein, SOVA_FEFI = :fefi, SOVA_NUDI = :nudi, SOVA_USMO = :usmo, SOVA_FEMO = :femo, SOVA_FEAR = CURRENT_TIMESTAMP
                        WHERE SOVA_IDSO = :idso";
        $gstInsSol = $pdo->prepare($qryInsSol);

        $femo = $hoy->toDateTimeString();

        $gstInsSol->bindParam(":fein", $solicitud['fecha_inicio']);
        $gstInsSol->bindParam(":fefi", $solicitud['fecha_final']);
        $gstInsSol->bindParam(":nudi", $solicitud['numero_dias']);
        $gstInsSol->bindParam(":usmo", $solicitud['numero_empleado']);
        $gstInsSol->bindParam(":femo", $femo);
        $gstInsSol->bindParam(":idso", $solicitud['id_solicitud']);

        if(!$gstInsSol->execute()){
            return $this->makeResponse(true, "ERROR_SER(MCOMSV000): No se pudo modificar la solicitud", [], 500);
        }

        return $this->makeResponse(false, "EXITO: Solicitud modificada");
    }

    public function getSolicitudes($estatus, $idus, $rfce, $nonce){
        $pdo = DB::connection()->getPdo();
        $encController = new EncController();
        $rfce = $encController->desencriptar(base64_encode($rfce . "|" . $nonce));

        $qry = "SELECT SOVA_IDSO AS IDSOLICITUD, SOVA_FEIN AS FECHAINICIO, SOVA_FEFI AS FECHAFIN, SOVA_NUDI AS NUMERODIAS 
                FROM S001V01TSOVA WHERE SOVA_IDUS = :idus AND SOVA_ESTA = :esta AND SOVA_RFCE = :rfce";
        $gst = $pdo->prepare($qry);

        $estatus = ucfirst($estatus);
        $gst->bindParam(":idus", $idus);
        $gst->bindParam(":esta", $estatus);
        $gst->bindParam(":rfce", $rfce);

        if(!$gst->execute()){
            return $this->makeResponse(true, "ERR_MESMSV_GSO000: No se pudo obtener las solicitudes", [], 500);
        }

        return $this->makeResponse(false, "EXITO", $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;
    }
}
