<?php
/**
 * Clase encargada de la sincronización de data entre las plataformas e ILSAdmin
 */
class apiclientils extends Model
{
    /**
     * Url to the API
     * @var string
     */
  //  private $url = URL_SEND_ILSADMIN.'/app/api-ils';
    private $url = URL_SEND_ILSADMIN.'/app/api_snc';
    /**
     * Sincronización 1=activo 0=inactivo
     * @var int
     */
    private $status = SEND_ILSADMIN ?: 0;
    /**
     * Tiempo máximo que se espera la respuesta del servidor
     * @var int
     */
    public static $TIMEOUT = 10;
    /**
     * token de sincronización
     * @var string
     */
    public static $token = '';
    /**
     * credenciales de sincronización
     * @var string[]
     */
   // public $auth = ['users' => 'ApiRest', 'pass' => '3m6%26%mF+#tC?2k'] ;
    public $auth = ['users' => 'ApiHas', 'pass' => 'F9&wKaDWu*4QELgLBLLr'];
    /**
     * Total de data sincronizada
     * @var int
     */
    public $totalToSend = 0;

    /**
     * Tabla donde se guardan los registros en cola
     * @var string
     */
    public $tablaCola = 'cola_soap';

    /**
     * ids De los registros procesados
     * @var string
     */
    public $idsProcess = [];

    /**
     * Constructor devuelve instancia de la clase
     */
    public function __construct()
    {
        if ($_REQUEST['debug']) {
            $this->tablaCola = 'cola_soap_debug';
        }
        return $this;
    }

    /**
     * Lista de plataformas que se sincronizan
     * 
     * @param $table
     * @return array|string
     */
    public static function getTablesAndId($table)
    {
        $tablasIds = [
            'beneficiaries'             => 'id',
            'broker'                    => 'id_broker',
            'broker_nivel'              => 'id',
            'broker_parameters'         => 'id_broker',
            'broker_parameters_detail'  => 'id',
            'orders'                    => 'id',
            'transactimport'            => 'id',
            'orders_raider'             => 'id',
            'order_comision'            => 'id',
            'coupons'                   => 'id',
            'relaciotn_coupons'         => 'id_detail',
            'plans'                     => 'id',
            'plan_category'             => 'id_plan_categoria',
            'plan_categoria_detail'     => 'id',
            'plan_detail'               => 'id',
            'plan_raider'               => 'id',
            'plans_wording'             => 'id_plan_wording',
            'plans_wording_type'        => 'id_plans_wording_type',
            'policies_parameter'        => 'id',
            'phone_plans'               => 'id_phone',
            'phone_plans_text'          => 'id',
            'phone_parameter'           => 'id_phone',
            'phone_parameter_text'      => 'id',
            'plan_times'                => 'id',
            'relacion_plan_cupon'       => 'id_plan_coupon',
            'relaciotn_restriction'     => 'id_detail',
            'restriction'               => 'id_restric',
            'plan_type'                 => 'id',
            'plan_voucher'              => 'id',
            'benefit'                   => 'id',
            'benefit_type'              => 'id',
            'benefit_detail'            => 'id',
            'benefit_plan'              => 'id',
            'benefit_product'           => 'id',
            'raiders'                   => 'id_raider',
            'raiders_detail'            => 'id',
            'revocation_parameter'      => 'id',
            'territory'                 => 'id_territory',
            'user_associate'            => 'id',
            'users'                     => 'id',
            'usoweb_parameter'          => 'id',
            'wording_parameter'         => 'id',
            'plan_times_ag_net'         => 'id',
            'plan_band_age'             => 'id',
            'plans_renewal'             => 'id',
            'plan_deductible'           => 'id',
            'sms'                       => 'id_log_sms',
            'surcharge_age'             => 'srchg_age_id',
            'surcharge_region'          => 'srchg_region_id',
            'credit_note'               => 'nro_notaCredito',
            'payment_orders'            => 'id',
            'providers'                 => 'id_provider',
            'currency'                  => 'id_currency',
            'payment_record'            => 'id',
            'clients_corporate'         => 'id_client',
            'plan_esim'                 => 'id',
            'ESIM_orden'                => 'id'
        ];
        return empty($table) ? $tablasIds : $tablasIds[$table];
    }

