<?php

class MercadolivreUtils extends WebServiceMktp
{
    const unknownError = 'Erro não identificado';

    private $accessToken = null;
    private $refreshToken;
    private $tokenExpire;
    private $pathEtiqueta;
    private $httpCodeError = array(400, 401, 403, 404, 405, 406, 407, 409, 410, 429, 500, 501, 502, 504);

    public function __construct($registry)
    {
        parent::__construct($registry);
        if ($this->config->get('mercadolivre_access_token') && $this->config->get('mercadolivre_token_expire')) {
            $this->accessToken = trim($this->config->get('mercadolivre_access_token'));
            $this->refreshToken = trim($this->config->get('mercadolivre_refresh_token'));
            $this->tokenExpire = trim($this->config->get('mercadolivre_token_expire'));
        }

        if ($this->config->get('mercadolivre_app_id')) {
            $this->appId = trim($this->config->get('mercadolivre_app_id'));
        }

        if ($this->config->get('mercadolivre_app_secret')) {
            $this->appSecret = trim($this->config->get('mercadolivre_app_secret'));
        }

        $this->pathEtiqueta = DIR_CACHE . 'cachefiles/';

        $this->API_DOMAIN = 'https://api.mercadolibre.com';
    }

    public function callAPI($url, $method = 'GET', $dataSend = false, $header = array(), $file = false, &$httpcode = false, $dataType = Type_DadosSend::TpDados_Identificar, $returnFile = false)
    {
        $httpcode = 0;

        $data = parent::callAPI($url, $method, $dataSend, $header, $file, $httpcode, $dataType, $returnFile);

        if($httpcode == 409 AND ((isset($data->message)) AND strpos($data->message, 'optimistic locking') > 0)){
            sleep(1);
            $data = parent::callAPI($url, $method, $dataSend, $header, $file, $httpcode, $dataType, $returnFile);
        }

        if ($httpcode == 0) {
            $this->setErrorML($this->error, $url, $method, $dataSend, $data);
            $this->error = ' Connection Error';
        } elseif (in_array($httpcode, $this->httpCodeError)) {
            $this->error = '';
            if (is_object($data) && property_exists($data, 'cause') && (!is_null($data->cause)) && (is_array($data->cause))) {
                foreach ($data->cause as $caos) {
                    if (is_object($caos) && (property_exists($caos, 'code')) && (property_exists($caos, 'type')) && ($caos->type == 'error')) {

                        if (property_exists($caos, 'code')) {
                            $this->error = '(' . $caos->code . '):' . $caos->message;
                        } elseif (property_exists($caos, 'message')) {
                            $this->error = $data->message;
                        } else {
                            if (strpos($url, 'items/validate') !== false) {
                                $this->error .= ' - Validation failed.';
                            }
                        }
                    }
                }

                if ((empty($this->error)) && (count($data->cause) > 0)) {

                    $errorData = current($data->cause);

                    if (is_array($errorData)) {
                        $errorData = (object)$errorData;
                    }

                    if ($errorData instanceof StdClass) {
                        $code = $errorData->code ?? '';
                        if (!empty($code)) {
                            $this->error = '(' . $code . '): ';
                        }

                        if (isset($errorData->error)) {
                            $this->error .= $errorData->error;
                        } else if (isset($errorData->message)) {
                            $this->error .= $errorData->message;
                        }

                        if (empty($this->error)) {
                            $this->error = self::unknownError;
                        }
                    } else {
                        $this->error = $errorData;
                    }

                } else if (is_object($data) && (property_exists($data, 'error')) && ($this->error == '')) {
                    $msg_error = $data->error;
                    if (is_object($data) && property_exists($data, 'message')) {
                        $msg_error .= ' - ' . $data->message;
                    }
                    $this->error = $msg_error;
                }

            } else if (is_object($data) && (property_exists($data, 'error'))) {
                $msg_error = $data->error;
                if (is_object($data) && property_exists($data, 'message')) {
                    $msg_error .= ' - ' . $data->message;
                }
                $this->error = $msg_error;
            } else {
                $this->error = self::unknownError;
            }

            // Tratar os códigos abaixo como erro pra salvar no log de erro, pra gente ver como vem o JSON e fazer uma validação melhor depois
            if (empty($this->error) || $this->error == self::unknownError) {
                if ($httpcode == 429) {
                    $this->error = 'Too many requests';
                } else if ($httpcode == 504) {
                    $this->error = 'Gateway timeout';
                }
            }
            if (!empty($this->error)) {
                $this->setErrorML($this->error, $url, $method, $dataSend, $data);
            }


            //Caso não apresentou erro, pode ser Warning por exemplo, então retornamos true, foi com sucesso...
            return empty($this->error);

        } else {
            return $data;
        }
    }

    public function setErrorML($error, $url = '', $method = '', $data = '', $body = '')
    {
        if ($this->config->get('mercadolivre_debug')) {
            $error_msg = 'Error =>' . $error . PHP_EOL;
            $error_msg .= (!empty($url)) ? 'Url => ' . $url . PHP_EOL : '';
            $error_msg .= (!empty($method)) ? 'Metodo => ' . $method . PHP_EOL : '';
            $error_msg .= (!empty($data)) ? 'Data => ' . print_r($data, true) . PHP_EOL : '';
            $error_msg .= (!empty($body)) ? 'Body => ' . print_r($body, true) . PHP_EOL : '';
            $error_msg .= '-----------------------------------------------------------------------------------------------';
            $this->logml->write($error_msg);
        }
    }

    public function meli_formataphone($phone)
    {
        $phone = ($phone) ? substr(preg_replace("/[^0-9]/", "", $phone), 0, 15) : '';
        $phone_lenght = ($phone) ? strlen($phone) : 0;

        if ($phone_lenght > 0) {
            for ($cont = 0; $cont < $phone_lenght; $cont++) {
                $phone = (substr($phone, 0, 1) == '0') ? substr(preg_replace("/[^0-9]/", "", $phone), 1, 15) : $phone;
            };

            $phone = (substr($phone, 0, 4) == '5555') ? substr($phone, 2, 15) : $phone;
            $phone_lenght = ($phone) ? strlen($phone) : 0;

            if ($phone_lenght > 10) {
                $phone = substr(preg_replace("/[^0-9]/", "", $phone), 0, 10);
            }

            return $phone;
        }

        return '';
    }

