<?php

namespace App\DAO;

use App\DAO\DAOResponse\DAOResponse;
use App\DAO\DAOResponse\DAOResponseInterface;
use App\DAO\Pagination\Pagination;
use App\Entities\Procedimento\Procedimento;
use App\Entities\ProcedimentoExame\ProcedimentoExame;
use App\Mappers\ProcedimentoMapper;
use App\Models\ProcedimentoExameModel;
use App\Models\ProcedimentoModel;
use App\Services\LogService;
use App\Services\ServiceResponse\ServiceResponse;
use App\Services\ServiceResponse\ServiceResponseInterface;
use App\Utils\Collections\QueryFilterCollection;
use App\Utils\Collections\QueryOrderCollection;
use App\ViewModels\Procedimento\ProcedimentoRegistrationViewModel\ProcedimentoRegistrationViewModel;
use App\ViewModels\Procedimento\ProcedimentoUpdateViewModel\ProcedimentoUpdateViewModel;
// use Cassandra\Varint;
use CodeIgniter\Model;
use Exception;

class ProcedimentoDAO extends DAO {

	public function persistProcedimentoRegistrationVM(ProcedimentoRegistrationViewModel $procedimento_registration_vm): DAOResponse {

		$dao_response = new DAOResponse();
		try {
			$this->db->transStart();
			$procedimento = $this->persistProcedimento( $procedimento_registration_vm );
			$pog = $procedimento_registration_vm->toArray();
			if (!empty( $pog ['exame'] )) {
				$procedimento_exame = $this->persistProcedimentoExame( $procedimento_registration_vm, $procedimento );
			}

			$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 persistProcedimento(ProcedimentoRegistrationViewModel $procedimento_registration_vm): Procedimento {

		$procedimento_model = new ProcedimentoModel();

		$procedimento = ProcedimentoMapper::getProcedimentoFromProcedimentoRegistrationVM( $procedimento_registration_vm );
		$procedimento_model->registerProcedimento( $procedimento );
		return $procedimento;

	}

	private function persistProcedimentoExame(ProcedimentoRegistrationViewModel $procedimento_registration_vm, Procedimento $procedimento): ProcedimentoExame {

		$procedimento_model = new ProcedimentoExameModel();
		$array_prod = $procedimento_registration_vm->toArray();
		foreach ( $array_prod ['exame'] as $id ) {
			$procedimento_exame = ProcedimentoMapper::getProcedimentoExameFromProcedimentoRegistrationVM( $id, $procedimento->getId() );
			$procedimento_model->registerProcedimentoExame( $procedimento_exame );
		}
		return $procedimento_exame;

	}

	# -------------------------------------------------------------------------------------------------------------------#
	public function getProcedimentoList(?Pagination $pagination, ?QueryFilterCollection $query_filter_collection,
			?QueryOrderCollection $query_order_collection = null): 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,
                tipo_procedimento
            WHERE 
                  procedimento.id_tipo_procedimento = tipo_procedimento.id
        ';
		return $this->getDAOResponseFromPaginatedSelect( [ ], $pagination, $query_filter_collection, $query_order_collection, $sql,
			function (array $result_array) {
				return ProcedimentoMapper::getProcedimentoDetailsVMCollectionFromArray( $result_array );
			} );

	}

	public function getProcedimentoConsultaList(?Pagination $pagination, ?QueryFilterCollection $query_filter_collection,
			?QueryOrderCollection $query_order_collection = null, $unidade_id): DAOResponse {

		$sql = '
            SELECT distinct
                procedimento.id AS id,
                procedimento.descricao AS descricao,
                tipo_procedimento.id AS id_tipo_procedimento,
                tipo_procedimento.descricao as tipo_procedimento
            FROM
                procedimento,
                tipo_procedimento,
                convenio_procedimento,
                procedimento_valor
            WHERE 
                tipo_procedimento.id = 2
              	and procedimento.id_tipo_procedimento = tipo_procedimento.id
              	and procedimento.id  = convenio_procedimento.id_procedimento 
              	and convenio_procedimento.id = procedimento_valor.id_convenio_procedimento
				and procedimento_valor.status = 1              	
              	and convenio_procedimento.id_unidade  = :unidade_id:
        ';
		$binds = [ 
			"unidade_id" => $unidade_id
		];

		return $this->getDAOResponseFromPaginatedSelect( $binds, $pagination, $query_filter_collection, $query_order_collection, $sql,
			function (array $result_array) {
				return ProcedimentoMapper::getProcedimentoDetailsVMCollectionFromArray( $result_array );
			} );

	}

	public function getProcedimentoExameList(?Pagination $pagination, ?QueryFilterCollection $query_filter_collection,
			?QueryOrderCollection $query_order_collection = null, $unidade_id): DAOResponse {

		$sql = '
            SELECT distinct
                procedimento.id AS id,
                procedimento.descricao AS descricao,
                tipo_procedimento.id AS id_tipo_procedimento,
                tipo_procedimento.descricao as tipo_procedimento
            FROM
                procedimento,
                tipo_procedimento,
                convenio_procedimento,
				procedimento_valor
            WHERE 
                tipo_procedimento.id = 4
                and procedimento.id_tipo_procedimento = tipo_procedimento.id
                and procedimento.id  = convenio_procedimento.id_procedimento
				and convenio_procedimento.id = procedimento_valor.id_convenio_procedimento
				and procedimento_valor.status = 1                
                and convenio_procedimento.id_unidade  = :unidade_id:
        ';
		$binds = [ 
			"unidade_id" => $unidade_id
		];
		return $this->getDAOResponseFromPaginatedSelect( $binds, $pagination, $query_filter_collection, $query_order_collection, $sql,
			function (array $result_array) {
				return ProcedimentoMapper::getProcedimentoDetailsVMCollectionFromArray( $result_array );
			} );

	}
	
	public function getProcedimentoHigieneList(?Pagination $pagination, ?QueryFilterCollection $query_filter_collection,
	    ?QueryOrderCollection $query_order_collection = null, $unidade_id): DAOResponse {
	        
	        $sql = '
            SELECT distinct
                procedimento.id AS id,
                procedimento.descricao AS descricao,
                tipo_procedimento.id AS id_tipo_procedimento,
                tipo_procedimento.descricao as tipo_procedimento
            FROM
                procedimento,
                tipo_procedimento,
                convenio_procedimento,
				procedimento_valor
            WHERE
                tipo_procedimento.id = 9
                and procedimento.id_tipo_procedimento = tipo_procedimento.id
                and procedimento.id  = convenio_procedimento.id_procedimento
				and convenio_procedimento.id = procedimento_valor.id_convenio_procedimento
				and procedimento_valor.status = 1
                and convenio_procedimento.id_unidade  = :unidade_id:
        ';
	        $binds = [
	            "unidade_id" => $unidade_id
	        ];
	        return $this->getDAOResponseFromPaginatedSelect( $binds, $pagination, $query_filter_collection, $query_order_collection, $sql,
	            function (array $result_array) {
	                return ProcedimentoMapper::getProcedimentoDetailsVMCollectionFromArray( $result_array );
	            } );
	        
	}

	public function getProcedimentoInternacaoList(?Pagination $pagination, ?QueryFilterCollection $query_filter_collection,
			?QueryOrderCollection $query_order_collection = null, $unidade_id): DAOResponse {

		$sql = <<<SQL
		    SELECT distinct
		        procedimento.id AS id,
		        procedimento.descricao AS descricao,
		        tipo_procedimento.id AS id_tipo_procedimento,
		        tipo_procedimento.descricao as tipo_procedimento
		    FROM
		        procedimento,
		        tipo_procedimento,
		        convenio_procedimento,
				procedimento_valor
		    WHERE
		        tipo_procedimento.id = 6
		        and procedimento.id_tipo_procedimento = tipo_procedimento.id
		      	and procedimento.id  = convenio_procedimento.id_procedimento
				and convenio_procedimento.id = procedimento_valor.id_convenio_procedimento
				and procedimento_valor.status = 1
		      	and convenio_procedimento.id_unidade  = :unidade_id:
		        and procedimento.id not in (969,970,971)
		SQL;
		$binds = [ 
			"unidade_id" => $unidade_id
		];
		return $this->getDAOResponseFromPaginatedSelect( $binds, $pagination, $query_filter_collection, $query_order_collection, $sql,
			function (array $result_array) {
				return ProcedimentoMapper::getProcedimentoDetailsVMCollectionFromArray( $result_array );
			} );

	}

	public function getProcedimentoMedicacaoList(?Pagination $pagination, ?QueryFilterCollection $query_filter_collection,
			?QueryOrderCollection $query_order_collection = null, $unidade_id): DAOResponse {

		$sql = <<<SQL
		    SELECT distinct
		        procedimento.id AS id,
		        procedimento.descricao AS descricao,
		        tipo_procedimento.id AS id_tipo_procedimento,
		        tipo_procedimento.descricao as tipo_procedimento
		    FROM
		        procedimento,
		        tipo_procedimento,
		        convenio_procedimento,
				procedimento_valor
		    WHERE
		        tipo_procedimento.id = 5
		        and procedimento.id_tipo_procedimento = tipo_procedimento.id
		        and procedimento.id  = convenio_procedimento.id_procedimento
				and convenio_procedimento.id = procedimento_valor.id_convenio_procedimento 
		        and procedimento_valor.status = 1
		        and convenio_procedimento.id_unidade  = :unidade_id:
		SQL;
		$binds = [ 
			"unidade_id" => $unidade_id
		];
		return $this->getDAOResponseFromPaginatedSelect( $binds, $pagination, $query_filter_collection, $query_order_collection, $sql,
			function (array $result_array) {
				return ProcedimentoMapper::getProcedimentoDetailsVMCollectionFromArray( $result_array );
			} );

	}

	public function getProcedimentoCirurgiaList(?Pagination $pagination, ?QueryFilterCollection $query_filter_collection,
			?QueryOrderCollection $query_order_collection = null, $unidade_id): DAOResponse {

		$sql = <<<SQL
		    SELECT distinct
		        procedimento.id AS id,
		        procedimento.descricao AS descricao,
		        tipo_procedimento.id AS id_tipo_procedimento,
		        tipo_procedimento.descricao as tipo_procedimento
		    FROM
		        procedimento,
		        tipo_procedimento,
		        convenio_procedimento,
				procedimento_valor
		    WHERE
		        tipo_procedimento.id = 3
		        and procedimento.id_tipo_procedimento = tipo_procedimento.id
		        and procedimento.id  = convenio_procedimento.id_procedimento 
				and convenio_procedimento.id = procedimento_valor.id_convenio_procedimento
		        and procedimento_valor.status = 1
		        and convenio_procedimento.id_unidade  = :unidade_id:
		SQL;
		$binds = [ 
			"unidade_id" => $unidade_id
		];
		return $this->getDAOResponseFromPaginatedSelect( $binds, $pagination, $query_filter_collection, $query_order_collection, $sql,
			function (array $result_array) {
				return ProcedimentoMapper::getProcedimentoDetailsVMCollectionFromArray( $result_array );
			} );

	}

	public function deleteProcedimento(int $id_procedimento): DAOResponse {

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

	}

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

		$sql = '
            SELECT distinct
                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 ProcedimentoMapper::getProcedimentoDetailsVMFromArray( $result_array [0], $query->getResultArray() );
			} );

	}

	public function getExames(int $id_procedimento) {

		$sql = 'SELECT distinct 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 getExamesFromProcedimentos(array $procedimentos) {

		$values = '(' . implode( ',', $procedimentos ) . ')';
		$sql = <<<SQL
		    SELECT distinct
		        exame.id AS id, 
		        exame.nome AS nome,
		        procedimento_exame.id_procedimento as procedimento_id 
		    FROM procedimento_exame, exame
		    WHERE 1=1
		        AND exame.id = procedimento_exame.id_exame 
		        AND procedimento_exame.id_procedimento in $values
		SQL;
		$query = $this->db->query( $sql );
		return $query->getResultArray();

	}

	# --------------------------------------------------------------------------------------------------------------#
	public function persistProcedimentoUpdateVM(int $id_procedimento, ProcedimentoUpdateViewModel $procedimento_update_vm): DAOResponse {

		$this->db->transStart();
		$status_persistence = $this->persistProcedimentoFromProcedimentoUpdateVM( $id_procedimento, $procedimento_update_vm );
		$pog = $procedimento_update_vm->toArray();
		if (!empty( $pog ['exames'] )) {
			$status_persistence_exame = $this->persistProcedimentoExameFromProcedimentoUpdateVM( $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.';
				}
				if (strpos( $error ['message'], 'fk_procedimento_alterado_por' ) !== false) {
					$dao_response_errors ['alterado_por'] = 'Esse usuário não existe.';
				}
			}
			if ($status_response === DAOResponseInterface::DUPLICATED_UNIQUE_KEY && strpos( $error ['message'], 'procedimento.descricao' ) !== false) {
				$dao_response_errors ['descricao'] = 'Essa descrição de procedimento já 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 persistProcedimentoFromProcedimentoUpdateVM(int $id_procedimento, ProcedimentoUpdateViewModel $procedimento_update_vm): bool {

		$procedimento_model = new ProcedimentoModel();
		$procedimento = ProcedimentoMapper::getProcedimentoFromProcedimentoUpdateVM( $procedimento_update_vm );
		$sucess_update = $procedimento_model->updateProcedimentoByID( $id_procedimento, $procedimento );
		return $sucess_update;

	}

	private function persistProcedimentoExameFromProcedimentoUpdateVM(int $id_procedimento, ProcedimentoUpdateViewModel $procedimento_update_vm): bool {

		$procedimento_model = new ProcedimentoExameModel();
		$service_response = new ServiceResponse();
		$dao_response = $this->deleteProcedimentoExame( $id_procedimento );
		switch ($dao_response->getStatus()) {
			case DAOResponseInterface::SUCESS :
				break;
			case DAOResponseInterface::ERROR_DELETING :
				$service_response->setStatus( ServiceResponseInterface::ERROR_DELETING );
				return false;
			default :
				$service_response->setStatus( ServiceResponseInterface::DATABASE_CONNECTION_ERROR );
				return false;
		}
		foreach ( $procedimento_update_vm->getExames() as $id ) {
			$procedimento_exame = ProcedimentoMapper::getProcedimentoExameFromProcedimentoRegistrationVM( $id, $id_procedimento );
			$procedimento_model->registerProcedimentoExame( $procedimento_exame );
		}
		return true;

	}

	private function deleteProcedimentoExame(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 getProcedimentoByUnidadeByTipoConvenioId(int $unidadeId, int $tipoConvenio): DAOResponse {

		$sql = <<<SQL
			select
				p.id as procedimento_id, p.descricao as procedimento_descricao,
				p.id_tipo_procedimento as tipo_procedimento_id ,
				tp.descricao as tipo_procedimento_descricao, cp.id_unidade as unidade_id,
				cp.id as convenio_procedimento_id , cp.id_convenio as convenio_id ,
				tc.id tipo_convenio_id, tc.descricao as tipo_convenio_descricao, pv.status
			from
				convenio_procedimento cp , procedimento_valor pv, procedimento p ,
				tipo_procedimento tp , convenio c , tipo_convenio tc
			where
				cp.id_unidade = :unidade_id:
				and cp.id = pv.id_convenio_procedimento
				and pv.status = 1
				and cp.id_procedimento = p.id
				and cp.id_convenio = c.id
				and c.id_tipo_convenio = tc.id
				and c.id_tipo_convenio = :convenio_tipo_id:
				and p.id_tipo_procedimento = tp.id
		SQL;

		$binds = [ 
			'unidade_id' => $unidadeId,
			'convenio_tipo_id' => $tipoConvenio
		];

		$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 {
			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 getProcedimentoByUnidadeByTipoId(int $unidadeId, int $tipoProcedimento): DAOResponse {

		$sql = <<<SQL
			select
				p.id as procedimento_id, p.descricao as procedimento_descricao,
				p.id_tipo_procedimento as tipo_procedimento_id ,
				tp.descricao as tipo_procedimento_descricao, cp.id_unidade as unidade_id, 
				cp.id as convenio_procedimento_id , cp.id_convenio as convenio_id ,
				tc.id tipo_convenio_id, tc.descricao as tipo_convenio_descricao, pv.status
			from
				convenio_procedimento cp , procedimento_valor pv, procedimento p ,
				tipo_procedimento tp , convenio c , tipo_convenio tc
			where
				cp.id_unidade = :unidade_id:
				and cp.id = pv.id_convenio_procedimento
				and pv.status = 1
				and cp.id_procedimento = p.id
				and cp.id_convenio = c.id
				and c.id_tipo_convenio = tc.id
				and p.id_tipo_procedimento = tp.id
				and tp.id = :procedimento_tipo_id:
		SQL;
		
		$binds = [
				'unidade_id' => $unidadeId,
				'procedimento_tipo_id' => $tipoProcedimento
		];
		
		$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 {
			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;

	}

}