    /**
     * Retorna el prefijo de la plataforma
     * @return string
     */
    public function getPrefixToParameters()
    {
        static $prefijo;
        if (empty($prefijo)) {
            $prefijo = $this->_SQL_tool(
                $this->SELECT_SINGLE,
                __METHOD__,
                'SELECT parameter_value FROM `parameters` WHERE parameter_key like "PREFIJO"'
            )['parameter_value'];
        }
        return $prefijo;
    }

    /**
     * Retorna el prefijo de la agencia master
     * @return string
     */
    public function getPrefixToBroker($idBroker)
    {
        static $prefijoBroker;
        if (empty($prefijoBroker[$idBroker])) {
            //Obtenemos el broker parent
            $idBrokerParent = $this->brokerParent($idBroker);

            //Buscamos si el broker es master y obtenemos el prefijo
            $prefijoBroker[$idBrokerParent] = $this->_SQL_tool(
                $this->SELECT_SINGLE,
                __METHOD__,
                "SELECT prefijo FROM `broker_parameters` WHERE `id_broker` = '{$idBrokerParent}'"
            )['prefijo'] ?: $this->getPrefixToParameters();

            //Guardamos en cache el prefijo del broker en el broker parent
            $prefijoBroker[$idBroker] = $prefijoBroker[$idBroker] ?: $prefijoBroker[$idBrokerParent];
        }
        return $prefijoBroker[$idBroker];
    }
    /**
     * Se encarga de procesar los header de la petición y para guardar el token
     * 
     * @param $curl
     * @param $data
     * @return string
     */
    private function __responseHeaderCallback($curl, $data)
    {
        if (($strlen = strlen($data)) <= 2) return $strlen;
        if (substr($data, 0, 4) == 'HTTP')
            $this->response->code = (int) substr($data, 9, 3);
        else {
            $data = trim($data);
            if (strpos($data, ': ') === false) {
                return $strlen;
            }
            list($header, $value) = explode(': ', $data, 2);
            $header = strtolower($header);
            if ($header == 'token') {
                self::$token = $value;
            }
        }
        return $strlen;
    }
    /**
     * Llamada a la api de ILSAdmin
     * @param $url
     * @param $data
     * @return array|string
     */
    public function callApi($url, $data)
    {
        $curl = curl_init();
        $headers = [
            "authorization:Basic " . base64_encode($this->auth['users'] . ":" . $this->auth['pass']),
        ];
        if (self::$token) {
            $headers[] = 'TOKEN:' . self::$token;
        }
        curl_setopt_array($curl, array(
            CURLOPT_URL             => $url,
            CURLOPT_RETURNTRANSFER  => true,
            CURLOPT_HEADER          => false,
            CURLOPT_ENCODING        => "",
            CURLOPT_MAXREDIRS       => 10,
            CURLOPT_TIMEOUT         => self::$TIMEOUT,
            CURLOPT_CUSTOMREQUEST   => "POST",
            CURLOPT_POST            => true,
            CURLOPT_POSTFIELDS      => $data,
            CURLOPT_HTTPHEADER      => $headers,
            CURLOPT_HEADERFUNCTION  => [&$this, '__responseHeaderCallback']
        ));
        $response = curl_exec($curl);
        $err = curl_error($curl);
        curl_close($curl);
        $resultJson = json_decode(trim($response), true) ?: self::clearSpace(strip_tags($response));
        return $resultJson ?: (json_decode(trim($err), true) ?: $err);
    }