    public function convertPicturesObject($picturesobj)
    {
        $pictures = $picturesobj->getImagens();
        $retorno = array();

        if (count($pictures) <= 0) {
            return $retorno;
        };

        foreach ($pictures as $img) {
            $retorno[] = $img->getLink();
        }
        return $retorno;

    }

    public function getAppUser()
    {
        $this->getAccess();

        $header = array(
            'Authorization: Bearer ' . $this->accessToken
        );

        $url = $this->API_DOMAIN . '/users/me';
        $app_user = $this->callAPI($url, 'GET', array(), $header);

        if ($this->error) {
            return false;
        } else {
            return $app_user->id;
        }
    }

    public function getValidateAccount()
    {
        $this->getAccess();

        $header = array(
            'Authorization: Bearer ' . $this->accessToken
        );

        $url = $this->API_DOMAIN . '/users/' . $this->getAppUser() . '?attributes=status';

        $user = $this->callAPI($url, 'GET', array(), $header);

        if ($this->error) {
            return false;
        } else {
            return $user->status->list;
        }
    }

    public function getAccess()
    {
        if ($this->accessToken && $this->refreshToken) {
            if ($this->tokenExpire < time() + 1) {
                $this->refreshToken();
            }
            return true;
        } else {
            return false;
        }
    }

    public function setAccess($accessToken, $refreshToken, $tokenExpire)
    {
        $this->load->type('tipocomum');
        $this->load->model('setting/setting');

        $data = array(
            'mercadolivre_access_token' => $accessToken,
            'mercadolivre_refresh_token' => $refreshToken,
            'mercadolivre_token_expire' => time() + $tokenExpire,
            'mercadolivre_new_customer' => Type_Status::Tp_Habilitado
        );

        $this->model_setting_setting->editSettingForKey('mercadolivre', $data);

        $this->accessToken = $accessToken;
        $this->refreshToken = $refreshToken;
        $this->tokenExpire = $tokenExpire;
    }

    public function getAccessToken($code)
    {
        $url = $this->API_DOMAIN . '/oauth/token';

        $param = array(
            "client_id" => $this->appId,
            "client_secret" => $this->appSecret,
            "redirect_uri" => $this->config->get('config_ssl') . 'mercadolivreauth',
            "code" => $code,
            "grant_type" => "authorization_code"
        );

        $data = $this->callAPI($url, 'POST', $param);
        if ($this->error) {
            return '<strong>Error:</strong> Failed to get access code. Please check your application setting. <br> <strong>Message from Mercadolivre:</strong>' . $this->error;
        } else {
            if (is_object($data) && property_exists($data, 'access_token')) {
                $accessToken = $data->access_token;
                $tokenExpire = $data->expires_in;
                $refreshToken = $data->refresh_token;
                $this->setAccess($accessToken, $refreshToken, $tokenExpire);
            }
            return '';
        }
    }

    public function refreshToken()
    {

        $url = $this->API_DOMAIN . '/oauth/token';
        $param = array(
            "client_id" => $this->appId,
            "client_secret" => $this->appSecret,
            "refresh_token" => $this->refreshToken,
            "grant_type" => "refresh_token"
        );

        $data = $this->callAPI($url, 'POST', $param);

        if (is_object($data) && property_exists($data, 'access_token')) {
            $accessToken = $data->access_token;
            $tokenExpire = $data->expires_in;
            $refreshToken = $data->refresh_token;
            $this->setAccess($accessToken, $refreshToken, $tokenExpire);
        }

    }

    public function getLoginUrl()
    {
        $url = 'https://auth.mercadolivre.com.br/authorization?client_id=' . $this->appId . '&response_type=code&redirect_url=' . $this->config->get('config_ssl') . 'mercadolivreauth';
        return $url;
    }

    public function getLogoutUrl()
    {
    }

    public function createTestUser()
    {
        $this->getAccess();

        $user = array();
        $url = $this->API_DOMAIN . '/applications/' . $this->appId;
        $param = array();
        $data = $this->callAPI($url, 'GET', $param);
        if (isset($data) && ($data)) {
            $site_id = $data->site_id;

            $url = $this->API_DOMAIN . '/users/test_user';
            $param = array();
            $param['site_id'] = $site_id;
            $param = json_encode($param);

            $header = array(
                'Content-Type: application/json',
                'Content-Length: ' . strlen($param),
                'Authorization: Bearer ' . $this->accessToken
            );

            $data = $this->callAPI($url, 'POST', $param, $header);

            if (isset($data) && ($data)) {
                $user['user'] = 'Usuário: ' . $data->nickname . '  |  Senha: ' . $data->password;
            } else {
                $user['error'] = 'Não foi possível gerar usuário de teste. Verifique suas configurações.';
            }
        } else {
            $user['error'] = 'Não foi possível gerar usuário de teste. Verifique suas configurações.';
        }

        return $user;
    }

    public function addProduct($param)
    {
        // Validate removido depois de termos problemas com validação de atributos sem o menor sentido,
        // além do endpoint não retornar corretamente código de http e o retorno não ter padrão -- by Rafbor
//    if ($this->validateItem($param)) {
        $this->getAccess();
        $url = $this->API_DOMAIN . '/items';
        $param = json_encode($param);
        $header = array(
            'Content-Type: application/json',
            'Content-Length: ' . strlen($param),
            'Authorization: Bearer ' . $this->accessToken
        );

        $data = $this->callAPI($url, 'POST', $param, $header);
        return $data;
    }


    public function updateProduct($param, $itemId)
    {
        $this->getAccess();
        $url = $this->API_DOMAIN . '/items/' . $itemId;
        $param = json_encode($param);
        $header = array(
            'Content-Type: application/json',
            'Content-Length: ' . strlen($param),
            'Authorization: Bearer ' . $this->accessToken
        );
        $data = $this->callAPI($url, 'PUT', $param, $header);
        return $data;
    }

    public function updateProductDescription($param, $itemId)
    {
        $this->getAccess();
        $url = $this->API_DOMAIN . '/items/' . $itemId . '/description?api_version=2';
        $param = json_encode($param);
        $header = array(
            'Content-Type: application/json',
            'Content-Length: ' . strlen($param),
            'Authorization: Bearer ' . $this->accessToken
        );
        $data = $this->callAPI($url, 'PUT', $param, $header);
        return $data;
    }

