<?php

namespace App\DAO;

use App\DAO\DAOResponse\DAOResponse;
use App\DAO\DAOResponse\DAOResponseInterface;
use App\DAO\Pagination\Pagination;
use App\Entities\ConvenioProcedimento\ConvenioProcedimento;
use App\Entities\ProcedimentoValor\ProcedimentoValor;
use App\Mappers\ProcedimentoValorMapper;
use App\Models\ConvenioProcedimentoModel;
use App\Models\ProcedimentoValorModel;
use App\Services\LogService;
use App\Utils\Collections\QueryFilterCollection;
use App\Utils\Collections\QueryOrderCollection;
use App\ViewModels\ProcedimentoValor\ProcedimentoValorRegistrationViewModel\ProcedimentoValorRegistrationViewModel;
use App\ViewModels\ProcedimentoValor\ProcedimentoValorUpdateViewModel\ProcedimentoValorUpdateViewModel;
use Exception;

class ProcedimentoValorDAO extends DAO {

	public function persistProcedimentoValorRegistrationVM(ProcedimentoValorRegistrationViewModel $procedimento_registration_vm, $idusr): DAOResponse {

		$dao_response = new DAOResponse();
		try {
			$this->db->transStart();
			$convenioprocediemto = $this->persistConvenioProcedimento( $procedimento_registration_vm, $idusr );
			$procedimento_registration_vm->setIdConvenioProcedimento( $convenioprocediemto->getId() );
			$procedimento = $this->persistProcedimentoValor( $procedimento_registration_vm );

			$error = $this->db->error();
			if ($error && $error ['code']) {
				$status_persistence = $error ['code'];
				$dao_response_errors = [ ];
				if (strpos( $error ['message'], 'procedimento.descricao' ) !== false) {
					$dao_response_errors ['descricao'] = 'Esse procedimento já existe';
				}
				if (strpos( $error ['message'], 'fk_procedimento_id_tipo_procedimento' ) !== false) {
					$dao_response_errors ['id_tipo_procedimento'] = 'O tipo de procedimento não existe';
				}
				if (strpos( $error ['message'], 'fk_procedimento_criado_por' ) !== false) {
					$dao_response_errors ['criado_por'] = 'Esse usuário não existe.';
				}
				$dao_response->setErrors( $dao_response_errors );
				log_message( 'error', json_encode( $error ) );
			} else {
				$status_persistence = !!$procedimento->getId() ? DAOResponseInterface::SUCESS : DAOResponseInterface::DATABASE_CONNECTION_ERROR;
				$this->db->transComplete();
			}
		} catch ( Exception $exception ) {
			log_message( 'error', json_encode( $this->db->error() ) );
			$status_persistence = DAOResponseInterface::DATABASE_CONNECTION_ERROR;
			$procedimento = null;
		}
		$this->db->close();
		$dao_response->setStatus( $status_persistence );
		if ($status_persistence === DAOResponseInterface::SUCESS) {
			$dao_response->setData( $procedimento );
		}
		return $dao_response;

	}

	private function persistProcedimentoValor(ProcedimentoValorRegistrationViewModel $procedimento_registration_vm): ProcedimentoValor {

		$procedimento_model = new ProcedimentoValorModel();
		$procedimento = ProcedimentoValorMapper::getProcedimentoValorFromProcedimentoValorRegistrationVM( $procedimento_registration_vm );
		$procedimento_model->registerProcedimentoValor( $procedimento );
		return $procedimento;

	}

	private function persistConvenioProcedimento(ProcedimentoValorRegistrationViewModel $procedimento_registration_vm, $idusr): ConvenioProcedimento {

		$convenio_procedimento_model = new ConvenioProcedimentoModel();
		$convenio_procedimento = ProcedimentoValorMapper::getProcedimentoValorToRegisterConvenioProcedimento( $procedimento_registration_vm, $idusr );
		$convenio_procedimento_model->registerConvenioProcedimento( $convenio_procedimento );
		return $convenio_procedimento;

	}

