<?php

declare(strict_types=1);

class Sinistro2_VistoriadordespesaController extends Agro_Controller_Action
{
    /**
     * @var array
     */
    private $tipoValidacaoCamposForm = [
        'id_funcionario' => FILTER_VALIDATE_INT,
        'id_proposta' => FILTER_VALIDATE_INT,
        'id_parametro_tipo_despesa' => FILTER_VALIDATE_INT,
        'vl_despesa' => FILTER_VALIDATE_FLOAT,
        'id_estado' => FILTER_VALIDATE_INT,
        'dt_despesa' => 'DATA'
    ];

    /**
     * @var array
     */
    private $camposObrigatoriosForm = [
        'id_funcionario' => 'Vistoriador',
        'id_parametro_tipo_despesa' => 'Tipo de Despesa',
        'dt_despesa' => 'Data',
        'vl_despesa' => 'Valor',
        'id_estado' => 'Estado / UF',
        'id_proposta' => 'Proposta'
    ];
    
    private $tiposArquivosPermitidos = ['application/pdf' => '.pdf', 'image/png' => '.png', 'image/jpeg' => '.jpeg ou .jpg'];

    public function indexAction(): void
    {
        parent::indexAction();
        $this->view->title = 'Sinistro » Cobrança » Anexar Comprovantes de Despesas';
    }

    public function getModel(): Sinistro2_Model_VistoriadorDespesa
    {
        return new Sinistro2_Model_VistoriadorDespesa();
    }

    public function getForm(): Sinistro2_Form_VistoriadorDespesa
    {
        return new Sinistro2_Form_VistoriadorDespesa();
    }

    public function getFilter()
    {
        $usuario = Zend_Auth::getInstance()->getIdentity();
        $where = '';
		$params = $this->getRequest()->getParams();

        $funcionario = null;
        $isVistoriador = $usuario->id_role == Sistema_Model_Usuarios::PERFIL_VISTORIADOR;
        $isVistoriadorResponsavel = $usuario->id_role == Sistema_Model_Usuarios::PERFIL_VISTORIADOR_RESPONSAVEL;

        if ($isVistoriador || $isVistoriadorResponsavel) {
            $funcionario = (new Sistema_Model_Funcionarios())->getFuncionarioByIdUsuario($usuario->id);
        }

        if ($params['id_empresa'] || $isVistoriador || $isVistoriadorResponsavel) {
            $idEmpresa = !empty($funcionario) ? $funcionario['id_empresa'] : (int) $params['id_empresa'];
            $where = 'sf.id_empresa = ' . $idEmpresa;
        }

        if ($params['id_funcionario'] || $isVistoriador) {
            $idFuncionario = $isVistoriador ? (int) $funcionario['id'] : (int) $params['id_funcionario'];
           
            $where = empty($where) ? '' : "{$where} AND ";
            $where .= 'svd.id_funcionario = ' . $idFuncionario;
        }

        if ($params['id_proposta']) {
            $where = empty($where) ? '' : "{$where} AND ";
            $where .= 'svd.id_proposta =
                (SELECT id FROM seguro.propostas sp
                INNER JOIN seguro.propostas_endosso spe ON spe.id_proposta = sp.id AND fl_proposta_vigente IS TRUE
                WHERE id_proposta_mae =' . ((int) $params['id_proposta']) . '
            )';
        }

        if ($params['id_parametro_tipo_despesa']) {
            $where = empty($where) ? '' : "{$where} AND ";
            $where .= 'svd.id_parametro_tipo_despesa = ' . ((int) $params['id_parametro_tipo_despesa']);
        }

        if ($params['id_estado']) {
            $where = empty($where) ? '' : "{$where} AND ";
            $where .= 'svd.id_estado = ' . ((int) $params['id_estado']);
        }

        if ($params['dt_despesa_inicial']) {
            $dataIni = DateTime::createFromFormat('d/m/Y', $params['dt_despesa_inicial']);

            $where = empty($where) ? '' : "{$where} AND ";
            $where .= "svd.dt_despesa >= '{$dataIni->format('Y-m-d')}'";
        }

        if ($params['dt_despesa_final']) {
            $dataFinal = DateTime::createFromFormat('d/m/Y', $params['dt_despesa_final']);

            $where = empty($where) ? '' : "{$where} AND ";
            $where .= "svd.dt_despesa <= '{$dataFinal->format('Y-m-d')}'";
        }

        return $where;
    }