    public function isShippingAllowed($category_id)
    {
        $this->getAccess();
        $user_id = $this->getAppUser();
        $param = array();
        $mode = array();
        $url = $this->API_DOMAIN . '/users/' . $user_id . '/shipping_modes?category_id=' . $category_id;
        $category_shipping = $this->callAPI($url, 'GET', $param);

        if ($this->error) {
            return true;
        }

        if (is_array($category_shipping)) {
            foreach ($category_shipping as $avail_method) {
                $mode[] = $avail_method->mode;
            }

            if ((in_array('me1', $mode)) || (in_array('me2', $mode))) {
                return false;
            }
        }

        return true;
    }

    public function validateItem($param)
    {
        $this->getAccess();
        $url = $this->API_DOMAIN . '/items/validate';
        $param = json_encode($param);
        $header = array(
            'Content-Type: application/json',
            'Content-Length: ' . strlen($param),
            'Authorization: Bearer ' . $this->accessToken
        );

        $this->callAPI($url, 'POST', $param, $header);

        if ($this->error == '') return true;
        else return false;
    }

    public function ml_getproduct($itemId)
    {
        $this->getAccess();
        $param = array();
        $header = array(
            'Authorization: Bearer ' . $this->accessToken
        );
        $url = $this->API_DOMAIN . '/items/' . $itemId;
        $product_data = $this->callAPI($url, 'GET', $param, $header);

        return $product_data;
    }

    public function getProductVariation($itemId, $variationId)
    {
        $this->getAccess();
        $param = array();
        $header = array(
            'Authorization: Bearer ' . $this->accessToken
        );
        $url = $this->API_DOMAIN . '/items/' . $itemId . '/variations/' . $variationId;
        $product_data = $this->callAPI($url, 'GET', $param, $header);

        return $product_data;
    }

    public function updateOpencartProduct($itemId, $dataConsulta = null)
    {
        $this->getAccess();

        if (!$dataConsulta) {
            $param = array();
            $header = array(
                'Authorization: Bearer ' . $this->accessToken
            );
            $url = $this->API_DOMAIN . '/items/' . $itemId;
            $product_data = $this->callAPI($url, 'GET', $param, $header);
        } else {
            $product_data = $dataConsulta;
        }
        return $product_data;
    }

    public function getShipments($shipmentsId)
    {
        $this->getAccess();
        $param = array();
        $url = $this->API_DOMAIN . '/shipments/' . $shipmentsId;

        $header = array(
            'x-format-new: true',
            'Authorization: Bearer ' . $this->accessToken
        );

        $shipping_data = $this->callAPI($url, 'GET', $param, $header);

        return $shipping_data;
    }

    public function getShipmentsCosts($shipmentsId)
    {
        $this->getAccess();
        $param = array();
        $url = $this->API_DOMAIN . '/shipments/' . $shipmentsId . '/costs';

        $header = array(
            'X-Costos-New: true',
            'Authorization: Bearer ' . $this->accessToken
        );

        $shipping_data = $this->callAPI($url, 'GET', $param, $header);

        return $shipping_data;
    }

    public function getShipmentsItems($shipmentsId)
    {
        $this->getAccess();
        $param = array();
        $url = $this->API_DOMAIN . '/shipments/' . $shipmentsId . '/items';
        $header = array(
            'x-format-new: true',
            'Authorization: Bearer ' . $this->accessToken
        );
        $shipping_data = $this->callAPI($url, 'GET', $param, $header);

        return $shipping_data;
    }

    public function getmeliOrder($mercaOrderId)
    {
        $this->getAccess();

        $header = array(
            'x-format-new: true',
            'Authorization: Bearer ' . $this->accessToken
        );

        $param = array();
        $url = $this->API_DOMAIN . '/orders/' . $mercaOrderId;
        $metodo = 'GET';
        $order_data = $this->callAPI($url, $metodo, $param, $header);

        if (!$order_data) {
            throw new exception($this->error);
        } else {
            $this->marketplace->logArray[] = array('tipoEnviado' => Type_LogEnviado::Tp_LogRecebido, 'tipoIntegracao' => Type_MarketPlace::Tp_Mercadolivre,
                'origem' => __METHOD__, 'tipoLog' => Type_Log::Tp_LogPedido, 'tipoId' => Type_LogId::Tp_LogIdMktPedido, 'entidadeId' => $order_data->id ?? '',
                'url' => $url, 'metodo' => $metodo, 'httpCode' => $this->httpcode, 'retorno' => $order_data, 'dataHora' => $this->db->dataAtual());
        }

        return $order_data;
    }

    public function getmeliOrderBillingInfo($mercaOrderId)
    {
        $this->getAccess();

        $header = array(
            'x-format-new: true',
            'Authorization: Bearer ' . $this->accessToken
        );

        $param = array();
        $url = $this->API_DOMAIN . '/orders/' . $mercaOrderId . '/billing_info';
        $metodo = 'GET';
        $billing_data = $this->callAPI($url, $metodo, $param, $header, false, $httpcode);


        if (!$billing_data) {
            throw new exception($this->error);
        } else {
            $this->marketplace->logArray[] = array('tipoEnviado' => Type_LogEnviado::Tp_LogRecebido, 'tipoIntegracao' => Type_MarketPlace::Tp_Mercadolivre,
                'origem' => __METHOD__, 'tipoLog' => Type_Log::Tp_LogPedido, 'tipoId' => Type_LogId::Tp_LogIdMktPedido, 'entidadeId' => $mercaOrderId,
                'url' => $url, 'metodo' => $metodo, 'httpCode' => $this->httpcode, 'retorno' => $billing_data, 'dataHora' => $this->db->dataAtual());
        }

        return $billing_data;
    }

    function updateListingType()
    {
        $this->getAccess();
        $url = $this->API_DOMAIN . '/applications/' . $this->appId;
        $param = array();
        $data = $this->callAPI($url, 'GET', $param);
        $site_id = $data->site_id;

        //getting categories
        $url = $this->API_DOMAIN . '/sites/' . $site_id . '/listing_types';
        $param = array();
        $data = $this->callAPI($url, 'GET', $param);

        if (empty($data)) $data = array();

        $this->db->query("delete from " . DB_PREFIX . "mercadolivre_listing_type where 1");
        foreach ($data as $single) {

            if (!empty($single->name)) {
                $this->db->query("INSERT INTO " . DB_PREFIX . "mercadolivre_listing_type SET code = '" . $this->db->escape($single->id) . "', `name` = '" . $this->db->escape($single->name) . "'");
            }

        }
    }

