<?php

class Remessa
{
    private $erro = [];
    private $remessa = "";
    private $sequencial;
    private $codigos;
    private $filename;
    private $total_registros = 0;
    private $valor_total = 0;

    // Controle
    private $banco;
    private $lote;
    private $registro;

    // Empresa
    private $inscricao_tipo;
    private $inscricao_numero;

    private $convenio;

    private $cc_agencia_codigo;
    private $cc_agencia_digito;

    private $cc_conta_codigo;
    private $cc_conta_digito;

    private $cc_digito;

    private $nome;
    private $beneficiario;
    private $inscricao_beneficiario;

    private $nome_banco;

    // Arquivo
    private $arquivo_codigo;
    private $arquivo_data;
    private $arquivo_hora;
    private $arquivo_layout;
    private $arquivo_densidade;

    // Endereço
    private $endereco_logradouro;
    private $endereco_numero;
    private $endereco_complemento;
    private $endereco_cidade;
    private $endereco_cep;
    private $endereco_estado;

    public function __construct()
    {
        $this->banco                    = $this->num(748, 3);

        $this->inscricao_tipo           = $this->num(2, 1);
        $this->inscricao_numero         = $this->num(95764684000104, 14);
        $this->inscricao_beneficiario   = $this->num(82892282000143, 14);
        $this->convenio                 = $this->alfa("18MO", 20);
        $this->cc_agencia_codigo        = $this->num(226, 5);
        $this->cc_agencia_digito        = $this->alfa("7", 1);
        $this->cc_conta_codigo          = $this->num(12836, 12);
        $this->cc_conta_digito          = $this->alfa("8", 1);
        $this->cc_digito                = $this->alfa("", 1);
        $this->nome                     = $this->alfa("HABIMOVEIS - ADMINISTRACAO DE IMOVEIS LTDA", 30);
        $this->beneficiario             = $this->alfa("PMF TCRS 2019", 30);
        $this->nome_banco               = $this->alfa("SICREDI", 30);

        $this->arquivo_codigo           = $this->num(1, 1);
        $this->arquivo_data             = $this->num(date("dmY"), 8);
        $this->arquivo_hora             = $this->num(date("His"), 6);
        $this->arquivo_layout           = $this->num(82, 3);
        $this->arquivo_densidade        = $this->num(1600, 5);

        $this->endereco_logradouro      = $this->alfa("PEDRO SOARES", 30);
        $this->endereco_numero          = $this->alfa("62", 5);
        $this->endereco_complemento     = $this->alfa("CENTRO", 15);
        $this->endereco_cidade          = $this->alfa("FLORIANOPOLIS", 20);
        $this->endereco_cep             = "88020070";
        $this->endereco_estado          = $this->alfa("SC", 2);
    }

    public function header()
    {
        $this->sequencial = $this->numeroSequencial();

        $remessa   = "";
        $remessa  .= $this->banco;
        $remessa  .= $this->num(0, 4);
        $remessa  .= $this->num(0, 1);
        $remessa  .= $this->alfa("", 9);
        $remessa  .= $this->inscricao_tipo;
        $remessa  .= $this->inscricao_numero;
        $remessa  .= $this->convenio;
        $remessa  .= $this->cc_agencia_codigo;
        $remessa  .= $this->cc_agencia_digito;
        $remessa  .= $this->cc_conta_codigo;
        $remessa  .= $this->cc_conta_digito;
        $remessa  .= $this->cc_digito;
        $remessa  .= $this->nome;
        $remessa  .= $this->nome_banco;
        $remessa  .= $this->alfa("", 10);
        $remessa  .= $this->arquivo_codigo;
        $remessa  .= $this->arquivo_data;
        $remessa  .= $this->arquivo_hora;
        $remessa  .= $this->num($this->sequencial, 6);
        $remessa  .= $this->arquivo_layout;
        $remessa  .= $this->arquivo_densidade;
        $remessa  .= $this->alfa("", 20);
        $remessa  .= $this->alfa("", 20);
        $remessa  .= $this->alfa("", 29);
        $remessa  .= "\r\n";

        $this->remessa .= $remessa;
    }

