<?php

namespace App\DAO;

use App\DAO\DAOResponse\DAOResponse;
use App\DAO\DAOResponse\DAOResponseInterface;
use App\DAO\Pagination\Pagination;
use App\Entities\Endereco\Endereco;
use App\Entities\Perfil\Perfil;
use App\Entities\Permissao\Permissao;
use App\Mappers\PerfilMapper;
use App\Models\CEPModel;
use App\Models\EnderecoModel;
use App\Models\PerfilModel;
use App\Models\PermissaoModel;
use App\Models\PessoaJuridicaModel;
use App\Models\PessoaModel;
use App\Utils\Collections\QueryFilterCollection;
use App\Utils\Collections\QueryOrderCollection;
use App\ViewModels\Perfil\PerfilRegistrationViewModel\PerfilRegistrationViewModel;
use App\ViewModels\Perfil\PerfilUpdateViewModel\PerfilUpdateViewModel;
use Exception;

class PerfilDAO extends DAO {

	public function persistPerfilRegistrationVM(PerfilRegistrationViewModel $perfil_registration_vm): DAOResponse {

		$dao_response = new DAOResponse();
		try {
			$this->db->transStart();
            $perfil = $this->persistPerfilFromPerfilRegistrationVM($perfil_registration_vm);
			$permissao = $this->persistPermissaoFromPerfilRegistrationVM( $perfil_registration_vm, $perfil );
			$permissao_ativo = $this->persistPermissaoFromPerfilRegistrationListVM( $perfil_registration_vm, $perfil );
			$error = $this->db->error();
			if ($error && $error ['code']) {
				$this->setDatabaseErrorsForPerfilRegistration( $dao_response, $error );
			} else {
                $dao_response->setStatus( DAOResponseInterface::SUCESS );
                $this->db->transComplete();
			}
		} catch ( Exception $exception ) {
			$this->setDatabaseErrorsForPerfilRegistration( $dao_response, $this->db->error() );
		}
		return $dao_response;

	}

	private function setDatabaseErrorsForPerfilRegistration(DAOResponse $dao_response, array $error): void {

		if ($error ['code']) {
			$dao_response->setStatus( $error ['code'] );
			$dao_response->setErrors( $this->getDatabaseResponseErrorsForPersistence( $error ) );
		} else {
			$dao_response->setStatus( DAOResponseInterface::DATABASE_CONNECTION_ERROR );
		}
		log_message( 'error', json_encode( $error ) );
	}

    private function persistPerfilFromPerfilRegistrationVM(PerfilRegistrationViewModel &$perfil_registration_vm): Perfil {

        $perfil_model = new PerfilModel();
        $perfil = PerfilMapper::getPerfilFromPerfilRegistrationVM( $perfil_registration_vm );
        $perfil_model->registerPerfil($perfil);
        if (! $perfil->getId()) {
            throw new Exception( 'O cadastro de perfil não teve êxito' );
        }
        return $perfil;

    }

	private function persistPermissaoFromPerfilRegistrationVM(PerfilRegistrationViewModel &$perfil_registration_vm, Perfil $perfil): Permissao {

		$permissao_model = new PermissaoModel();
        $response_array_end = $this->getEndpointList();
        foreach ($response_array_end as $key => $item):
            $permissao = PerfilMapper::getPermissaoFromPerfilRegistrationVM($perfil,  $item['id'], $item['controller'].'.'.$item['method'].'.'.$item['action']);
            $permissao_model->registerPermissao( $permissao );
            if (! $permissao->getId()) {
                throw new Exception( 'O cadastro de endereço não foi teve êxito' );
            }
            endforeach;
		return $permissao;

	}

    private function persistPermissaoFromPerfilRegistrationListVM(PerfilRegistrationViewModel &$perfil_registration_vm, Perfil $perfil): bool {

        $permissao_model = new PermissaoModel();
        //$endereco = PerfilMapper::getEnderecoFromPerfilUpdateVM( $perfil_update_vm );
        $sucess_update = $permissao_model->updatePermissao($perfil->getId(), $perfil_registration_vm->getPermissao());
        return $sucess_update;

    }

    // BUSCANDO ENDPOINTS DA APLICAÇÃO
    public function getEndpointList (): array {
        $sql = "
        SELECT id, `method`, controller, `action`, `path`
        FROM endpoint;
        ";
        $query = $this->db->query($sql);
        $result_array = $query->getResultArray();
        return $result_array;
    }


    // LISTAGEM DE PERFIL
	public function getPerfilList(?Pagination $pagination, ?QueryFilterCollection $query_filter_collection, ?QueryOrderCollection $query_order_collection = null): DAOResponse {

		$sql = '
            SELECT perfil.id as perfil_id
            ,perfil.descricao as perfil_nome 
            FROM perfil
        ';
		$binds = [];
        return $this->getDAOResponseFromPaginatedSelect( $binds, $pagination, $query_filter_collection, $query_order_collection, $sql, function (array $result_array) {
			return PerfilMapper::getPerfilVMListFromArray( $result_array );
		});

	}