    function updateCategory()
    {
        $this->getAccess();
        $url = $this->API_DOMAIN . '/applications/' . $this->appId;
        $param = array();
        $data = $this->callAPI($url, 'GET', $param);
        $site_id = $data->site_id;

        //getting categories
        $url = $this->API_DOMAIN . '/sites/' . $site_id . '/categories';
        $param = array();
        $data = $this->callAPI($url, 'GET', $param);

        if (empty($data)) $data = array();

        $this->db->query("delete from " . DB_PREFIX . "mercadolivre_category where 1");
        foreach ($data as $single) {

            if (!empty($single->name)) {
                $this->db->query("INSERT INTO " . DB_PREFIX . "mercadolivre_category SET mercaId = '" . $this->db->escape($single->id) . "', `name` = '" . $this->db->escape($single->name) . "'");
            }

        }
    }

    public function getCategory($catId)
    {
        $cat_data = $this->cache->get('get_category.' . $catId);

        $this->getAccess();
        $param = array();
        $return = array();

        if (!$cat_data) {
            $url = $this->API_DOMAIN . '/categories/' . $catId;
            $cat_data = $this->callAPI($url, 'GET', $param);
            $this->cache->set('get_category.' . $catId, $cat_data, 2592000 /* valor em segundos(30 dias) */);
        }

        foreach ($cat_data->children_categories as $cat) {
            $return[] = array('id' => $cat->id, 'name' => $cat->name);
        }

        $attribute_types = ($cat_data->attribute_types == 'variations') ? 1 : 0;
        $allowed = ($cat_data->settings->listing_allowed) ? 1 : 0;

        /*find listing type of the selected category*/
        $this->load->language('module/mercadolivre-extra');
        $ml_text_select = $this->language->get('ml_text_select');

        $url = $this->API_DOMAIN . '/categories/' . $catId . '/listing_types';
        $listing_type_data = $this->callAPI($url, 'GET', $param);

        $current_listtype = isset($this->request->get['listingType']) ? $this->request->get['listingType'] : '';

        $listing_type_html = '';
        if ($listing_type_data) {
            $listing_type_html .= '<option value="">' . $ml_text_select . '</option>';
            foreach ($listing_type_data as $listing_type) {
                $selected = ($current_listtype == $listing_type->id) ? 'selected' : '';
                $listing_type_html .= '<option ' . $selected . ' value="' . $listing_type->id . '">' . $listing_type->name . '</option>';
            }
            $listing_type_html .= '</select>';
        }
        return array('name' => $cat_data->name, 'categories' => $return, 'attribute_types' => $attribute_types, 'allowed' => $allowed, 'listing_type' => $listing_type_html);
    }

    public function getCategoryDimensions($catId)
    {
        $this->load->language('module/mercadolivre-extra');
        $dimensionList = $this->cache->get('categories_shipping_preferences.' . $catId);
        if (!$dimensionList) {
            $dimensionList = array();
            $param = array();
            $url = $this->API_DOMAIN . '/categories/' . $catId . '/shipping_preferences';
            $shipping_preferences_data = $this->callAPI($url, 'GET', $param);

            if ($shipping_preferences_data) {
                if (isset($shipping_preferences_data->dimensions)) {
                    $arrayMedidas = array('height', 'width', 'length');
                    foreach ($shipping_preferences_data->dimensions as $key => $dimension) {
                        if (in_array($key, $arrayMedidas)) {
                            $dimensionList[$key] = sprintf($this->language->get('text_' . $key), $dimension);
                        } else {
                            $dimensionList[$key] = $this->language->get('text_' . $key) . $dimension . ' g';
                        }
                    }
                }
            }
            $this->cache->set('categories_shipping_preferences.' . $catId, $dimensionList, 2592000 /* valor em segundos(30 dias) */);
        }
        return $dimensionList;
    }

    public function getCategoryRecursiva($catid)
    {

        $this->getAccess();
        $url = $this->API_DOMAIN . '/categories/' . $catid;
        $param = array();
        $cat_data = $this->callAPI($url, 'GET', $param);

        $retorno = '';

        if ($cat_data) {
            foreach ($cat_data->path_from_root as $key => $path) {
                $retorno .= $path->name . ' » ';
            }
        }
        return substr($retorno, 0, -3);

    }

    public function getCategorySetting($catid)
    {

        $this->getAccess();
        $url = $this->API_DOMAIN . '/categories/' . $catid;
        $param = array();
        $catdata = $this->callAPI($url, 'GET', $param);

        if ($catdata) {
            return (property_exists($catdata, 'settings')) ? $catdata->settings : false;
        } else {
            return $this->TranslateError($this->error);
        }
    }

    public function getShippingPreference($catid)
    {

        $this->getAccess();

        $seller_id = $this->getAppUser();

        $url = $this->API_DOMAIN . '/users/' . $seller_id . '/shipping_preferences?category_id=' . $catid;
        $param = array();
        $shipping = $this->callAPI($url, 'GET', $param);

        if ($shipping) {
            return (property_exists($shipping, 'modes')) ? $shipping->modes : false;
        } else {
            return $this->TranslateError($this->error);
        }
    }

    /*
      Função responsável por consultar e retornar os atributos dinamicos por categorias
      não variáveis.
    */
    public function getAttributesNotVariables($catId, $relevance = false)
    {
        $return = array();
        $attr_data = $this->cache->get('get_attributes_not_variables.' . $catId);

        if (!$attr_data) {
            $this->getAccess();
            $url = $this->API_DOMAIN . '/categories/' . $catId . '/attributes';
            $param = array();
            $attr_data = $this->callAPI($url, 'GET', $param);
            $return = array();
            if ($attr_data) {
                $this->cache->set('get_attributes_not_variables.' . $catId, $attr_data, 2592000 /* valor em segundos(30 dias) */);
            }

        }

        if ($attr_data) {
            foreach ($attr_data as $value) {
                $attributes = $this->parseAttributesNotVariables($value, false);
                if ($attributes) {
                    $return[] = $attributes;
                }
            }
        }


        return $return;
    }