    public function formAction(): void
    {
        $id = $this->getRequest()->getParam('id');
        $this->view->arquivos = [];
        $this->view->title = 'Sinistro » Cobrança » Anexar Comprovantes de Despesas';

        if (!empty($id)) {
            $vistoriadorDespesa = (new Sinistro2_Model_VistoriadorDespesa())->find($id)->current();

            $this->view->idParametroTipoDespesa = $vistoriadorDespesa['id_parametro_tipo_despesa'];
            $this->view->arquivos = (new Sinistro2_Model_VistoriadorDespesaArquivo())->getArquivos([$id]);
        }
        
        parent::formAction();
    }

    public function listAction(): void
	{
		$this->_paginator = (bool) $this->getRequest()->getParam('show_paginacao');
	    parent::listAction();
	}

    public function deleteAction(): void
    {
        try {
            $this->getHelper('ViewRenderer')->setNoRender();
		    $this->getHelper('layout')->disableLayout();

            $adapter = Agro_Db_Table_Abstract::getDefaultAdapter();

            $id = (int) $this->getRequest()->getParam('id');
            
            $adapter->beginTransaction();

            $modelVistoriadorDespesaArquivo = new Sinistro2_Model_VistoriadorDespesaArquivo();

            $arquivos = $modelVistoriadorDespesaArquivo->getArquivos([$id]);
            
            if (!empty($arquivos)) {
                if (!$modelVistoriadorDespesaArquivo->delete($id, 'id_vistoriador_despesa')) {
                    throw new Exception('Erro ao excluir arquivos vinculados.');
                }
            }

            $despesaExcluida = (new Sinistro2_Model_VistoriadorDespesa())->delete($id);

            if (!$despesaExcluida) {
                throw new Exception('Erro ao excluir despesa.');
            }

            $adapter->commit();

            $retorno = ['status' => true, 'message' => 'Despesa excluída.'];
        } catch (Exception $e) {
            $adapter->rollback();
            $retorno = ['status' => false, 'message' => $e->getMessage()];
        }

        $this->getResponse()->setBody(Zend_Json::encode($retorno));
    }