	# -------------------------------------------------------------------------------------------------------------------#
	public function getProcedimentoValorList(?Pagination $pagination, ?QueryFilterCollection $query_filter_collection,
			?QueryOrderCollection $query_order_collection = null): DAOResponse {

		$sql = '
            SELECT 
                procedimento_valor.id as id, 
                procedimento_valor.id_convenio_procedimento as id_convenio_procedimento,
            	procedimento_valor.valor as valor, 
				procedimento_valor.valor_interno as valor_interno, 
				procedimento_valor.valor_repasse_medico as valor_repasse_medico, 
            	procedimento_valor.data_inicio as data_inicio, 
            	procedimento_valor.data_fim as data_fim,
            	procedimento_valor.status as status,
            	convenio.id as id_convenio,
            	convenio.descricao as descricao_convenio,
            	convenio.codigo as codigo_convenio,
            	procedimento.id as id_procedimento,
            	procedimento.descricao as descricao_procedimento,
            	tipo_procedimento.descricao as descricao_tipo_procedimento,
            	convenio_procedimento.id_unidade as id_unidade,
            	unidade.descricao as descricao_unidade
            FROM procedimento_valor 
            inner join convenio_procedimento on procedimento_valor.id_convenio_procedimento = convenio_procedimento.id 
            inner join convenio on convenio_procedimento.id_convenio = convenio.id
            inner join procedimento on convenio_procedimento.id_procedimento = procedimento.id 
            inner join unidade on unidade.id = convenio_procedimento.id_unidade 
            inner join tipo_procedimento on procedimento.id_tipo_procedimento = tipo_procedimento.id 
        ';
		return $this->getDAOResponseFromPaginatedSelect( [ ], $pagination, $query_filter_collection, $query_order_collection, $sql,
			function (array $result_array) {
				return ProcedimentoValorMapper::getProcedimentoValorDetailsVMCollectionFromArray( $result_array );
			} );

	}

	# -------------------------------------------------------------------------------------------------------------------#
	public function getConvenioProcedimentoNotValor(?Pagination $pagination, ?QueryFilterCollection $query_filter_collection,
			?QueryOrderCollection $query_order_collection = null): DAOResponse {

		$sql = <<<SQL
			select distinct
				procedimento.id as id_procedimento, procedimento.descricao as descricao_procedimento,
				procedimento.criado_em as criado_em, procedimento.id_tipo_procedimento, procedimento.criado_por
			from procedimento
			where
				procedimento.id not in (select id_procedimento from convenio_procedimento where id_unidade = :id_unidade: and id_convenio = :id_convenio:)
		SQL;

		
		$array = $query_filter_collection->toArray();

		$binds = [ 
			'id_unidade' => $array [0] ['value'],
			'id_convenio' => $array [1] ['value']
		];

		return $this->getDAOResponseFromPaginatedSelect( $binds, $pagination, null, $query_order_collection, $sql,
			function (array $result_array) {
				return ProcedimentoValorMapper::getConvenioProcedimentoNotValorVMCollectionFromArray( $result_array );
			} );

	}

	# -------------------------------------------------------------------------------------------------------------------#
	public function deleteProcedimentoValor(int $id_procedimento): DAOResponse {

		$procedimento_model = new ProcedimentoValorModel();
		$dao_response = $this->getDAOResponseForDeletion( $procedimento_model, $id_procedimento );
		return $dao_response;

	}

	public function inativaProcedimentoValor(int $id_procedimento): DAOResponse {

		$sql = <<<SQL
		update 
			procedimento_valor
		set 
			status = 0
		where
			id = :procedimentovalor_id:			
		SQL;
		$binds = [ 
			'procedimentovalor_id' => $id_procedimento
		];

		$dao_response = new DAOResponse();
		$conn = db_connect();
		$result = $conn->query( $sql, $binds )->numRows;
		$errors = $conn->error();
		if ($errors ['code'] != 0) {
			$dao_response->setStatus( DAOResponseInterface::DATABASE_CONNECTION_ERROR );
			$dao_response->setErrors( $errors );
		} else {
			$affectrows = $conn->affectedRows();			
			if($affectrows > 0) {
				$dao_response->setStatus( DAOResponseInterface::SUCESS );
			} else {
				$dao_response->setStatus( DAOResponseInterface::RECORD_NOT_FOUND );
			}
			$dao_response->setData([]);
		}

		return $dao_response;

	}
	