    /*
      Monta a estrutura de atributos não váriaveis, separa o json em itens necessários
      para a geração dos atributos no sistema.
    */
    public function parseAttributesNotVariables($attr, $relevance)
    {
        if (!is_object($attr)) return false;

        /*$variations = false;
        if((property_exists($attr->tags,'allow_variations')) && (!property_exists($attr->tags,'read_only'))){
            $variations = true;
        }

        if((property_exists($attr->tags,'variation_attribute')) && (!property_exists($attr->tags,'read_only'))){
            $variations = true;
        }*/

        if (property_exists($attr->tags, 'allow_variations')) return false;
        if (property_exists($attr->tags, 'variation_attribute')) return false;

        if ($relevance) {
            if ($attr->relevance != $relevance) {
                return false;
            }
        }

        $retorno = array();
        $retorno['id'] = $attr->id;
        $retorno['name'] = $attr->name;
        $retorno['relevance'] = $attr->relevance;
        $retorno['value_type'] = $attr->value_type;
        $retorno['value_max_length'] = (isset($attr->value_max_length) ? $attr->value_max_length : 0);
        $retorno['required'] = (property_exists($attr->tags, 'required') ? true : false);
        $retorno['read_only'] = (property_exists($attr->tags, 'read_only') ? true : false);
        $retorno['type'] = (isset($attr->type) ? $attr->type : false);
        $retorno['fixed'] = (property_exists($attr->tags, 'fixed') ? true : false);
        $retorno['hidden'] = (property_exists($attr->tags, 'hidden') ? true : false);
        $retorno['variable'] = false;
        if (($retorno['fixed']) && (isset($attr->values)) && (is_array($attr->values))) {
            $retorno['fixed_id'] = (isset($attr->values[0]->id) ? $attr->values[0]->id : '');
            $retorno['fixed_name'] = (isset($attr->values[0]->name) ? $attr->values[0]->name : '');
            if ($retorno['fixed_id'] == '') {
                $retorno['fixed_id'] = $retorno['fixed_name'];
            }
        } else {
            $retorno['fixed_id'] = '';
            $retorno['fixed_name'] = '';
        }
        $attr_units = array();
        if ((isset($attr->allowed_units)) && is_array($attr->allowed_units)) {
            foreach ($attr->allowed_units as $value) {
                $attrname = '';
                if (isset($value->name)) {
                    $attrname = $value->name;
                } elseif ($value->id) {
                    $attrname = $value->id;
                }
                if (!empty($attrname)) {
                    $attr_units[$attrname] = $attrname;
                }
            }
        }

        $attr_values = array();

        if ((isset($attr->values)) && is_array($attr->values)) {
            foreach ($attr->values as $value) {
                $valueOpt = '';
                $valueOpt_id = '';

                if (isset($value->name)) {
                    $valueOpt = $value->name;
                }
                if (isset($value->id)) {
                    $valueOpt_id = $value->id;
                } else {
                    $valueOpt_id = $value->name;
                }

                $attr_values[$valueOpt_id] = $valueOpt;
            }
        }
        $retorno['options'] = $attr_values;
        $retorno['allowed_units'] = $attr_units;

        return $retorno;
    }

    public function getAttributes($catId)
    {
        $attr_data = false;//$this->cache->get('get_attributes.' . $catId);

        if (!$attr_data) {
            $this->getAccess();
            $url = $this->API_DOMAIN . '/categories/' . $catId . '/attributes';
            $param = array();
            $attr_data = $this->callAPI($url, 'GET', $param);
            $return = array();

            $this->cache->set('get_attributes.' . $catId, $attr_data, 2592000 /* valor em segundos(30 dias) */);
        }

        if (!is_array($attr_data)) $attr_data = array();

        foreach ($attr_data as $single) {
            $return[] = $this->parseAttr($single);
        }

        return $return;
    }

    private function parseAttr($attr)
    {

        if (!is_object($attr)) return false;
        if (!property_exists($attr->tags, 'allow_variations')) return false;

        $return = array();
        $return['id'] = $attr->id;
        $return['name'] = $attr->name;
        $return['type'] = $attr->value_type;
        $return['required'] = (property_exists($attr->tags, 'required') ? 1 : 0);

        $option_values = array();
        if (isset($attr->values) && is_array($attr->values)) {
            foreach ($attr->values as $value) {
                $option_values[$value->id] = $value->name;
            }
        }
        $return['options'] = $option_values;

        return $return;
    }

    public function getMercadolivreIDs($productId, $option = '')
    {
        $sql = "SELECT * FROM `oc_marketplace_product` WHERE `product_id` = '" . (int)$productId . "'";
        if ($option != '') $sql .= " AND `option` = '" . $option . "'";
        $result = $this->db->query($sql);
        return $result->row;
    }

    public function getMercadolivreStatus($productId)
    {
        $is_listed = $this->getMercadolivreIDs($productId);
        $this->load->language('module/mercadolivre-extra');
        if (count($is_listed) > 0) return $this->language->get('lang_listed');
        else return $this->language->get('lang_not_listed');
    }

    public function getMercadolivreData($productId, $type)
    {
        $listed_info = $this->getMercadolivreIDs($productId);
        if ($listed_info) {
            $status = (Type_MercadoLivreItemStatus::getNickFromValue($listed_info['status'])) ? Type_MercadoLivreItemStatus::getNickFromValue($listed_info['status']) : $listed_info['status'];
            if (isset($status)) {
                if (($type == 'status') && ($status != "")) return $status;
                if ($type == 'url') return $listed_info['url'];
            }
        } else return '';
    }

    public function delete($itemId)
    {
        $param = array();
        $param['status'] = "closed";
        return $this->updateProduct($param, $itemId);
    }

    public function paused($itemId, $qtdzero = false)
    {
        $param = array();
        $param['status'] = Type_MercadoLivreItemStatus::Tp_Paused;
        if ($qtdzero) {
            $param['available_quantity'] = 0;
        }
        return $this->updateProduct($param, $itemId);
    }

    public function active($itemId)
    {
        $this->load->type('tipomercadolivre');
        $param = array();
        $param['status'] = Type_MercadoLivreItemStatus::Tp_Active;
        return $this->updateProduct($param, $itemId);
    }