    public function headerLote()
    {
        $remessa   = "";
        $remessa  .= $this->banco;
        $remessa  .= $this->num(1, 4);
        $remessa  .= $this->num(1, 1);
        $remessa  .= $this->alfa("C", 1);
        $remessa  .= $this->num(03, 2);
        $remessa  .= $this->num(31, 2);
        $remessa  .= $this->num(42, 3);
        $remessa  .= $this->alfa("", 1);
        $remessa  .= $this->inscricao_tipo;
        $remessa  .= $this->inscricao_numero;
        $remessa  .= $this->convenio;
        $remessa  .= $this->cc_agencia_codigo;
        $remessa  .= $this->cc_agencia_digito;
        $remessa  .= $this->cc_conta_codigo;
        $remessa  .= $this->cc_conta_digito;
        $remessa  .= $this->cc_digito;
        $remessa  .= $this->nome;
        $remessa  .= $this->alfa("", 40);
        $remessa  .= $this->endereco_logradouro;
        $remessa  .= $this->endereco_numero;
        $remessa  .= $this->endereco_complemento;
        $remessa  .= $this->endereco_cidade;
        $remessa  .= substr($this->endereco_cep, 0, 5);
        $remessa  .= substr($this->endereco_cep, 5, 3);
        $remessa  .= $this->endereco_estado;
        $remessa  .= $this->alfa("", 18);
        $remessa  .= "\r\n";

        $this->remessa .= $remessa;
    }

    public function detalhe()
    {
        $data_pagamento = implode(array_reverse(explode("-", $_POST["data_pagamento"])));
        $remessa        = "";
        $i              = 1;

        foreach ($this->codigos as $codigo) {
            // SEGMENTO J
            $codigo_barras = $this->barras($codigo["codigo_barras"]);
            if ($codigo_barras) {
                $remessa  .= $this->banco;                                                          // Código no banco da compensação
                $remessa  .= $this->num(1, 4);                                                      // Lote de serviço
                $remessa  .= $this->num(3, 1);                                                      // Tipo de registro
                $remessa  .= $this->num($i, 5);                                                     // Nº Sequencial do registro no lote
                $remessa  .= $this->alfa("J", 1);                                                   // Código de segmento do reg. detalhe
                $remessa  .= $this->num(0, 1);                                                      // Tipo de Movimento
                $remessa  .= $this->num(0, 2);                                                      // Código da Instrução p/ Movimento
                $remessa  .= $codigo_barras;                                                        // Código de barras
                $remessa  .= $this->nome;                                                           // Nome da Concessionária / Órgao Público
                $remessa  .= date("dmY", strtotime($this->vencimento($codigo["codigo_barras"])));   // Data Vencimento (Nominal)
                $remessa  .= $this->num(str_replace(".", "", $codigo["valor"]), 15);                // Valor do Título (Nominal)
                $remessa  .= $this->num(0, 15);                                                     // Valor do Desconto + Abatimento
                $remessa  .= $this->num(0, 15);                                                     // Valor da Mora + Multa
                $remessa  .= $this->num($data_pagamento, 8);                                        // Data Pagamento
                $remessa  .= $this->num(str_replace(".", "", $codigo["valor"]), 15);                // Valor Pagamento
                $remessa  .= $this->num(0, 15);                                                     // Quantidade da Moeda
                $remessa  .= $this->alfa($this->sequencial . $this->total_registros, 20);           // Nº do Docto. Atribuído pela Empresa
                $remessa  .= $this->alfa($this->sequencial . $this->total_registros, 20);           // Nº do Docto. Atribuído pelo Banco
                $remessa  .= $this->num(9, 2);                                                      // Código de Moeda
                $remessa  .= $this->alfa("", 6);                                                    // Uso Exclusivo FEBRAN/CNAB
                $remessa  .= $this->alfa("", 10);                                                   // Códigos das Ocorrências p/ Retorno
                $remessa  .= "\r\n";


                $tipo = (strlen($this->extrairNumeros($codigo["documento"])) == 11 ? 1 : 2);
                $i++;
                // SEGMENTO J52 
                $remessa  .= $this->banco;                                  // Código no banco da compensação
                $remessa  .= $this->num(1, 4);                              // Lote de serviço
                $remessa  .= $this->num(3, 1);                              // Tipo de registro
                $remessa  .= $this->num($i, 5);                             // Nº Sequencial do registro no lote
                $remessa  .= $this->alfa("J", 1);                           // Código de segmento do reg. detalhe
                $remessa  .= $this->alfa("", 1);                            // Uso Exclusivo FEBRAN/CNAB
                $remessa  .= $this->num(1, 2);                              // Código de Movimento Remessa
                $remessa  .= $this->num(52, 2);                             // Identificação
                // SACADO       
                $remessa  .= $this->num($tipo, 1);                          // Tipo Inscrição
                $remessa  .= $this->num($this->extrairNumeros($codigo["documento"]), 15);          // Nº Inscrição
                $remessa  .= $this->alfa($codigo["nome"], 40);              // Nome
                // BENEFICIÁRIO 
                $remessa  .= $this->num($this->inscricao_tipo, 1);          // Tipo Inscrição
                $remessa  .= $this->num($this->inscricao_beneficiario, 15); // Nº Inscrição
                $remessa  .= $this->alfa($this->beneficiario, 40);          // Nome
                // AVALISTA
                $remessa  .= $this->num(0, 1);                              // Tipo Inscrição
                $remessa  .= $this->num(0, 15);                             // Nº Inscrição
                $remessa  .= $this->alfa("", 40);                           // Nome

                $remessa  .= $this->alfa("", 53);                           // Uso Exclusivo FEBRAN/CNAB
                $remessa  .= "\r\n";

                $this->total_registros += 2;
                $this->valor_total += $codigo["valor"];
                $i++;
            } else {
                $this->erro[] = $codigo["nome"];
            }
        }

        $this->remessa .= $remessa;
    }