	public function ativarProcedimentoValor(int $id_procedimento): DAOResponse {
		
		$sql = <<<SQL
		update
			procedimento_valor
		set
			status = 1
		where
			id = :procedimentovalor_id:
		SQL;
		$binds = [
				'procedimentovalor_id' => $id_procedimento
		];
		
		$dao_response = new DAOResponse();
		$conn = db_connect();
		$result = $conn->query( $sql, $binds )->numRows;
		$errors = $conn->error();
		if ($errors ['code'] != 0) {
			$dao_response->setStatus( DAOResponseInterface::DATABASE_CONNECTION_ERROR );
			$dao_response->setErrors( $errors );
		} else {
			$affectrows = $conn->affectedRows();
			if($affectrows > 0) {
				$dao_response->setStatus( DAOResponseInterface::SUCESS );
			} else {
				$dao_response->setStatus( DAOResponseInterface::RECORD_NOT_FOUND );
			}
			$dao_response->setData([]);
		}
		
		return $dao_response;
		
	}

	public function gettotalRequisicaoProcedimentoValorUnidade(int $id_procedimento, int $id_unidade): DAOResponse {

		$sql = <<<SQL
		select count(*) total 
		from 
			procedimento_valor pv , convenio_procedimento cp ,requisicao_procedimento rp 
		where 
			pv.id = :procedimentovalor_id: 
			and pv.id_convenio_procedimento  = cp.id  
			and cp.id_unidade = :unidade_id:
			and pv.id_convenio_procedimento  = rp.id_convenio_procedimento 
		SQL;
		$binds = [ 
			'procedimentovalor_id' => $id_procedimento,
			'unidade_id' => $id_unidade
		];

		$dao_response = new DAOResponse();
		$conn = db_connect();
		$result = $conn->query( $sql, $binds )
			->getResultArray();
		$errors = $conn->error();
		if ($errors ['code'] != 0) {
			$dao_response->setStatus( DAOResponseInterface::DATABASE_CONNECTION_ERROR );
			$dao_response->setErrors( $errors );
		} else {
			$dao_response->setStatus( DAOResponseInterface::SUCESS );
			$dao_response->setData( $result [0] );
		}

		return $dao_response;

	}

	public function getṔrocedimento(QueryFilterCollection $query_filter_collection): DAOResponse {

		$sql = '
            SELECT
                procedimento.id AS id,
                procedimento.descricao AS descricao,
                tipo_procedimento.id AS id_tipo_procedimento,
                tipo_procedimento.descricao as tipo_procedimento
            FROM
                procedimento
                LEFT JOIN tipo_procedimento ON tipo_procedimento.id = procedimento.id_tipo_procedimento
            WHERE 
                procedimento.id_tipo_procedimento = tipo_procedimento.id
        ';
		$binds = [ ];
		return $this->getDAOResponseFromUniqueRowSelect( $binds, $query_filter_collection, $sql,
			function (array $result_array) {
				$sql2 = 'SELECT exame.id AS id, exame.nome AS nome FROM procedimento_exame, exame
            WHERE exame.id = procedimento_exame.id_exame AND procedimento_exame.id_procedimento =' . $result_array [0] ['id'];
				$query = $this->db->query( $sql2 );
				return ProcedimentoValorMapper::getProcedimentoValorDetailsVMFromArray( $result_array [0], $query->getResultArray() );
			} );

	}

	public function getExames(int $id_procedimento) {

		$sql = 'SELECT exame.id AS id, exame.nome AS nome FROM procedimento_exame, exame
            WHERE exame.id = procedimento_exame.id_exame AND procedimento_exame.id_procedimento =' . $id_procedimento;
		$binds = [ ];
		$query = $this->db->query( $sql );
		return $query->getResultArray();

	}