    public function searchProductMercadoLivre($data = array())
    {
        $this->getAccess();
        $seller_id = $this->getAppUser();

        $param = array();
        if (isset($data['status'])) {
            $param['status'] = $data['status'];
        }

        if (isset($data['offset'])) {
            $param['offset'] = $data['offset'];
        }

        if (isset($data['limit'])) {
            $param['limit'] = $data['limit'];
        }

        if (isset($data['filter_anuncio']) && ($data['filter_anuncio'])) {
            $param['q'] = $data['filter_anuncio'];
        }

        $url = $this->API_DOMAIN . '/users/' . $seller_id . '/items/search';

        $header = array(
            'Authorization: Bearer ' . $this->accessToken
        );

        $idProductsML = $this->callAPI($url, 'GET', $param, $header);

        if ($this->error) {
            return false;
        } else {
            return $idProductsML;
        }

    }

    public function infoProductMercadoLivre($mercaId)
    {

        $url = $this->API_DOMAIN . '/items?ids=' . $mercaId . '&attributes=id,title,permalink';

        $param = array();
        $infoProductsMl = $this->callAPI($url, 'GET', $param);

        if ($this->error) {
            return false;
        } else {

            usort(

                $infoProductsMl,

                function ($a, $b) {

                    if ($a->body->id == $b->body->id) return 0;

                    return (($a->body->id < $b->body->id) ? -1 : 1);
                }
            );

            return $infoProductsMl;
        }

    }

    public function getShipmentLabels($shipment_ids)
    {
        $this->getAccess();
        $param = array();
        $url = $this->API_DOMAIN . '/shipment_labels?shipment_ids=' . $shipment_ids . '&savePdf=Y';

        $header = array(
            'Authorization: Bearer ' . $this->accessToken,
            'Accept: application/pdf'
        );

        return $this->callAPI($url, 'GET', $param, $header, false, $httpcode, Type_DadosSend::TpDados_Identificar, true);
    }

    public function getEtiqueta($mercaId)
    {
        $this->getAccess();
        $param = array();

        $header = array(
            'Authorization: Bearer ' . $this->accessToken
        );

        $urlOrder = $this->API_DOMAIN . '/orders/' . $mercaId;
        $order_data = $this->callAPI($urlOrder, 'GET', $param, $header);
        if ($order_data && (property_exists($order_data->shipping, 'id')) && !is_null($order_data->shipping->id)) {
            $urlShipping = $this->API_DOMAIN . '/shipments/' . $order_data->shipping->id;
            $shipping_data = $this->callAPI($urlShipping, 'GET', $param, $header);
            if (property_exists($shipping_data, 'status')) {
                if (($shipping_data->status == 'ready_to_ship') /*&& (!in_array($shipping_data->substatus, ['stale', 'invoice_pending']))*/) {
                    //return $this->API_DOMAIN . '/shipment_labels?shipment_ids=' . $shipping_data->id . '&response_type=pdf';
                    $retorno = $this->getShipmentLabels($shipping_data->id);
                    if (($retorno) && ($this->httpcode == 200)) {
                        return $retorno;
                    } else {
                        return false;
                    }
                }
            }
        }
        return false;
    }

    public function splitEtiqueta($mktOrderId)
    {

        $urlEtiqueta = $this->getEtiqueta($mktOrderId);

        if (!$urlEtiqueta) {
            throw new exception('Etiqueta indisponível no mercado livre');
        }

        $header = array(
            'Authorization: Bearer ' . $this->accessToken
        );

        $dataEtiqueta = $this->callAPI($urlEtiqueta, 'GET', false, $header, false, $httpcode, Type_DadosSend::TpDados_Json, true);

        if ($dataEtiqueta) {

            $pathEtiqueta = $this->pathEtiqueta . $mktOrderId . '.pdf';
            $this->ftrgeralregistry->setEtiquetaPdf($pathEtiqueta, $dataEtiqueta);

            if (file_exists($pathEtiqueta)) {

                $this->load->dmodel('tool/etiqueta');
                $link = $this->dmodel_tool_etiqueta->split($pathEtiqueta);

                if (!$link) {
                    throw new exception("Link's das etiquetas indisponíveis");
                }

                return $link;

            } else {
                throw new exception('Erro ao salvar etiqueta no servidor');
            }

        } else {
            throw new exception('Erro ao baixar etiqueta do Mercado Livre');
        }

    }

    public function getShippingType($shipping, $localpickup)
    {
        $result = '';

        if ($shipping == 'not_specified') {
            $result = Type_MercadoLivreModoEnvio::Tp_NotSpecified;
        }

        if (($shipping == 'me1') && ($localpickup == false)) {
            $result = Type_MercadoLivreModoEnvio::Tp_MercadoEnvio10;
        }

        if (($shipping == 'me1') && ($localpickup == true)) {
            $result = Type_MercadoLivreModoEnvio::Tp_MercadoEnvio11;
        }

        if (($shipping == 'me2') && ($localpickup == false)) {
            $result = Type_MercadoLivreModoEnvio::Tp_MercadoEnvio20;
        }

        if (($shipping == 'me2') && ($localpickup == true)) {
            $result = Type_MercadoLivreModoEnvio::Tp_MercadoEnvio21;
        }

        return $result;
    }

    public function getSearchOrderMercadoLivre($data = array())
    {
        $this->getAccess();
        $seller_id = $this->getAppUser();
        $param = array();

        if (!empty($data['filter_date_start'])) {
            $param['order.date_created.from'] = $data['filter_date_start'] . 'T00:00:00.000-00:00';
        }

        if (!empty($data['filter_date_end'])) {
            $param['order.date_created.to'] = $data['filter_date_end'] . 'T23:59:59.000-00:00';
        }

        if (isset($data['filter_name']) && ($data['filter_name'])) {
            $param['q'] = $data['filter_name'];
        }

        if (isset($data['order.status'])) {
            $param['order.status'] = $data['order.status'];
        }

        if (isset($data['sort'])) {
            $param['sort'] = $data['sort'];
        }

        if (isset($data['offset'])) {
            $param['offset'] = $data['offset'];
        }

        if (isset($data['limit'])) {
            $param['limit'] = $data['limit'];
        }

        $param['seller'] = $seller_id;

        $header = array(
            'Authorization: Bearer ' . $this->accessToken
        );

        $url = $this->API_DOMAIN . '/orders/search';
        $searchordersML = $this->callAPI($url, 'GET', $param, $header);

        if ($this->error) {
            return array(
                'error' => $this->error
            );
        } else {
            return array(
                'results' => $searchordersML->results,
                'paging-total' => $searchordersML->paging->total
            );
        }
    }

