<?php

use PayPalCheckoutSdk\Core\AccessToken;

require_once('restapi.php');

class Cep extends RestApi
{
    const CEP_DB_NAME = 'cep';
    const CEP_DB_USERNAME = 'cep';
    const CEP_DB_PASSWORD = 'Fut!13@CeP@13Orc';

    public function getCep($cep)
    {
        try {
            $query = "SELECT /*+ MAX_EXECUTION_TIME(3000) */ * FROM ceps WHERE cep = " . intval($cep);
            $address = $this->query($query);

            if ($address === false || $address == null) {
                return $this->getCorreiosAccessToken($cep);
            } else {
                return current($address);
            }

        } catch (Exception $e) {
            if ($e->getCode() == 70100) {
                return $this->getCorreiosAccessToken($cep);
            }

            throw $e;
        }
    }

    public function getCorreiosAccessToken($cep)
    {
        try {
            $correiosKeys = $this->getCorreiosParams();
            $accessTokenKeyDb = Crypto::descriptografarAES256($correiosKeys['correios_key'], Crypto::CAES256);
            $data = json_decode($accessTokenKeyDb, true);

            $token = $data['token'];
            $contract = $data['correioscontract'];

            // Documentação referente a API dos Correios: https://www.correios.com.br/atendimento/developers/arquivos/manual-para-integracao-correios-api e https://cws.correios.com.br/
            $url = 'https://api.correios.com.br/token/v1/autentica/contrato';
            $header = [
                'Accept: application/json',
                'Authorization: Basic ' . $token,
                'Content-Type: application/json'
            ];
            $params = [
                'numero' => $contract
            ];

            $requestApi = parent::callAPI($url, 'POST', $params, $header, false, $response, Type_DadosSend::TpDados_Json);

            $accessToken = $requestApi->token;

            return $this->searchAddressFromCorreios($cep, $accessToken);

        } catch (Exception $error) {
            throw new Exception('Erro ao gerar token dos Correios: ' . $error->getMessage());
        }
    }

    public function searchAddressFromCorreios($cep, $accessToken)
    {
        try {
            $url = 'https://api.correios.com.br/cep/v2/enderecos/' . $cep;
            $header = [
                'Accept: application/json',
                'Authorization: Bearer ' . $accessToken,
            ];

            $requestApi = parent::callAPI($url, 'GET', false, $header, false, $response, Type_DadosSend::TpDados_Json);

            return $this->getTownCode($requestApi);

        } catch (Exception $error) {
            throw new Exception('Erro ao buscar endereço via API Correios: ' . $error->getMessage());
        }
    }

    public function getTownCode($addressObject)
    {
        $address = (array) $addressObject;

        $uf = $address['uf'];
        $city = $address['localidade'];
        $url = 'https://servicodados.ibge.gov.br/api/v1/localidades/estados/' . $uf . '/municipios';

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode == 200) {
            $data = json_decode($response, true);
            $cityCode = array_filter($data, function ($town) use ($city) {
                return $town['nome'] === $city;
            });

            $cityCode = reset($cityCode);
            $address['codigo_municipio'] = $cityCode['id'];
        } else {
            $address['codigo_municipio'] = false;
        }