	# --------------------------------------------------------------------------------------------------------------#
	public function persistProcedimentoValorUpdateVM(int $id_procedimento, ProcedimentoValorUpdateViewModel $procedimento_update_vm): DAOResponse {

		$this->db->transStart();
		$status_persistence = $this->persistProcedimentoValorFromProcedimentoValorUpdateVM( $id_procedimento, $procedimento_update_vm );
		$error = $this->db->error();
		// $this->db->close();
		$dao_response = new DAOResponse();
		if ($error && $error ['code']) {
			$status_response = $error ['code'];
			$dao_response_errors = [ ];
			if ($status_response === DAOResponseInterface::FOREIGN_KEY_DONT_EXISTS) {
				if (strpos( $error ['message'], 'fk_procedimento_id_tipo_procedimento' ) !== false) {
					$dao_response_errors ['id_tipo_procedimento'] = 'Esse tipo de procedimento não existe.';
				}
			}
			$dao_response->setErrors( $dao_response_errors );
			(new LogService())->log_error( $status_response, "Ocorreu um erro durante a atualização do procedimento $id_procedimento",
				$procedimento_update_vm->toArray() );
		} else {
			$this->db->transComplete();
			$status_response = $status_persistence ? DAOResponseInterface::SUCESS : DAOResponseInterface::RECORD_NOT_FOUND;
		}
		$dao_response->setStatus( $status_response );
		return $dao_response;

	}

	private function persistProcedimentoValorFromProcedimentoValorUpdateVM(int $id_procedimento, ProcedimentoValorUpdateViewModel $procedimento_update_vm): bool {

		$procedimento_model = new ProcedimentoValorModel();
		$procedimento = ProcedimentoValorMapper::getProcedimentoValorFromProcedimentoValorUpdateVM( $procedimento_update_vm );
		$sucess_update = $procedimento_model->updateProcedimentoValorByID( $id_procedimento, $procedimento );
		return $sucess_update;

	}

	private function deleteProcedimentoValorExame(int $id_procedimento): DAOResponse {

		$sql = "
            DELETE procedimento_exame FROM procedimento_exame
            WHERE procedimento_exame.id_procedimento = :id_procedimento:";

		$binds = [ 
			'id_procedimento' => $id_procedimento
		];
		$query = $this->db->query( $sql, $binds );
		$affected_rows = $query->connID->affected_rows;
		$dao_response = new DAOResponse();
		if ($affected_rows < 0) {
			$dao_response->setStatus( DAOResponseInterface::ERROR_DELETING );
			return $dao_response;
		} else {
			$dao_response->setStatus( DAOResponseInterface::SUCESS );
			return $dao_response;
		}

	}

	public function getProcedimentosValorByUnidadeID(int $id_unidade): DAOResponse {

		$dao_response = new DAOResponse();

		$sql = <<<SQL
			select distinct
				un.id as id_unidade, un.descricao as unidade, tp.id as id_tipo_procedimento,
				tp.descricao as tipo_procedimento, p.id as id_procedimento,
				p.descricao as procedimento, tc.id as id_tipo_convenio, 
				tc.descricao as tipo_convenio, c.id as id_convenio, 
				c.codigo as id_convenio, pv.id_convenio_procedimento,
				pv.id as id_procediemto_valor, pv.valor
			from
				unidade un, convenio_procedimento cp , convenio c,
				tipo_convenio tc , procedimento p, tipo_procedimento tp,
				procedimento_valor pv
			where
				un.id = :unidade_id:
				and un.id = cp.id_unidade				
				and cp.id_convenio = c.id
				and c.id_tipo_convenio = tc.id
				and cp.id_procedimento = p.id
				and p.id_tipo_procedimento = tp.id
				and cp.id = pv.id_convenio_procedimento
				and pv.status = 1
				and pv.data_fim ='1900-01-01'
		SQL;

		$binds = [ 
			'unidade_id' => $id_unidade
		];

		$conn = db_connect();
		$result = $conn->query( $sql, $binds )
			->getResultArray();
		$errors = $conn->error();

		if ($errors ['code'] != 0) {
			$dao_response->setStatus( DAOResponseInterface::DATABASE_CONNECTION_ERROR );
			$dao_response->setErrors( $errors );
		} else {
			if (count( $result ) > 0) {
				$dao_response->setStatus( DAOResponseInterface::SUCESS );
				$dao_response->setData( $result );
			} else {
				$dao_response->setStatus( DAOResponseInterface::RECORD_NOT_FOUND );
				$dao_response->setData( [ ] );
			}
		}

		return $dao_response;

	}