    public function trailerLote()
    {
        $this->valor_total = number_format($this->valor_total, 2, ".", "");
        $remessa   = $this->banco;                                              // Código do banco na compensação
        $remessa  .= $this->num(1, 4);                                          // Lote de serviço
        $remessa  .= $this->num(5, 1);                                          // Tipo de registro
        $remessa  .= $this->alfa("", 9);                                        // Uso exclusivo SICREDI
        $remessa  .= $this->num($this->total_registros + 2, 6);                 // Quantidade de registros do lote
        $remessa  .= $this->num(str_replace(".", "", $this->valor_total), 18);  // Somatória dos valores
        $remessa  .= $this->num(0, 18);                                         // Somatória de quantidade de moedas
        $remessa  .= $this->num(0, 6);                                          // Número aviso de débito
        $remessa  .= $this->alfa("", 165);                                      // Uso exclusivo SICREDI
        $remessa  .= $this->alfa("", 10);                                       // Códigos das ocorrências para retorno
        $remessa  .= "\r\n";

        $this->remessa .= $remessa;
    }

    public function trailer()
    {
        $remessa   = "";
        $remessa  .= $this->banco;                                  // Código do banco na compensação
        $remessa  .= $this->num(9999, 4);                           // Lote de serviço
        $remessa  .= $this->num(9, 1);                              // Tipo de registro
        $remessa  .= $this->alfa("", 9);                            // Uso exclusivo SICREDI
        $remessa  .= $this->num(1, 6);                              // Qtde de lotes do arquivo
        $remessa  .= $this->num(($this->total_registros + 4), 6);   // Qtde de registros do arquivo
        $remessa  .= $this->num(0, 6);                              // Qtde de contas p/ conc. (lotes) 3
        $remessa  .= $this->alfa("", 205);                          // Uso exclusivo SICREDI
        $remessa  .= "\r\n";

        $this->remessa .= $remessa;
    }

    public function gerar()
    {
        $dia = str_pad(date("d"), 2, "0", STR_PAD_LEFT);
        $mes = str_pad(date("m"), 2, "0", STR_PAD_LEFT);
        $sequencial  = $this->sequencial;

        if ($sequencial < 10) {
            $sequencial = "0{$sequencial}";
        };

        $filename = "18MO{$dia}{$sequencial}.REM";

        $file       = "remessas/{$filename}";
        $handle     = fopen($file, "w");

        fwrite($handle, $this->remessa);
        fclose($handle);

        header("Content-type: text/plain");
        header("Content-Disposition: attachment; filename={$filename}");
        header("Pragma: no-cache");
        header("Expires: 0");
        readfile($file);
        return;
    }

    /**
     * Recupera o último número sequencial
     * @return int
     */
    private function numeroSequencial(): int
    {
        $file       = "sequencial.txt";
        $handle     = fopen($file, "r");
        $sequencial = fread($handle, filesize($file));
        $handle     = fopen($file, "w");

        fwrite($handle, ($sequencial + 1));
        fclose($handle);

        return $sequencial;
    }