    public function salvarAction(): void
    {
        $this->isAjax();
        
        $retorno = ['message' => 'Despesa do vistoriador lançada com sucesso.', 'status' => true];
        $filesToUpload = [];

        try {
            $adapter = Agro_Db_Table_Abstract::getDefaultAdapter();

            $adapter->beginTransaction();

            $params = $this->getRequest()->getParams();
            
            $arquivosVinculados = null;
            
            if (!empty($params['dt_despesa'])) {
                $dateTime = DateTime::createFromFormat('d/m/Y', $params['dt_despesa']);
                $params['dt_despesa'] = $dateTime->format('Y-m-d');
            }
            
            $this->validaCamposForm($params);

            // Necessário vincular a proposta vigente
            $params['id_proposta'] = (new Seguro_Model_Propostas())->getPropostasVigentes([$params['id_proposta']])[0]['id_proposta'];
            
            if (!empty($params['ds_justificativa'])) {
                $params['ds_justificativa'] = htmlspecialchars($params['ds_justificativa']);
            }
            
            $parametroTipoDespesa = (new Sinistro2_Model_ParametroTipoDespesa)->find($params['id_parametro_tipo_despesa'])->current();

            if (!empty($params['id']) && !empty($parametroTipoDespesa->fl_anexo_obrigatorio)) {
                $arquivosVinculados = (new Sinistro2_Model_VistoriadorDespesaArquivo())->getArquivos([$params['id']]);
            }
            
            // Se no tipo de despesa não houver limites informados, não valida justificativa
            if (
                (!empty($parametroTipoDespesa->vl_minimo) || !empty($parametroTipoDespesa->vl_maximo))
                && ($parametroTipoDespesa->vl_minimo > $params['vl_despesa']
                    || $parametroTipoDespesa->vl_maximo < $params['vl_despesa'])
                && empty($params['ds_justificativa'])
            ) {
                throw new Exception('Justificativa não informada.');
            }
            
            if (!empty($parametroTipoDespesa->fl_anexo_obrigatorio) && empty($_FILES) && empty($arquivosVinculados)) {
                throw new Exception('É obrigatório anexar comprovantes.');
            }

            $propriedade = (new Seguro_Model_PropostasPropriedades())->get($params['id_proposta']);
            if (empty($propriedade)) {
                throw new Exception('Erro ao buscar UF da proposta.');
            }

            $params['id_estado'] = $propriedade['id_estado'];
            
            $model = $this->getModel();

            $vistoriadorDespesa = $model->save($params);

            if (!empty($params['idsArquivosRemover'])) {
                $ids = explode(',', $params['idsArquivosRemover']);
                $this->deletaArquivos($ids);
            }
            
            if (!empty($_FILES)) {
                $filesToUpload = $this->salvaArquivos($vistoriadorDespesa->id);

                foreach ($filesToUpload as $uploadedFile => $destination) {
                    if (!move_uploaded_file($uploadedFile, $destination)) {
                        throw new Exception('Não foi possível fazer o upload do arquivo: '. htmlspecialchars(basename($destination)), 1);
                    }
                }
            }
            
            $adapter->commit();

        } catch (Exception $e) {
            $adapter->rollback();

            // Deleta arquivos
            if ($e->getCode() == 1) {
                foreach ($filesToUpload as $file) {
                    if (file_exists($file)) {
                        unlink($file);
                    }
                }
            }

            $retorno = [
                'message' => $e->getMessage(),
                'status' => false
            ];
        }

        $this->getResponse()->setBody(Zend_Json::encode($retorno));
    }

    private function deletaArquivos(array $idsVistoriadorDespesaArquivo): void
    {
        $modelVistoriadorDespesaArquivo = new Sinistro2_Model_VistoriadorDespesaArquivo();

        $arquivos = $modelVistoriadorDespesaArquivo->find($idsVistoriadorDespesaArquivo);
        $ids = [];
        
        foreach ($arquivos as $arquivo) {
            if (file_exists($arquivo['ds_caminho_arquivo'])) {
                $ids[] = $arquivo['id'];
                unlink($arquivo['ds_caminho_arquivo']);
            }
        }
        
        if (!empty($ids)) {
            $modelVistoriadorDespesaArquivo->delete($ids);
        }
    }

    private function salvaArquivos(int $id): array
    {
        $modelVistoriadorDespesaArquivo = new Sinistro2_Model_VistoriadorDespesaArquivo();
        $targetDir = APPLICATION_PATH . '/upload_files/vistoriador_despesa/';

        if (!is_dir($targetDir)) {
            mkdir($targetDir, 0777, true);
        }

        if (!empty($_FILES)) {
            $filesToUpload = [];
            $numberOfFiles = count($_FILES['files']['name']);
            
            for ($i = 0; $i < $numberOfFiles; $i++) {
                $tipoArquivo = mime_content_type($_FILES['files']['tmp_name'][$i]);

                if (!$this->tiposArquivosPermitidos[$tipoArquivo]) {
                    throw new Exception(
                        "Arquivos devem ser: "
                        . implode(', ', $this->tiposArquivosPermitidos)
                    );
                }

                $targetFile = $targetDir . "DESPESA_{$id}_" . date("Ymd") . '_' . basename($_FILES['files']['name'][$i]);

                $modelVistoriadorDespesaArquivo->save([
                    'id_vistoriador_despesa' => $id,
                    'ds_caminho_arquivo' => $targetFile
                ]);
                
                $filesToUpload[$_FILES['files']['tmp_name'][$i]] = $targetFile;
            }
        }
        
        return $filesToUpload;
    }

