<?php

namespace Teknisa\Libs\Util;

use Doctrine\ORM\EntityManager;
use Zeedhi\Framework\DataSource\DataSet;
use Zeedhi\Framework\DTO\Request\Row;
use Zeedhi\Framework\DTO\Response;
use Zeedhi\Framework\DTO\Response\Error;
use Teknisa\Libs\Util\NewCode;
use Teknisa\Libs\Util\Environment;

class Address {
    protected $connection;
    protected $environment;
    protected $newCode;

    public function __construct(EntityManager $entityManager, Environment $environment, NewCode $newCode) {
        $this->connection = $entityManager->getConnection();
        $this->environment = $environment;
        $this->newCode = $newCode;
    }

    public function findAddress(Row $request, Response $response) {
        $row = $request->getRow();
        
        $CEP = str_pad($row['CEP'], 8 , "0");
        $BAIRRO = isset($row['NMBAIRCLIE'])? $row['NMBAIRCLIE'] : ''; // trativa feita para integração de pedidos.
        $NRORG = isset($row['NRORG']) ? $row['NRORG'] : $this->environment->getNrOrgTrab();
        $CDOPERADOR = isset($row['$CDOPERADOR']) ? $row['$CDOPERADOR'] : $this->environment->getCurrentUserId();

        $this->environment->setNrOrgTrab($NRORG);
        $this->environment->setCurrentUserId($CDOPERADOR);
        
        try {
            $endereco = false;
            $retornoCorreios = $this->searchCEP($CEP);
            if ($retornoCorreios) {
            
                $endereco = array();

                $params =  array();
                $params["DSLOGRADOURO"] = explode(' ', $retornoCorreios["logradouro"])[0];

                $retornoSelect = $this->findLogradouroByName($params);

                if ($retornoSelect && count($retornoSelect) > 0) {
                    $endereco['CDLOGRADOURO'] = $retornoSelect["CDLOGRADOURO"];
                    $endereco['DSLOGRADOURO'] = $retornoSelect["DSLOGRADOURO"];
                }

                $endereco['DSENDERECO'] = (!isset($endereco['DSLOGRADOURO']) || $retornoCorreios["logradouro"] == '')? $retornoCorreios["logradouro"] : substr($retornoCorreios["logradouro"], strlen($endereco['DSLOGRADOURO']) + 1);

                $params =  array();
                $params["SGESTADO"] = $retornoCorreios["uf"];

                $retornoSelect = $this->findEstadoByCode($params);

                if ($retornoSelect) {
                    $endereco["CDPAIS"]   = '0055';
                    $endereco["NMPAIS"]   = 'Brasil';
                    $endereco["SGESTADO"] = $retornoSelect["SGESTADO"];
                    $endereco["NMESTADO"] = $retornoSelect["NMESTADO"];

                    $acentos = array(
                        'á'=>'a', 'à'=>'a', 'ã'=>'a', 'â'=>'a', 'ä'=>'a', 'é'=>'e', 'ê'=>'e', 'è'=>'e', 'ë'=>'e', 'í'=>'i', 'ì'=>'i', 'ï'=>'i',
                        'î'=>'i', 'ó'=>'o', 'ô'=>'o', 'ò'=>'o', 'õ'=>'o', 'ö'=>'o', 'ú'=>'u', 'ü'=>'u', 'ù'=>'u', 'û'=>'u', 'ç'=>'c', 'Á'=>'A',
                        'À'=>'A', 'Ã'=>'A', 'Â'=>'A', 'Ä'=>'A', 'É'=>'E', 'Ê'=>'E', 'È'=>'E', 'Ë'=>'E', 'Í'=>'I', 'Ì'=>'I', 'Ï'=>'I', 'Î'=>'I',
                        'Ó'=>'O', 'Ô'=>'O', 'Õ'=>'O', 'Ò'=>'O', 'Ö'=>'O', 'Ú'=>'U', 'Ù'=>'U', 'Û'=>'U', 'Ü'=>'U', 'Ç'=>'C'
                    );

                    $params =  array();
                    $params["SGESTADO"] = $endereco["SGESTADO"];
                    $params["NMMUNICIPIO"] = $retornoCorreios["municipio"];
                    $params["NMMUNICIPIO_SEMACENTO"] = strtr($retornoCorreios["municipio"], $acentos);
                    $params["IBGE"] = $retornoCorreios["ibge"];

                    $retornoSelect = $this->findMunicipioByName($params);

                    if ($retornoSelect) {
                        $endereco['NMMUNICIPIO'] = $retornoSelect["NMMUNICIPIO"];
                        $endereco['CDMUNICIPIO'] = $retornoSelect["CDMUNICIPIO"];
                        
                        $params =  array();
                        $params["SGESTADO"] = $endereco['SGESTADO'];
                        $params["CDMUNICIPIO"] = $endereco['CDMUNICIPIO'];
                        $params["NMBAIRRO"] = $retornoCorreios["bairro"];
                        $params["NMBAIRRO_SEMACENTO"] = strtr($retornoCorreios["bairro"], $acentos);
                        $params['NRORG'] = $NRORG;
                        
                        //funcao com bairro não é mais utilizada foi definido na SEPG que bairro da tabelas de endereço nao vão mais ter Relação com a tebela de Bairro
                        
                        if($retornoCorreios["bairro"] != "") {
                            $trueUpper = array(
                                'á'=>'A', 'à'=>'A', 'ã'=>'A', 'â'=>'A', 'ä'=>'A', 'é'=>'E', 'ê'=>'E', 'è'=>'E', 'ë'=>'Ë', 'í'=>'I', 'ì'=>'I', 'ï'=>'I',
                                'î'=>'I', 'ó'=>'O', 'ô'=>'O', 'ò'=>'O', 'õ'=>'O', 'ö'=>'O', 'ú'=>'U', 'ü'=>'U', 'ù'=>'U', 'û'=>'U', 'ç'=>'C'
                            );

                            $retornoCorreios["bairro"] = strtoupper(strtr($retornoCorreios["bairro"], $trueUpper));
                            $endereco['NMBAIRRO'] = $retornoCorreios["bairro"];
                        }else{
                            //para casos que o buscaCep nao traga bairro pega o bairro que o cliente enviar tratativa feita para integração de pedidos.
                            $endereco['NMBAIRRO'] = $BAIRRO;
                        }
                        $endereco['CDBAIRRO'] = '';
                    }
                    else {
                        $response->setError(new Error("O município ".$params["NMMUNICIPIO"]." não foi carregado automaticamente porque não está cadastrado no sistema.", 500));
                    }
                }
                else {
                    $response->setError(new Error("O estado ".$params["SGESTADO"]." não foi carregado automaticamente porque não está cadastrado no sistema.", 500));
                }

                $response->addDataSet(new DataSet("ENDERECO", $endereco));
            }
            else {
                $response->setError(new Error("CEP não encontrado.", 500));
            }
        }
        catch (\Exception $e) {
            $response->setError(new Error($e->getMessage(), 500));
        }
    }
    