    /**
     * Obtiene los registros que se van a sincronizar
     * 
     * @param $tabla
     * @param $where
     * @return array|bool
     */
    public function GetDataBD($tabla, $where)
    {
        $registros = $this->_SQL_tool(
            $this->SELECT,
            __METHOD__,
            "SELECT * FROM {$tabla} WHERE {$where}"
        );
        $this->totalToSend = $this->total_verdadero;
        return $registros;
    }
    /**
     * Verificamos si el plan asociado al registro tiene un prefijo personalizado, se cambia el prefijo de ser necesario
     * 
     * @param $registros
     * @param $tabla
     * @return void
     */
    protected function fixPrefix(&$registros, $tabla)
    {
        static $idBroker;

        // Base a las tablas obtenemos el id de la orden
        if (in_array($tabla, ["beneficiaries", "orders_raider"])) {
            $idOrden = $registros['id_orden'];
        }

        if (in_array($tabla, ["order_comision"])) {
            $idOrden = $registros['id_order'];
        }

        if (in_array($tabla, ["orders"])) {
            $idOrden = $registros['id'];
        }

        //Con el id de la orden obtenemos el id del broker
        if ($idOrden && empty($idBroker[$idOrden])) {
            $query = "SELECT agencia FROM orders WHERE id='{$idOrden}'";
            $idBroker[$idOrden] = $this->_SQL_tool('SELECT_SINGLE', __METHOD__, $query)['agencia'];
        }

        //obtenemos el id el plan a partir de las tablas
        if (in_array($tabla, ['plans_wording', 'plan_times', 'benefit_plan', 'plan_raider'])) {
            $idPlan = $registros['id_plan'];
        }

        if ($tabla == "restriction") {
            $idPlan = $registros['id_plans'];
        }

        if ($tabla == "plans") {
            $idPlan = $registros['id'];
        }

        if ($tabla == "plan_detail") {
            $idPlan = $registros['plan_id'];
        }

        //con el id del plan obtenemos el id del broker
        if ($idPlan && empty($idBroker[$idPlan])) {
            $query = "SELECT id_broker FROM restriction WHERE id_plans='{$idPlan}'";
            $idBroker[$idPlan] = $this->_SQL_tool('SELECT_SINGLE', __METHOD__, $query)['id_broker'];
        }

        //Si el id del broker existe obtenemos el prefijo
        if ($idBroker[$idPlan] || $idBroker[$idOrden]) {
            $registros['prefijo'] = $this->getPrefixToBroker($idBroker[$idPlan] ?: $idBroker[$idOrden]);
        }
    }

    /**
     * obtener el broker parent
     * 
     * @param $idBroker
     * @return int
     */
    protected function brokerParent($idBroker)
    {
        static $idBrokerParent;
        if (empty($idBrokerParent[$idBroker])) {
            $query = "SELECT parent FROM broker_nivel WHERE id_broker = '{$idBroker}'";
            $idBrokerParent[$idBroker] = $this->_SQL_tool('SELECT_SINGLE', __METHOD__, $query)['parent'];
            $idBrokerParent[$idBroker] = $idBrokerParent[$idBroker] ? $this->brokerParent($idBrokerParent[$idBroker]) : $idBroker;
        }
        return $idBrokerParent[$idBroker];
    }

    /**
     * @param $registros
     * @param $tabla
     * @return void
     */
    protected function fixDataAndPrefix(&$registros, $tabla, $customPrefix)
    {
        //Los campos sin valor son limpiados para reducir el tamaño de la petición
        $registros = array_filter($registros, "strlen");

        //Cargamos el prefijo genérico
        $registros['prefijo'] = $this->getPrefixToParameters();
        $this->fixPrefix($registros, $tabla);
        //Si enviamos un prefijo personalizado reemplazamos el valor
        if ($customPrefix && $registros['prefijo'] <> $customPrefix) {
            $registros['prefijo'] = $customPrefix;
        }
    }