    public function getShippingTypeUser()
    {
        $this->getAccess();
        $header = array(
            'Authorization: Bearer ' . $this->accessToken
        );

        $url = $this->API_DOMAIN . '/users/' . $this->getAppUser();
        $userShipping = $this->callAPI($url, 'GET', array(), $header);

        if ($this->error) {
            return false;
        } else {
            $shipping_modes = array();
            foreach ($userShipping->shipping_modes as $key => $shipping_mode) {
                $shipping_modes[] = $this->getShippingType($shipping_mode, false);
                if (in_array($shipping_mode, ['me2', 'me2'])) {
                    $shipping_modes[] = $this->getShippingType($shipping_mode, true);
                }
            }
            return $shipping_modes;
        }

    }

    public function getUserInfo($userid)
    {

        $this->getAccess();

        $header = array(
            'Authorization: Bearer ' . $this->accessToken
        );

        $url = $this->API_DOMAIN . '/users/' . $userid;
        $userinfo = $this->callAPI($url, 'GET', array(), $header);

        return $userinfo;

    }

    public function getQuestions($question_id)
    {

        $this->getAccess();

        $header = array(
            'Authorization: Bearer ' . $this->accessToken
        );

        $url = $this->API_DOMAIN . '/questions/' . $question_id;
        $question = $this->callAPI($url, 'GET', array(), $header);

        return $question;

    }

    public function deleteQuestion($question_id)
    {

        $this->getAccess();

        $param = array();

        $header = array(
            'Authorization: Bearer ' . $this->accessToken
        );

        $url = $this->API_DOMAIN . '/questions/' . $question_id;
        $question = $this->callAPI($url, 'DELETE', $param, $header);

        return $question;

    }

    public function answerQuestion($data = array())
    {

        $this->getAccess();

        $param = array(
            'question_id' => $data['question_id'],
            'text' => $data['resposta']
        );

        $header = array(
            'Authorization: Bearer ' . $this->accessToken
        );

        $url = $this->API_DOMAIN . '/answers';
        $answer = $this->callAPI($url, 'POST', json_encode($param), $header);

        return $answer;
    }

    public function setNfe($shipmentsId, $arquivoxml, $order_id)
    {
        $this->getAccess();

        /*validação feita por que o mercado livre em ambiente de teste esta validando se a nota esta em homologação.
        */
        if (defined('AMBIENTE_TESTE')) {
            $arquivoxml = str_replace('<tpAmb>2', '<tpAmb>1', $arquivoxml);
        }

        $url = $this->API_DOMAIN . '/shipments/' . $shipmentsId . '/invoice_data?siteId=MLB';
        $header = array(
            'Content-Type: application/xml',
            'Authorization: Bearer ' . $this->accessToken
        );

        $hasNfe = $this->callAPI($url, 'GET', array(), $header);

        if ($hasNfe) {
            $metodo = 'PUT';
            $url = $this->API_DOMAIN . '/shipment_invoice/' . $hasNfe->id . '?siteId=MLB';
        } else {
            $metodo = 'POST';
        }

        $retorno = $this->callAPI($url, $metodo, $arquivoxml, $header);

        if (empty($this->error)) {
            $retornoLog = $retorno;
        } else {
            $retornoLog = $this->error;
        }

        $this->marketplace->logArray[] = array('tipoEnviado' => Type_LogEnviado::Tp_LogEnviado, 'tipoIntegracao' => Type_MarketPlace::Tp_Mercadolivre,
            'origem' => __METHOD__, 'tipoLog' => Type_Log::Tp_LogPedido, 'tipoId' => Type_LogId::Tp_LogIdPedido, 'entidadeId' => $order_id,
            'url' => $url, 'metodo' => $metodo, 'httpCode' => $this->httpcode, 'retorno' => $retornoLog, 'enviado' => $arquivoxml, 'dataHora' => $this->db->dataAtual());

        if (($retorno)) {
            return $retorno;
        } else {
            $retorno = new \stdClass;
            $retorno->error = $this->error;
            return $retorno;
        }

    }

    private function translateErrorAttribute($error, $data)
    {
        $this->load->language('module/mercadolivre-extra');
        // pega os atributos faltantes no meio da mensagem
        preg_match('/\[(.*)\]/', $error, $matches);
        $attributes = substr($matches[0], 1, strlen($matches[0]) - 2);

        // pega id da categoria
        preg_match('/(MLB)\w+/', $error, $matches);
        $category = $matches[0];

        // pega atributos variáveis
        $attributesGet = array_filter($this->getAttributes($category));
        $attributesAll = array();
        foreach ($attributesGet as $value) {
            $attributesAll[$value['id']] = $value['name'];
        }

        // pega atributos não variáveis
        if (isset($data['attr_label'])) {
            $attributesAllNotVar = $data['attr_label'];
        } else {
            $attributesGet = array_filter($this->getAttributesNotVariables($category));
            $attributesAllNotVar = array();
            foreach ($attributesGet as $value) {
                $attributesAllNotVar[$value['id']] = $value['name'];
            }
        }

        // traduz atributos para a mensagem
        $attributesTranslate = array();

        $attributes = explode(',', $attributes);
        foreach ($attributes as $value) {
            if (isset($attributesAll[trim($value)])) {
                $attributesTranslate[] = $attributesAll[trim($value)];
            } else {
                $attributesTranslate[] = $attributesAllNotVar[trim($value)];
            }
        }

        // pega nome da categoria
        if (isset($data['mercaTree'])) {
            $category = $data['mercaTree'];
            $category = Trim(strrchr($category, ' '));
        } else {
            $category = $this->getCategory($category);
            $category = $category['name'];
        }

        $msg = sprintf($this->language->get('text_error_attribute'), '[' . implode(', ', $attributesTranslate) . ']', '"' . $category . '"');

        return $msg;
    }

    public function TranslateError($error, $data = array())
    {
        if ($pos = strpos($error, Type_MercadoLivreError::TP_ItemAttributesMissingRequired)) {
            $msg = $this->translateErrorAttribute($error, $data);
            if ($msg) {
                return $msg;
            }
        }

        foreach (Type_MercadoLivreError::getEnumList() as $key => $value) {
            $pos = strpos($error, $key);
            if ($pos !== false) {
                return $value;
            }
        }
        return $error;
    }