    public function getPerfilDetailsByPerfilID (int $id_perfil): DAOResponse {
        $sql = "
            SELECT 
            perfil.id as perfil_id 
            ,perfil.descricao as perfil_nome 
            ,permissao.id as permissao_id
            ,permissao.ativo as ativo
            ,endpoint.id as endpoint_id
            ,endpoint.`method`  as endpoint_method
            ,endpoint.controller  as endpoint_controller
            ,endpoint.`action`  as endpoint_action
            ,endpoint.`path`  as endpoint_path
            FROM perfil 
            INNER JOIN permissao on perfil.id = permissao.id_perfil 
            INNER JOIN endpoint on endpoint.id = permissao.id_endpoint
            WHERE 
            	perfil.id = :id_perfil:
            order by endpoint_controller
        ";
        $binds = ['id_perfil' => $id_perfil];
        $query = $this->db->query($sql, $binds);
        $result_array = $query->getResultArray();
        $dao_response = new DAOResponse;
        if (count($result_array)) {
            $perfil_details_vm = PerfilMapper::getPerfilDetailsVMFromArray($result_array);
            $dao_response->setData($perfil_details_vm);
            $dao_response->setStatus(DAOResponseInterface::SUCESS);
        } else {
            $dao_response->setStatus(DAOResponseInterface::RECORD_NOT_FOUND);
        }
        return $dao_response;
    }

	private function getDatabaseResponseErrorsForPersistence(array $error): array {

		$dao_response_errors = [ ];
		if ($error && $error ['code']) {
			if ($error ['code'] === DAOResponseInterface::FOREIGN_KEY_DONT_EXISTS) {
				if (strpos( $error ['message'], 'cep' ) !== false) {
					$dao_response_errors ['cep'] = 'Esse cep não existe.';
				}
			} else if ($error ['code'] === DAOResponseInterface::DUPLICATED_UNIQUE_KEY) {
				if (strpos( $error ['message'], '\'pessoa_juridica.cnpj\'' ) !== false) {
					$dao_response_errors ['cpf'] = 'Uma unidade com esse CNPJ já está cadastrada no sistema.';
				}
                if (strpos( $error ['message'], '\'pessoa.id_endereco\'' ) !== false) {
                    $dao_response_errors ['cpf'] = 'Erro ao cadastrar pessoa.';
                }
			}
		}
		return $dao_response_errors;

	}

    // PERSISTINDO ALTERAÇÕES
	public function persistPerfilUpdateVM(int $id_perfil, PerfilUpdateViewModel $perfil_update_vm): DAOResponse {

		$dao_response = new DAOResponse();
		$this->db->transStart();
		$status_persistence = $this->persistPerfilFromPerfilUpdateVM( $id_perfil, $perfil_update_vm );
		$status_persistence = $status_persistence && $this->persistPermissaoDeleteFromPerfilUpdateVM( $id_perfil, $perfil_update_vm );
		$status_persistence = $status_persistence && $this->persistPermissaoFromPerfilUpdateListVM( $id_perfil, $perfil_update_vm );
		$error = $this->db->error();
		if ($error && $error ['code']) {
			$this->setDatabaseErrorsForPerfilUpdate( $dao_response, $error );
			return $dao_response;
		} else if ($status_persistence) {
			$this->db->transComplete();
			$dao_response->setStatus( DAOResponseInterface::SUCESS );
		}
		$this->db->close();
		return $dao_response;

	}

	private function setDatabaseErrorsForPerfilUpdate(DAOResponse $dao_response, array $error): void {

		$dao_response->setStatus( $error ['code'] );
		$dao_response->setErrors( $this->getDatabaseResponseErrorsForPersistence( $error ) );

	}

    private function persistPerfilFromPerfilUpdateVM(int $id_perfil, PerfilUpdateViewModel &$perfil_update_vm): bool {

        $perfil_model = new PerfilModel();
        $perfil = PerfilMapper::getPerfilFromPerfilUpdateVM( $perfil_update_vm );
        $sucess_update = $perfil_model->updatePerfilByID( $id_perfil, $perfil );
        return $sucess_update;
    }

    private function persistPermissaoDeleteFromPerfilUpdateVM(int $id_perfil, PerfilUpdateViewModel &$perfil_update_vm): bool {

        $perfil_model = new PermissaoModel();
        $sucess_update = $perfil_model->limpaPermissao($id_perfil);
        return $sucess_update;
    }

	private function persistPermissaoFromPerfilUpdateListVM(int $id_perfil, PerfilUpdateViewModel &$perfil_update_vm): bool {

		$permissao_model = new PermissaoModel();
		//$permissao = PerfilMapper::getPermissaoFromPerfilUpdateVM( $perfil_update_vm );
		$sucess_update = $permissao_model->updatePermissao( $id_perfil, $perfil_update_vm->getPermissao() );
		return $sucess_update;

	}


    // VALIDA SE PERFIL EXISTE AO EDITAR

    public function getPerfilFromId(int $id_perfil) {

        $sql = '
            SELECT 1
            FROM perfil
            WHERE perfil.id = :id_perfil:
        ';
        $binds = [
            'id_perfil' => $id_perfil ];
        $query = $this->db->query( $sql, $binds );
        $result_array = $query->getResultArray();
        return ($result_array);

    }

}