    public function buscaCepAbertoCurl($cep){
        $apiKey = '29480816412064b521ff9c2e5f0f7462'; // Substitua pela sua chave de API
        $api = 'CEPAberto';
        
        try {
            // Configura a URL da API
            $url = 'https://www.cepaberto.com/api/v3/cep?cep=' . $cep;
    
            // Inicializa a sessão cURL
            $ch = curl_init($url);
    
            // Configura as opções da cURL
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_HTTPHEADER, [
                'Authorization: Token token=' . $apiKey,
                'Content-Type: application/json'
            ]);
    
            // Executa a requisição e obtém a resposta
            $response = curl_exec($ch);
    
            // Verifica se houve algum erro com a requisição
            if (curl_errno($ch)) {
                throw new \Exception('Erro de cURL: ' . curl_error($ch), 500);
            }
    
            // Fecha a sessão cURL
            curl_close($ch);
        
            if($response != false && $api == 'CEPAberto'){
                return $response;
            }
            return false;
        
        } catch (\Exception $e) {
            // Trata o erro e exibe uma mensagem adequada
            echo 'Erro: ' . $e->getMessage();
        }
    }

    public function searchCEP($cep) {
        $user = '';
        $pass = '';
        $host = '192.168.122.3';
        $port = '8080';
        $auth = base64_encode("$user:$pass");
        $header[] = "Proxy-Authorization: Basic $auth";
        $header[] = "Content-Type: application/json";
        $api = 'viacep';
        
        try {
            // Nova api de cep. Postmon estava desatualizada Primeira Tentativa 
            $response = @file_get_contents('http://viacep.com.br/ws/'.$cep.'/json/');
            $response = json_decode($response, true);
            $cepError = \Zeedhi\Framework\Util\Functions::arrayKeyExists("erro", $response);
            
            if ($cepError || empty($response)) {
                throw new \Exception('Erro ao consultar o CEP na primeira tentativa.', 500);
            }
            
        } catch(\Exception $e) {
            try{
                $api = 'postmon';
                //Segunda tentativa utilizando a antiga ferramenta, caso viacep esteja fora do AR.
                $response = @file_get_contents('http://api.postmon.com.br/cep/'.$cep);
                $response = json_decode($response, true);
                $cepError = \Zeedhi\Framework\Util\Functions::arrayKeyExists("erro", $response);
                
                if ($cepError || empty($response)) {
                    throw new \Exception('Erro ao consultar o CEP na segunda tentativa.', 500);
                }
                
            }catch(\Exception $e) {
                try {
                    $api = 'CEPAberto';
                    //Terceira tentativa utilizando uma api que evolui constantemente porem possui limite de requisiçoes.
                    $response = $this->buscaCepAbertoCurl($cep);
                    $response = json_decode($response, true);
                    $cepError = \Zeedhi\Framework\Util\Functions::arrayKeyExists("erro", $response);
                    
                    if ($cepError || empty($response)) {
                        throw new \Exception('Erro ao consultar o CEP na terceira tentativa.', 500);
                    }
                } catch(\Exception $e) {
                    return false;
                }
            }
        }
        
        if ($response != false && $api == 'viacep' && !$cepError) {
            return array(
                'logradouro' => \Zeedhi\Framework\Util\Functions::arrayKeyExists("logradouro", $response) ? $response["logradouro"] : "",
                'bairro'     => \Zeedhi\Framework\Util\Functions::arrayKeyExists("bairro", $response) ? $response["bairro"] : "",
                'municipio'  => \Zeedhi\Framework\Util\Functions::arrayKeyExists("localidade", $response) ? $response["localidade"] : "",
                'uf'         => \Zeedhi\Framework\Util\Functions::arrayKeyExists("uf", $response) ? $response["uf"] : "",
                'cep'        => $response["cep"],
                'ibge'       => \Zeedhi\Framework\Util\Functions::arrayKeyExists("ibge", $response) ? $response["ibge"] : ""
            );
        }
        elseif($response != false && $api == 'postmon' && !$cepError){
            return array(
                'logradouro' => \Zeedhi\Framework\Util\Functions::arrayKeyExists("logradouro", $response) ? $response["logradouro"] : "",
                'bairro'     => \Zeedhi\Framework\Util\Functions::arrayKeyExists("bairro", $response) ? $response["bairro"] : "",
                'municipio'  => \Zeedhi\Framework\Util\Functions::arrayKeyExists("cidade", $response) ? $response["cidade"] : "",
                'uf'         => \Zeedhi\Framework\Util\Functions::arrayKeyExists("estado", $response) ? $response["estado"] : "",
                'cep'        => $response["cep"],
                'ibge'       => \Zeedhi\Framework\Util\Functions::arrayKeyExists("codigo_ibge", $response) ? $response["codigo_ibge"] : ""
            );

        } elseif($response != false && $api == 'CEPAberto' && !$cepError) {
            return array(
                'logradouro' => \Zeedhi\Framework\Util\Functions::arrayKeyExists("logradouro", $response) ? $response["logradouro"] : "",
                'bairro'     => \Zeedhi\Framework\Util\Functions::arrayKeyExists("bairro", $response) ? $response["bairro"] : "",
                'municipio'  => \Zeedhi\Framework\Util\Functions::arrayKeyExists("cidade", $response) ? $response["cidade"]["nome"] : "",
                'uf'         => \Zeedhi\Framework\Util\Functions::arrayKeyExists("estado", $response) ? $response["estado"]["sigla"] : "",
                'cep'        => $response["cep"],
                'ibge'       => \Zeedhi\Framework\Util\Functions::arrayKeyExists("cidade", $response) ? $response["cidade"]["ibge"] : ""
            );
        }
        
        return false;
    }

    private function findEstadoByCode($params) {
        $sql = "SELECT SGESTADO, NMESTADO
                  FROM ESTADO
                 WHERE CDPAIS = '0055'
                   AND UPPER(SGESTADO) = UPPER(:SGESTADO)";

        $std = $this->connection->executeQuery($sql,$params);

        return $dataSet = $std->fetchAssociative();
    }

    private function findMunicipioByName($params) {
        $ibgeParams = array();
        $ibgeParams['IBGE'] = $params['IBGE'];
        
        $sql = "SELECT NMMUNICIPIO, CDMUNICIPIO FROM MUNICIPIO
            WHERE CDMUNICIBGE = :IBGE";
        $std = $this->connection->executeQuery($sql, $ibgeParams);
        $dataSet = $std->fetchAssociative();
        if(!$dataSet){
            $sql = "SELECT NMMUNICIPIO, CDMUNICIPIO
                      FROM MUNICIPIO
                     WHERE CDPAIS = '0055'
                       AND SGESTADO = :SGESTADO
                       AND (UPPER(NMMUNICIPIO) = UPPER(:NMMUNICIPIO)
                        OR UPPER(NMMUNICIPIO) = UPPER(:NMMUNICIPIO_SEMACENTO))";
            unset($params['IBGE']);
            $std = $this->connection->executeQuery($sql, $params);
            $dataSet = $std->fetchAssociative();
        }
        return $dataSet;
    }

    private function findBairroByName($params) {
        $sql = "SELECT NMBAIRRO, CDBAIRRO
                  FROM BAIRRO
                 WHERE CDPAIS = '0055'
                  AND SGESTADO = :SGESTADO
                  AND CDMUNICIPIO = :CDMUNICIPIO
                  AND (UPPER(NMBAIRRO) = UPPER(:NMBAIRRO)
                    OR UPPER(NMBAIRRO) = UPPER(:NMBAIRRO_SEMACENTO))
                  AND NRORG = :NRORG";

        $std = $this->connection->prepare($sql);
        $std->executeQuery($params);
        return $dataSet = $std->fetchAssociative();
    }

    private function findLogradouroByName($params) {
        $sql = "SELECT CDLOGRADOURO, DSLOGRADOURO
                  FROM LOGRADOURO
                 WHERE UPPER(DSLOGRADOURO) LIKE UPPER(:DSLOGRADOURO) || '%'
                   AND NRORG = :NRORG";

        $params['NRORG'] = $this->environment->getNrOrgTrab();

        $std = $this->connection->executeQuery($sql, $params);

        return $dataSet = $std->fetchAssociative();
    }

    private function insertBairro($NMBAIRRO, $CDMUNICIPIO, $SGESTADO, $CDPAIS) {
        $sql = "INSERT INTO BAIRRO (
                CDBAIRRO,       NMBAIRRO,           CDPAIS,
                SGESTADO,       CDMUNICIPIO,        NRORG,
                DTINCLUSAO,     CDOPERINCLUSAO,     NRORGINCLUSAO)
                VALUES (
                :CDBAIRRO,      :NMBAIRRO,          :CDPAIS,
                :SGESTADO,      :CDMUNICIPIO,       :NRORG,
                SYSDATE,        :CDOPERINCLUSAO,    :NRORG)";

        $CONTADOR = "BAIRRO".$CDPAIS.$SGESTADO.$CDMUNICIPIO;
        $CODIGO = $this->newCode->getCodeByTableName($CONTADOR, $this->environment->getNrOrgTrab());
        $params = array(
            "CDBAIRRO"       => $CODIGO,
            "NMBAIRRO"       => $NMBAIRRO,
            "CDPAIS"         => $CDPAIS,
            "SGESTADO"       => $SGESTADO,
            "CDMUNICIPIO"    => $CDMUNICIPIO,
            "NRORG"          => $this->environment->getNrOrgTrab(),
            "CDOPERINCLUSAO" => $this->environment->getCdOperador()
        );
        $std = $this->connection->prepare($sql);
        $std->executeQuery($params);

        return $CODIGO;
    }
}