<?php declare(strict_types=1);

class Seguro_Service_ImportarParcelas
{
    const MAP_CABECALHO = [
        'proposta (endosso)' => true,
        'valor subvenção federal' => true,
        'valor subvenção estadual' => true,
        'valor segurado' => true,
        'número da parcela' => true,
        'data de vencimento' => true
    ];

    const MAP_INDEX_COLUNA = [0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F'];

    /**
     * @var array
     */
    private $erros = [];

    public function executar(array $arquivo)
    {
        if ($arquivo['type'] != 'text/csv') {
            throw new InvalidArgumentException('Arquivo informado não é CSV.');
        }

        $fileHandle = fopen($arquivo['tmp_name'], 'r');
        
        if ($fileHandle === false) {
            throw new Exception('Erro ao abrir arquivo CSV.');
        }

        $linha = 1;
        $arrayPropostasParcelas = $dadosInsert = $arrayIdsPropostas = [];

        while ($dados = fgetcsv($fileHandle, 1000, ";")) {
            if ($linha == 1) {
                $this->validarCelulasCabecalho($dados);
            } else {
                $valorTotal = 0;
                $idxValor = -1;

                if ($this->linhaEstaVazia($dados)) {
                    $linha++;
                    continue;
                }

                foreach ($dados as $idx => $valor) {
                    $this->validarValor($idx, $linha, $valor);

                    if (in_array($idx, [1, 2, 3]) && $valorTotal == 0) {
                        $valorTotal = $valor;
                        $idxValor = $idx;
                    }
                }

                $arrayPropostasParcelas[] = ['id_proposta' => $dados[0], 'nr_parcela' => $dados[4]];

                if (empty($this->erros)) {
                    $arrayIdsPropostas[] = $dados[0];
                    $dateTimeObject = DateTime::createFromFormat('d/m/Y', $dados[5]);
                    $dadosInsert[] = [
                        'id_proposta' => $dados[0],
                        'nr_parcela' => $dados[4],
                        'vl_total' => $valorTotal,
                        'vl_segurado' => $idxValor == 3 ? $valorTotal : 0,
                        'vl_subvencao_federal' => $idxValor == 1 ? $valorTotal : 0,
                        'vl_subvencao_estadual' => $idxValor == 2 ? $valorTotal : 0,
                        'dt_vencimento' => $dateTimeObject->format('Y-m-d')
                    ];
                }
            }

            $linha++;
        }

        fclose($fileHandle);

        if (!empty($this->erros)) {
            return;
        }

        $modelParcelas = new Seguro_Model_PropostasParcelas();

        $rs = $modelParcelas->getPropostasParcelasCadastradas($arrayPropostasParcelas);

        foreach ($rs as $row) {
            $this->erros[] = "Proposta {$row['id_proposta']} já possui parcela de número {$row['nr_parcela']} cadastrada.";
        }

        $sqlInsert = $this->getInsert($dadosInsert, $arrayIdsPropostas);

        if (!empty($this->erros)) {
            return;
        }

        $db = Zend_Db_Table_Abstract::getDefaultAdapter();

        try {
            $db->query($sqlInsert);
        } catch (Zend_Db_Exception $e) {
            throw new Exception('Erro ao cadastrar parcelas.');
        }
    }

    private function linhaEstaVazia(array $celulas): bool
    {
        foreach ($celulas as $valor) {
            if (!empty($valor)) {
                return false;
            }
        }

        return true;
    }

    public function getErros(): array
    {
        return $this->erros;
    }

    private function getInsert(array $dadosInsert, array $arrayIdsPropostas)
    {
        $insert = 'INSERT INTO seguro.propostas_parcelas (
            id_usuario_criacao,
            id_proposta,
            nr_parcela,
            vl_total,
            vl_segurado,
            vl_subvencao_federal,
            vl_subvencao_estadual,
            dt_vencimento,
            id_endosso
            ) VALUES %VALORES%';

        $map = (new Seguro_Model_PropostasEndosso())->getMapIdPropostaToIdEndosso($arrayIdsPropostas);
        $usuario = Zend_Auth::getInstance()->getIdentity();

        $valores = $virgula = '';
        foreach ($dadosInsert as $row) {
            $row['id_endosso'] = $map[$row['id_proposta']] ?? null;

            if (empty($row['id_endosso'])) {
                $this->erros[] = "Proposta {$row['id_proposta']} não existe.";
            }

            $idEndosso = $map[$row['id_proposta']];

            $valores .= "{$virgula} ({$usuario->id}, {$row['id_proposta']}, {$row['nr_parcela']}, {$row['vl_total']}";
            $valores .= ",{$row['vl_segurado']}, {$row['vl_subvencao_federal']}, {$row['vl_subvencao_estadual']}";
            $valores .= ",'{$row['dt_vencimento']}', {$idEndosso})";

            $virgula = ',';
        }

        return str_replace('%VALORES%', $valores, $insert);
    }

    private function validarCelulasCabecalho(array $celulas): void
    {
        $mensagemErro = '';
        foreach ($celulas as $valor) {
            $aux = preg_replace('/^\xEF\xBB\xBF/', '', $valor);

            if (!array_key_exists(trim(strtolower($aux)), self::MAP_CABECALHO)) {
                $mensagemErro .= !empty($mensagemErro) ? ',' . $valor : $valor;
            }
        }

        if (!empty($mensagemErro)) {
            throw new Exception('Os seguintes cabeçalhos não existem: ' . $mensagemErro);
        }
    }

    private function validarValor(int $idx, int $linha, $valor): void
    {
        $coluna = self::MAP_INDEX_COLUNA[$idx];

        switch ($idx) {
            case 0:
            case 4:

                if (!filter_var($valor, FILTER_VALIDATE_INT) || $valor < 0) {
                    $this->erros[] = "Linha {$linha} coluna {$coluna}: valor deve ser um inteiro positivo.";
                }

                break;
            case 1:
            case 2:
            case 3:
                $aux = (bool) strstr($valor, ',');
                if ($aux) {
                    $this->erros[] = "Linha {$linha} coluna {$coluna}: valor inválido.";
                }

                break;
            default:

                $aux = DateTime::createFromFormat('d/m/Y', $valor);
                
                if (!$aux) {
                    $this->erros[] = "Linha {$linha} coluna {$coluna}: data inválida.";
                }

                break;
        }
    }
}