        return $address;
    }

    private function getCorreiosParams()
    {
        $query = $this->query('select * from parametros');

        $response = $query[0];

        return $response;
    }

    public function getCepEndereco($chave)
    {
        $chaves = explode(",", $chave);
        $inc = 0;
        $sql = ' ';
        $filtro = ' Where ';
        foreach ($chaves as $value) {
            $value = '"' . trim($value) . '"';
            if ($inc == 0)
                $sql .= "( (position(concat('|',{$value},'|')  in a.CHAVE_CONSULTA)) +
                      (position(concat({$value},'|')  in a.CHAVE_CONSULTA)) +
                      (position({$value}  in a.CHAVE_CONSULTA))";
            else
                $sql .= "+ (position(concat('|',{$value},'|')  in a.CHAVE_CONSULTA)) +
                      (position(concat({$value},'|')   in a.CHAVE_CONSULTA)) +
                      (position({$value} in a.CHAVE_CONSULTA))";

            if ($inc == 0)
                $filtro .= "( a.CHAVE_CONSULTA  like concat('%', " . $value . ", '%'))";
            else
                $filtro .= "and ( a.CHAVE_CONSULTA  like concat('%'," . $value . ", '%'))";

            $inc++;
        }

        if ($inc > 0)
            $sql .= ") ordenacao,";

        $ceps = $this->query("
            select " . $sql . "
                a.uf, a.cidade, a.bairro, a.tipo_logradouro, a.logradouro, a.logradouro_completo, a.cep, a.complemento,
                a.nome, a.chave_consulta, a.codigo_municipio
            from ceps a " . $filtro . "
            order by 1, a.logradouro, a.cidade
            limit 100");

        return $ceps ? $ceps : false;
    }

    public function query($sql, $fetch = true)
    {
        $result = false;

        $link = mysqli_connect(DB_HOSTNAME, self::CEP_DB_USERNAME, self::CEP_DB_PASSWORD);
        $db_selected = mysqli_select_db($link, self::CEP_DB_NAME);
        if ($db_selected) {
            $query = $link->query($sql);
            if ($query) {
                $result = $fetch ? $query->fetch_all(MYSQLI_ASSOC) : true;
            }
        }
        $link->close();
        return $result;
    }

    public function insert($data)
    {
        $link = mysqli_connect(DB_HOSTNAME, self::CEP_DB_USERNAME, self::CEP_DB_PASSWORD);
        $data = array_filter($data);
        if ($data) {
            $sql = "INSERT INTO ceps (" . implode(', ', array_keys($data)) . ") VALUES (";

            foreach ($data as $value) {
                $sql .= mb_strtoupper("'" . mysqli_real_escape_string($link, trim($value)) . "', ", 'UTF-8');
            }
            $sql = substr($sql, 0, -2);
            $sql .= ");";
            return $this->query($sql, false);
        }

        return false;
    }

    public function update($postcode, $data)
    {
        $link = mysqli_connect(DB_HOSTNAME, self::CEP_DB_USERNAME, self::CEP_DB_PASSWORD);
        $data = array_filter($data);
        if ($data) {
            $sql = "UPDATE ceps SET ";

            foreach ($data as $key => $value) {
                $sql .= mysqli_real_escape_string($link, trim($key)) . " = ";
                $sql .= mb_strtoupper("'" . mysqli_real_escape_string($link, trim($value)) . "', ", 'UTF-8');
            }
            $sql = substr($sql, 0, -2);
            $sql .= " WHERE cep = '" . $postcode . "';";
            return $this->query($sql, false);
        }

        return false;
    }

    public function saveNewCep($postcode, $data) {
        try {
            $cepData = $this->getCorreiosAccessToken($postcode);
       
            if (($cepData && isset($cepData['cep'])) || (isset($data['uf']) && isset($data['city']) && isset($data['postcode']))) {
                $uf = $cepData['uf'] ?? $data['uf'];
                $cidade = $cepData['localidade'] ?? $data['city'];
                $bairro = $cepData['bairro'] ?? $data['address_2'];
                $tipo_logradouro = $cepData['tipoLogradouro'] ?? $data['tipo_logradouro'];
                $logradouro = $cepData['logradouro'] ?? $data['address_1'];
                $logradouro_completo = trim($tipo_logradouro . " " . $logradouro);
                $cep = $cepData['cep'] ?? $data['postcode'];
                $complemento = $cepData['complemento'] ?? $data['complemento'];
                $nome = $cepData['nomeLogradouro'] ?? null;
                $codigo_municipio = $cepData['codigo_municipio'] ?? $data['codigo_municipio'];
                $chave_consulta = ($uf ."|". $bairro ."|". $cidade ."|". (!empty($tipo_logradouro) ? $tipo_logradouro . " " : ""). $logradouro ."|". $cep);

                $dataToSave = [
                    'uf' => $uf,
                    'cidade' => $cidade,
                    'bairro' => $bairro,
                    'tipo_logradouro' => $tipo_logradouro,
                    'logradouro' => $logradouro,
                    'logradouro_completo' => $logradouro_completo,
                    'cep' => $cep,
                    'complemento' => $complemento,
                    'nome' => $nome,
                    'chave_consulta' => $chave_consulta,
                    'codigo_municipio' => $codigo_municipio
                ];
                return $this->insert($dataToSave);
            }
        } catch (Exception $e) {
            return false;
        }
    }

}