    /**
     * @param $tabla
     * @param $idField
     * @param $ids
     * @return false|void
     */
    public function updateChargedField($tabla, $idField, $ids = [])
    {
        if (empty($ids)) {
            return false;
        }
        //Apagamos el envió automático de data para evitar crear un bucle infinito
        $this->auto_soap = false;
        $ids = implode("','", $ids);
        $this->_SQL_tool("UPDATE", __METHOD__, "UPDATE {$tabla} SET cargado=1 WHERE {$idField} in('{$ids}')");
        //Activamos el envió de data automático
        $this->auto_soap = true;
    }

    /**
     * Consulta y Envía los registros a ILSAdmin
     * 
     * @param $tabla
     * @param $operacion
     * @param $where
     * @return array|string[]
     */
    public function sendApi($tabla, $operacion, $where, $customPrefix = '')
    {
        if (empty($where) || $this->status != 1) {
            return false;
        }
        $registros = $this->GetDataBD($tabla, $where);

        if (empty($registros)) {
            return false;
        }

        $idField = self::getTablesAndId($tabla);
        $this->idsProcess = array_column($registros, $idField);

        //Bandera que indica si la tabla posee el campo cargado
        $cargado = isset($registros[0]['cargado']);

        foreach ($registros as &$value) {
            $this->fixDataAndPrefix($value, $tabla, $customPrefix);
        }

        if ($this->comprobarCola() === 0) {
            try {
                $callApi = $this->callApi(
                    $this->url . '/SetDataArray',
                    base64_encode(json_encode([
                        'operacion' => $operacion,
                        'tabla'     => $tabla,
                        'registros' => $registros,
                    ]))
                );
                $callApi['IDS'] = $this->idsProcess;
                if ($callApi['STATUS'] != 'OK') {
                    throw new Exception(json_encode($callApi));
                }
                if (!empty($cargado)) {
                    $this->updateChargedField($tabla, $idField, $this->idsProcess);
                }

                return $callApi;
            } catch (Exception $e) {
                return $this->add_cola($operacion, $tabla, $where, $registros, $e->getMessage());
            }
        } else {
            return $this->add_cola($operacion, $tabla, $where, $registros, 'Hay registros en cola.');
        }
    }

    /**
     * Inserta un registro en la cola de envió
     * 
     * @param $operacion
     * @param $tabla
     * @param $where
     * @param $registros
     * @param $excepcion
     * @return integer
     */
    protected function add_cola($operacion, $tabla, $where, $registros, $excepcion)
    {
        $this->genera_log = false;
        $excepcion  = stripslashes(stripslashes($excepcion));
        $registros  = addslashes(json_encode($registros));
        $error      = addslashes($excepcion);
        $where      = addslashes($where);

        $query = "INSERT INTO 
            {$this->tablaCola} (
                operacion, 
                tabla, 
                `condicion`, 
                registros, 
                errores, 
                fecha_hora
            ) VALUES (
                '{$operacion}',
                '{$tabla}', 
                '{$where}', 
                '{$registros}', 
                '{$error}',
                now()
            )";
        $this->_SQL_tool($this->INSERT, __METHOD__, $query);

        $this->genera_log = true;
        return [
            "STATUS"    => "ERROR",
            "RESPONSE"  => "ERROR",
            "ERROR"     => json_decode($excepcion, true),
        ];
    }

    /**
     * Elimina un registro de la cola de envió
     * 
     * @param $id
     */
    public function delete_cola($id)
    {
        $this->genera_log = false;
        $this->_SQL_tool($this->DELETE, __METHOD__, "DELETE FROM {$this->tablaCola} WHERE id = '{$id}'");
        $this->genera_log = true;
    }

    /**
     * Actualiza cola
     * @param $id
     * @param $error
     * @return void
     */
    public function update_cola($id, $error)
    {
        $this->genera_log = false;
        $error = addslashes($error);
        $this->_SQL_tool($this->UPDATE, __METHOD__, "UPDATE {$this->tablaCola} SET errores = '{$error}' WHERE id = '{$id}'");
        $this->genera_log = true;
    }