    public function validavalordespesaAction(): void
	{
		$this->isAjax();

		$params = $this->getRequest()->getParams();
		$retorno = ['message' => 'Valor despesa válido', 'status' => true];

		try {
			if (!filter_var($params['id_parametro_tipo_despesa'], FILTER_VALIDATE_INT)) {
				throw new Exception('ID do tipo da despesa deve ser um inteiro', 400);
			}
	
			if (!filter_var($params['vl_despesa'], FILTER_VALIDATE_FLOAT)) {
				throw new Exception('Valor da despesa deve ser um decimal.', 400);
			}

			if (!filter_var($params['id_funcionario'], FILTER_VALIDATE_INT)) {
				throw new Exception('ID funcionário deve ser um inteiro.', 400);
			}

			$parametroTipoDespesa = (new Sinistro2_Model_ParametroTipoDespesa)
                ->find($params['id_parametro_tipo_despesa'])
                ->current();
            
            if (empty($parametroTipoDespesa)) {
				throw new Exception("Tipo de despesa não encontrado.", 404);
			}
            
            $ultrapassouLimite = (new Sinistro2_Model_VistoriadorDespesa())->ultrapassouLimitesDespesas(
                Sinistro2_Model_VistoriadorDespesaDTO::fromArray($params)
            );
            
            if ($ultrapassouLimite < 0) {
                throw new Exception("O valor lançado não alcança o valor mínimo previsto em contrato para a despesa informada. Por favor, justifique.", 422);
            }

            if ($ultrapassouLimite > 0) {
                throw new Exception("O valor lançado ultrapassa o valor máximo previsto em contrato para a despesa informada. Por favor, justifique.", 422);
            }

		} catch (Exception $e) {
			$retorno = ['message' => $e->getMessage(), 'status' => false];
			$this->getResponse()->setHttpResponseCode($e->getCode());
		}

		$this->getResponse()->setBody(Zend_Json::encode($retorno));
	}

    private function validaCamposForm(array $params): void
    {
        foreach ($this->camposObrigatoriosForm as $key => $campo) {
            if (empty($params[$key])) {
                throw new Exception("Campo '{$campo}' é obrigatório.");
            }
        }

        $proposta = (new Seguro_Model_Propostas())->find($params['id_proposta'])->current();

        if (empty($proposta)) {
            throw new Exception("Proposta {$params['id_proposta']} não existe.");
        }

        $mensagemErroData = 'Parâmetro %PLACE_HOLDER% deve ser uma data.';

        foreach($this->tipoValidacaoCamposForm as $key => $validacao) {
            if ($validacao == 'DATA') {
                $date = $params[$key];
                $d = DateTime::createFromFormat('Y-m-d', $date);

                if ($d && $d->format('Y-m-d') === $date) {
                    continue;
                }

                throw new Exception($mensagemErroData);
            }

            if (!filter_var($params[$key], $validacao)) {
                throw new Exception("Parâmetro {$key} é inválido.");
            }
        }
    }

    public function getestadovistoriadorAction(): void
    {
        $this->isAjax();

        $idFuncionario = $this->getRequest()->getParam('idFuncionario');
        $retorno = [];

        try {
            if (!filter_var($idFuncionario, FILTER_VALIDATE_INT)) {
                throw new Exception('ID Funcionario deve ser um inteiro.', 400);
            }

            $vistoriadorDespesa = (new Sinistro2_Model_VistoriadorDespesa())
                ->getUltimaDespesaLancadaVistoriador((int) $idFuncionario);
            
            $retorno = ['status' => true, 'idEstado' => $vistoriadorDespesa['id_estado'] ?? 0];
        } catch(Exception $e) {
            $this->getResponse()->setHttpResponseCode($e->getCode());
            $retorno = ['status' => false, 'message' => $e->getMessage()];
        }

        $this->getResponse()->setBody(Zend_Json::encode($retorno));
    }
}