    public function getNfe($shipmentsId)
    {
        $this->getAccess();
        $header = array(
            'Authorization: Bearer ' . $this->accessToken
        );

        $url = $this->API_DOMAIN . '/shipments/' . $shipmentsId . '/invoice_data?siteId=MLB';
        $nfe = $this->callAPI($url, 'GET', array(), $header);
        return $nfe;
    }

    public function sendPictures($mercadId, $param)
    {

        $this->getAccess();

        $url = $this->API_DOMAIN . '/items/' . $mercadId;
        $header = array(
            'Content-Type: application/json',
            'Authorization: Bearer ' . $this->accessToken
        );


        $data = $this->callAPI($url, 'PUT', json_encode($param), $header);
        return ($data) ? $data->pictures : false;
    }


    public function sendMenssagePackId($param, $meli_pack_id)
    {

        $this->getAccess();
        $url = $this->API_DOMAIN . '/messages/packs/' . $meli_pack_id . '/sellers/' . $this->getAppUser() . '?application_id=' . $this->appId;

        $header = array(
            'Content-Type: application/json',
            'Authorization: Bearer ' . $this->accessToken
        );

        $data = $this->callAPI($url, 'POST', json_encode($param), $header);

        if ($this->error) {
            throw new Exception($this->error);
        } else {
            return $data;
        }

    }


    public function sendMenssage($param)
    {

        $this->getAccess();
        $url = $this->API_DOMAIN . '/messages?application_id=' . $this->appId;
        $header = array(
            'Content-Type: application/json',
            'Authorization: Bearer ' . $this->accessToken
        );
        $data = $this->callAPI($url, 'POST', json_encode($param), $header);
        return true;
    }

    public function raiseGlobalErrors()
    {
        if (strpos($this->error, 'invalid_token')) {
            throw new Exception('Token inválido para recuperar os pedidos do mercadolivre.');
        }
    }


    public function uploadPictures($file)
    {
        $this->getAccess();
        $header = array(
            'Content-Type: application/json',
            'Authorization: Bearer ' . $this->accessToken
        );
        $url = $this->API_DOMAIN . '/pictures/items/upload';
        $param = array('file' => $file);
        $data = $this->callAPI($url, 'POST', $param, $header, true);
        return $data;
    }

    public function getSellerNoticies()
    {
        $this->getAccess();
        $param = array();
        $url = $this->API_DOMAIN . '/communications/notices?limit=20';

        $header = array(
            'x-format-new: true',
            'Authorization: Bearer ' . $this->accessToken
        );

        return $this->callAPI($url, 'GET', $param, $header);

    }

    public function getVisits($ids)
    {
        $param = array();

        $date_from = date('Y-m-d', strtotime('-1 year')) . 'T00:00:00.000-00:00';
        $date_to = date('Y-m-d') . 'T23:59:59.000-00:00';

        $url = $this->API_DOMAIN . '/items/visits?ids=' . $ids . '&date_from=' . $date_from . '&date_to=' . $date_to;
        $header = array(
            'x-format-new: true'
        );

        return $this->callAPI($url, 'GET', $param, $header);

    }

    public function getReviews($item)
    {
        $param = array();
        $url = $this->API_DOMAIN . '/reviews/item/' . $item;
        $header = array(
            'x-format-new: true'
        );

        return $this->callAPI($url, 'GET', $param, $header);

    }

    public function getCategoryAuto($title)
    {

        $param = array(
            'q' => $title,
            'limit' => 1
        );

        $url = $this->API_DOMAIN . '/sites/MLB/domain_discovery/search';
        $header = array(
            'x-format-new: true'
        );

        return $this->callAPI($url, 'GET', $param, $header);

    }


    public function getSiteSearch($barcode)
    {

        $param = array();

        $url = $this->API_DOMAIN . '/sites/MLB/search?q=' . $barcode;

        $header = array(
            'x-format-new: true'
        );

        return $this->callAPI($url, 'GET', $param, $header);

    }

    public function searchProductCatalogMercadoLivre($data = array())
    {
        $this->getAccess();
        $param = array();
        if (isset($data['status'])) {
            $param['status'] = $data['status'];
        }

        if (isset($data['offset'])) {
            $param['offset'] = $data['offset'];
        }

        if (isset($data['limit'])) {
            $param['limit'] = $data['limit'];
        }

        if (isset($data['filter_product']) && ($data['filter_product'])) {
            $param['q'] = $data['filter_product'];
        }

        if (isset($data['filter_category']) && ($data['filter_category'])) {
            $param['domain_id'] = $data['filter_category'];
        }

        if (isset($data['product_barcode']) && ($data['product_barcode'])) {
            $param['product_identifier'] = $data['product_barcode'];
        }

        $url = $this->API_DOMAIN . '/products/search?site_id=MLB';

        if (!empty($param)) {
            foreach ($param as $key => $value) {
                $url .= '&' . $key . '=' . $value;
            }
        }

        $header = array(
            'Authorization: Bearer ' . $this->accessToken
        );

        $productsML = $this->callAPI($url, 'GET', false, $header);

        if ($this->error) {
            return false;
        } else {
            return $productsML;
        }

    }

    public function getProductCatalog($productCatalogId)
    {
        $this->getAccess();

        $url = $this->API_DOMAIN . '/catalog_products/' . $productCatalogId;

        $header = array(
            'Authorization: Bearer ' . $this->accessToken
        );

        $product = $this->callAPI($url, 'GET', false, $header);

        if ($this->error) {
            return false;
        } else {
            return $product;
        }

    }

    public function linkProductCatalog($mainAdId, $catalogProductId, $variationId = false)
    {
        $this->getAccess();

        $url = $this->API_DOMAIN . '/items/catalog_listings';

        $header = array(
            'Authorization: Bearer ' . $this->accessToken
        );

        $param = array(
            'item_id' => $mainAdId,
            'catalog_product_id' => $catalogProductId,
        );

        if ($variationId !== false) {
            $param['variation_id'] = (int)$variationId;
        }

        $product = $this->callAPI($url, 'POST', json_encode($param), $header);

        if ($this->error) {
            return false;
        } else {
            return $product;
        }

    }

}