    /**
     * @param $cola
     * @return int
     */
    public function procesarCola($cola)
    {
        $cola['registros'] = json_decode($cola['registros'], true);

        try {
            $callApi = $this->callApi(
                $this->url . '/SetDataArray',
                base64_encode(json_encode([
                    'operacion' => $cola['operacion'],
                    'tabla' => $cola['tabla'],
                    'registros' => $cola['registros']
                ]))
            );
            if ($callApi['STATUS'] != 'OK') {
                throw new Exception(json_encode($callApi));
            }
            $this->delete_cola($cola['id']);
            return 0;
        } catch (Exception $e) {
            $error = addslashes($e->getMessage());

            $this->update_cola($cola['id'], $error);
            return 1;
        }
    }

    /**
     * Verifica si existen registros en la cola
     * 0=sin registros, 1=con registros
     * 
     * @return int
     */
    public function comprobarCola()
    {
        static $ultimoIntento;

        //verificamos cuando fue el ultimo intento de sincronización
        if (empty($ultimoIntento)) {
            $query = "SELECT timestampdiff(MINUTE, max(fecha_hora),NOW()) as minutes FROM {$this->tablaCola}";
            $ultimoIntento = $this->_SQL_tool($this->SELECT, __METHOD__, $query)[0]['minutes'];
        }

        //si el ultimo intento fue mas de 5 minutos, deja continuar el proceso
        if (is_numeric($ultimoIntento) && $ultimoIntento <= 5) {
            return 1;
        }

        //Obtenemos los registros de la cola y limitamos la cantidad de registros que se enviaran por ciclo
        $maxReg = 50;
        $arrCola = $this->_SQL_tool(
            $this->SELECT,
            __METHOD__,
            "SELECT * FROM {$this->tablaCola} ORDER BY `id` LIMIT {$maxReg}"
        );

        if ($arrCola) {
            $total = ($this->total_verdadero - $maxReg);

            foreach ($arrCola as $cola) {
                //si la respuesta es 1 significa que existen registros en la cola y abortamos el proceso
                if ($this->procesarCola($cola) === 1) {
                    return 1;
                }
            }
            return ($total > 0) ? $this->comprobarCola() : 0;
        }

        return 0;
    }