    /**
     * Converte um valor na quantidade de digitos definida.
     * @param  int    $string
     * @param  int    $lenght
     * @return string
     */
    private function num(int $string, int $lenght): string
    {
        return str_pad($string, $lenght, 0, STR_PAD_LEFT);
    }

    /**
     * Converte um valor na quantidade de digitos definida.
     * @param  string $string
     * @param  int    $lenght
     * @return string
     */
    private function alfa(string $string, int $lenght): string
    {
        if (mb_strlen($string) > $lenght) {
            $string = substr($string, 0, $lenght);
        }

        return str_pad($string, $lenght, " ", STR_PAD_RIGHT);
    }

    private function codigoBarras(string $codigo_barras): string
    {
        return substr($codigo_barras, 3);
    }

    /**
     * Converte dias em data
     * @param  string $codigo_barras
     * @return string
     */
    private function vencimento(string $codigo_barras): string
    {
        return date('Y-m-d', strtotime('1997-10-07 +' . substr($codigo_barras, 33, 4) . ' days'));
    }

    /**
     * Recupera o banco do código de barras
     * @param  string $codigo_barras
     * @return string
     */
    private function banco(string $codigo_barras): string
    {
        return substr($codigo_barras, 0, 3);
    }

    /**
     * Faz a importação do arquivo
     * @return void
     */
    public function importar(): void
    {
        $uploaddir      = "{$_SERVER["DOCUMENT_ROOT"]}/files/";
        $this->filename = basename($_FILES['arquivo']['name']);
        $uploadfile     = $uploaddir . $this->filename;

        move_uploaded_file($_FILES['arquivo']["tmp_name"], $uploadfile);

        if (substr($this->filename, -3) == "xls") {
            $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls();
        } else {
            $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
        }

        $file        = "files/{$this->filename}";
        $spreadsheet = $reader->load($file);
        $highestRow  = $spreadsheet->getActiveSheet()->getHighestRow();

        for ($i = 2; $i <= $highestRow; $i++) {
            $codigo     = $spreadsheet->getActiveSheet()->getCellByColumnAndRow(4, $i)->getValue();
            $documento  = $spreadsheet->getActiveSheet()->getCellByColumnAndRow(3, $i)->getValue();
            $valor      = $spreadsheet->getActiveSheet()->getCellByColumnAndRow(5, $i)->getValue();

            if (!empty($codigo) && !empty($documento) && !empty($valor)) {
                $this->codigos[] = [
                    "codigo_barras" => $codigo,
                    "documento"     => $documento,
                    "nome"          => "",
                    "valor"         => number_format(str_replace(",", ".", $valor), 2),
                ];
            }
        }

        $this->codigos;
    }

    private function barras($codigo)
    {
        return $this->calcula_barra($codigo);
    }

    private function calcula_barra($linha)
    {
        $barra  = preg_replace("/\D/", "", $linha);

        if ($this->modulo_11('34191000000000000001753980229122525005423000') != 1) {
            //if ($this->modulo_11($barra) != 1) {
            return false;
        }

        if (strlen($barra) < 47) {
            $barra = $barra . '00000000000' . substr(0, 47 - strlen($barra));
        }

        if (strlen($barra) != 47) {
            return false;
        }

        $barra = substr($barra, 0, 4)
            . substr($barra, 32, 15)
            . substr($barra, 4, 5)
            . substr($barra, 10, 10)
            . substr($barra, 21, 10);

        if ($this->modulo_11(substr($barra, 0, 4) . substr($barra, 5, 39)) != substr($barra, 4, 1)) {
            return false;
        }

        return $barra;
    }

    private function modulo_11($numero)
    {
        $numero  = preg_replace("/\D/", "", $numero);

        $soma = 0;
        $peso = 2;
        $base = 9;
        $digito = 0;

        for ($i = strlen($numero); $i > 0; --$i) {
            $soma += (substr($numero, $i - 1, 1) * $peso);

            if ($peso < $base) {
                $peso++;
            } else {
                $peso = 2;
            }
        }

        $digito = 11 - ($soma % 11);

        if ($digito > 9) {
            $digito = 0;
        }

        if ($digito == 0) {
            $digito = 1;
        }

        return $digito;
    }

    public function getErro()
    {
        return $this->erro;
    }
    function extrairNumeros($string)
    {
        return preg_replace('/\D/', '', $string);
    }
}