	public function getDadosProcedimentoValorByID(int $id_procedimentovalor): DAOResponse {

		$dao_response = new DAOResponse();

		$sql = <<<SQL
			select distinct
				p.id as id_procedimento,p.descricao as procedimento,
				tp.id as id_tipo_procedimento, tp.descricao as tipo_procedimento,
				pv.id_convenio_procedimento , cp.id_unidade as id_unidade,
				c.id as id_convenio, c.codigo as cod_convenio,
				tc.id as id_tipo_convenio, tc.descricao as tipo_convenio,
				pv.valor , pv.valor_interno, pv.status as status_procedimento_valor,
				pv.data_inicio, pv.data_fim
			from
				convenio_procedimento cp , procedimento p, tipo_procedimento tp,
				procedimento_valor pv, convenio c , tipo_convenio tc
			where
				pv.id = :procedimentovalor_id:
				and pv.id_convenio_procedimento = cp.id
				and cp.id_procedimento = p.id
				and p.id_tipo_procedimento = tp.id
				and cp.id_convenio = c.id
				and c.id_tipo_convenio = tc.id
		SQL;

		$binds = [ 
			'procedimentovalor_id' => $id_procedimentovalor
		];

		$conn = db_connect();
		$result = $conn->query( $sql, $binds )
			->getResultArray();
		$errors = $conn->error();

		if ($errors ['code'] != 0) {
			$dao_response->setStatus( DAOResponseInterface::DATABASE_CONNECTION_ERROR );
			$dao_response->setErrors( $errors );
		} else {
			if (count( $result ) > 0) {
				$dao_response->setStatus( DAOResponseInterface::SUCESS );
				$dao_response->setData( $result [0] );
			} else {
				$dao_response->setStatus( DAOResponseInterface::RECORD_NOT_FOUND );
				$dao_response->setData( [ ] );
			}
		}

		return $dao_response;

	}

	public function getDetalhesProcedimentoValorByID(int $id_procedimentovalor): DAOResponse {

		$dao_response = new DAOResponse();

		$sql = <<<SQL
			select distinct
				p.id as id_procedimento,p.descricao as procedimento,
				tp.id as id_tipo_procedimento, tp.descricao as tipo_procedimento,
				pv.id_convenio_procedimento, c.id as id_convenio , c.codigo,
				c.descricao as convenio, tc.id as id_tipo_convenio,
				tc.descricao as tipo_convenio , pv.valor , pv.valor_interno,
				pv.status, pv.data_inicio, pv.data_fim
			from
				procedimento_valor pv, convenio_procedimento cp , procedimento p,
				tipo_procedimento tp , convenio c , tipo_convenio tc
			where
				pv.id = :procedimentovalor_id:
				and pv.id_convenio_procedimento = cp.id
				and cp.id_convenio = c.id
				and c.id_tipo_convenio =tc.id
				and cp.id_procedimento = p.id
				and p.id_tipo_procedimento = tp.id 
		SQL;

		$binds = [ 
			'procedimentovalor_id' => $id_procedimentovalor
		];

		$conn = db_connect();
		$result = $conn->query( $sql, $binds )
			->getResultArray();
		$errors = $conn->error();

		if ($errors ['code'] != 0) {
			$dao_response->setStatus( DAOResponseInterface::DATABASE_CONNECTION_ERROR );
			$dao_response->setErrors( $errors );
		} else {
			if (count( $result ) > 0) {
				$dao_response->setStatus( DAOResponseInterface::SUCESS );
				$dao_response->setData( $result [0] );
			} else {
				$dao_response->setStatus( DAOResponseInterface::RECORD_NOT_FOUND );
				$dao_response->setData( [ ] );
			}
		}

		return $dao_response;

	}

}