    /**
     * Sincroniza un plan y toda la info asociada
     * 
     * @param $idPlan
     * @param $tablas
     * @return void
     */
    public static function sendPlans($idPlan, $tablas = ['plans', 'plan_category', 'plan_times','plan_band_age', 'raiders', 'plans_wording', 'benefit'], $prefix = '',$id='')
    {
        $Api = (new static());
        $return = [];
        self::$TIMEOUT = 50;
        if(!empty($idPlan)){
            if (empty($prefix)) {
                $idBroker   = $Api->_SQL_tool('SELECT_SINGLE', __METHOD__, "SELECT id_broker FROM restriction WHERE id_plans='{$idPlan}'")['id_broker'];
                $prefix     = $Api->getPrefixToBroker($idBroker);
            }
            $prefix=$prefix?:$Api->getPrefixToParameters();
            if (!empty($prefix)) {
                if (in_array('plans', $tablas)) {
                    $return['plans'] = $Api->sendApi('plans', 'UPDATE', "id='{$idPlan}'", $prefix);
                    $return['plan_detail'] = $Api->sendApi('plan_detail', 'UPDATE', "plan_id='{$idPlan}'", $prefix);
                }
                if (in_array('plan_category', $tablas)) {
                    $return['plan_category'] = $Api->sendApi('plan_category', 'UPDATE', "EXISTS(SELECT 1 FROM plans WHERE id='{$idPlan}' AND plan_category.id_plan_categoria=plans.id_plan_categoria)", $prefix);
                    if ($Api->idsProcess) {
                        $idCat = implode("','", $Api->idsProcess);
                        $return['plan_categoria_detail'] = $Api->sendApi('plan_categoria_detail', 'UPDATE', "plan_categoria_detail.id_plan_categoria in('{$idCat}')", $prefix);
                    }
                }
                if (in_array('plan_times', $tablas)) {
                    $return['plan_times'] = $Api->sendApi('plan_times', 'UPDATE', "id_plan='{$idPlan}'", $prefix);
                }
                if (in_array('plan_band_age', $tablas)) {
                    $return['plan_band_age'] = $Api->sendApi('plan_band_age', 'UPDATE', "id_plan='{$idPlan}'", $prefix);
                }
                if (in_array('benefit', $tablas)) {
                    $return['benefit_plan'] = $Api->sendApi('benefit_plan', 'UPDATE', "id_plan = '{$idPlan}'", $prefix);
                    $return['benefit'] = $Api->sendApi('benefit', 'UPDATE', "EXISTS(SELECT 1 FROM benefit_plan WHERE benefit_plan.id_plan = '{$idPlan}' AND benefit.id = benefit_plan.id_beneficio)", $prefix);
                    if ($Api->idsProcess) {
                        $idBen = implode("','", $Api->idsProcess);
                        $return['benefit_detail'] = $Api->sendApi('benefit_detail', 'UPDATE', "id_benefit in('{$idBen}')", $prefix);
                        $return['benefit_type'] = $Api->sendApi('benefit_type', 'UPDATE', "EXISTS(SELECT 1 FROM benefit WHERE benefit.id in('{$idBen}') AND benefit.Type_benefit = benefit_type.id)", $prefix);
                    }
                }
                if (in_array('plans_wording', $tablas)) {
                    $return['plans_wording_type'] = $Api->sendApi('plans_wording_type', 'UPDATE', "1", $prefix);
                    $return['plans_wording'] = $Api->sendApi('plans_wording', 'UPDATE', "id_plan='{$idPlan}'", $prefix);
                }
                if (in_array('restriction', $tablas)) {
                    $return['restriction'] = $Api->sendApi('restriction', 'UPDATE', "id_plans='{$idPlan}'", $prefix);
                    if ($Api->idsProcess) {
                        $idRest = implode("','", $Api->idsProcess);
                        $return['relaciotn_restriction'] = $Api->sendApi('relaciotn_restriction', 'UPDATE', "id_restric in('{$idRest}')", $prefix);
                    }
                }
                if (in_array('raiders', $tablas)) {
                    $return['plan_raider'] = $Api->sendApi('plan_raider', 'UPDATE', "id_plan ='{$idPlan}'", $prefix);
                    $return['raiders'] = $Api->sendApi('raiders', 'UPDATE', "EXISTS(SELECT 1 FROM plan_raider WHERE plan_raider.id_plan ='{$idPlan}' AND plan_raider.id_raider = raiders.id_raider)", $prefix);
                    if ($Api->idsProcess) {
                        $idRaider = implode("','", $Api->idsProcess);
                        $return['raiders_detail'] = $Api->sendApi('raiders_detail', 'UPDATE', "id_raider in('{$idRaider}')", $prefix);
                    }
                }
            }
        }else{
            if (in_array('benefit', $tablas)) {
                $return['benefit'] = $Api->sendApi('benefit', 'UPDATE', " benefit.id ={$id} ", $prefix);
                if ($Api->idsProcess) {
                    $idBen = implode("','", $Api->idsProcess);
                    $return['benefit_detail'] = $Api->sendApi('benefit_detail', 'UPDATE', "id_benefit in('{$idBen}')", $prefix);
                    $return['benefit_type'] = $Api->sendApi('benefit_type', 'UPDATE', "EXISTS(SELECT 1 FROM benefit WHERE benefit.id in('{$idBen}') AND benefit.Type_benefit = benefit_type.id)", $prefix);
                }
            } 
        }
        return $return;
    }
}