<?php

namespace App\Services;

use App\DAO\PacienteDAO;
use App\DAO\DAOResponse\DAOResponseInterface;
use App\DAO\Filter\QueryFilter;
use App\DAO\Pagination\Pagination;
use App\Entities\Paciente\Paciente;
use App\Mappers\PacienteMapper;
use App\Services\ServiceResponse\ServiceResponse;
use App\Services\ServiceResponse\ServiceResponseInterface;
use App\Utils\ResponseException;
use App\Utils\Collections\QueryFilterCollection;
use App\Utils\Collections\QueryOrderCollection;
use App\ViewModels\Paciente\PacienteRegistrationViewModel\PacienteRegistrationViewModel;

class PacienteService extends Service {

	public function registerPaciente(array $data): ServiceResponse {

		try {
			$passwd = $this->generateRandomString( 10 );
			// $data ['senha'] = $this->cript_password( trim( $data ['senha'] ) );
			if (strlen( trim( $data ['senha'] ) ) > 0) {
				$passwd = $this->cript_password( trim( $data ['senha'] ) );
			}
			$data ['senha'] = $passwd;
			$data ['token_confirmacao_email'] = '';
			$data ['ativo'] = 1;

			if (isset( $_FILES ['foto'] )) {
				$ext = strtolower( substr( $_FILES ['foto'] ['name'], -4 ) ); // Pegando extensão do arquivo
				$new_name = date( "Y.m.d-H.i.s" ) . $ext; // Definindo um novo nome para o arquivo
				$dir = '../writable/perfil/';
				move_uploaded_file( $_FILES ['foto'] ['tmp_name'], $dir . $new_name ); // Fazer upload do arquivo
			}
			$data ['foto'] = $dir . $new_name;

			$paciente_registration_vm = PacienteMapper::PacienteRegistrationViewModelMapper( $data );
		} catch ( ResponseException $response_exception ) {
			$log_service = new LogService();
			$log_service->log_error( ServiceResponseInterface::SERVICE_INTERNAL_ERROR,
				'Ocorreu um erro durante o mapeamento da entidade PacienteRegistrationViewModel', $response_exception->getData() );
			return ServiceResponse::builder()->setStatus( ServiceResponseInterface::SERVICE_INTERNAL_ERROR )
				->setErrors( $response_exception->getData() )
				->build();
		}
		$paciente_dao = new PacienteDAO();
		$dao_response = $paciente_dao->persistPacienteRegistrationVM( $paciente_registration_vm,
			function () use ($paciente_registration_vm, $passwd) {
				return $this->sendEmailConfirmationRegister( $paciente_registration_vm, $passwd );
			} );
		$service_response = new ServiceResponse();
		switch ($dao_response->getStatus()) {
			case DAOResponseInterface::SUCESS :
				$service_response->setStatus( ServiceResponseInterface::SUCESS );
				$paciente = (function (Paciente $paciente): Paciente {
					return $paciente;
				})( $dao_response->getData() );
				$service_response->setData( [ 
					'id_paciente' => $paciente->getId()
				] );
				break;
			case DAOResponseInterface::FOREIGN_KEY_DONT_EXISTS :
				if (file_exists( $dir . $new_name ) === true) {
					unlink( $dir . $new_name );
				}
				break;
			case DAOResponseInterface::DUPLICATED_UNIQUE_KEY :
				$service_response->setStatus( ServiceResponseInterface::VALIDATION_PARAMS_ERROR );
				$service_response->setErrors( $dao_response->getErrors() );
				if (file_exists( $dir . $new_name ) === true) {
					unlink( $dir . $new_name );
				}
				break;
			case DAOResponseInterface::INTERNAL_DAO_ERROR :
				$service_response->setStatus( ServiceResponseInterface::SERVICE_INTERNAL_ERROR );
				if (file_exists( $dir . $new_name ) === true) {
					unlink( $dir . $new_name );
				}
				break;
			default :
				$service_response->setStatus( ServiceResponseInterface::DATABASE_CONNECTION_ERROR );
				if (file_exists( $dir . $new_name ) === true) {
					unlink( $dir . $new_name );
				}
				break;
		}
		return $service_response;

	}

	public function selfRegisterPaciente(array $data): ServiceResponse {

		// TODO: Uma melhoria a se fazer nesse serviço é tanto o método selfRegisterPaciente quanto registerPaciente reutilizarem um mesmo
		// método privado a fim de garantir a mesma lógica de cadastro.
		try {
			$token = $this->generateToken();
			$email = $data ['email'];
			$data ['senha'] = $this->cript_password( $data ['senha'] );
			$data ['token_confirmacao_email'] = $token;
			$data ['ativo'] = 1;
			$data ['adicionado_por'] = null;

			if (isset( $_FILES ['foto'] )) {
				$ext = strtolower( substr( $_FILES ['foto'] ['name'], -4 ) ); // Pegando extensão do arquivo
				$new_name = date( "Y.m.d-H.i.s" ) . $ext; // Definindo um novo nome para o arquivo
				$dir = '../writable/perfil/';
				move_uploaded_file( $_FILES ['foto'] ['tmp_name'], $dir . $new_name ); // Fazer upload do arquivo
				$data ['foto'] = $dir . $new_name;
			}

			$paciente_registration_vm = PacienteMapper::PacienteRegistrationViewModelMapper( $data );
		} catch ( ResponseException $response_exception ) {
			$log_service = new LogService();
			$log_service->log_error( ServiceResponseInterface::SERVICE_INTERNAL_ERROR,
				'Ocorreu um erro durante o mapeamento da entidade PacienteRegistrationViewModel', $response_exception->getData() );
			return ServiceResponse::builder()->setStatus( ServiceResponseInterface::SERVICE_INTERNAL_ERROR )
				->setErrors( $response_exception->getData() )
				->build();
		}
		$paciente_dao = new PacienteDAO();
		$dao_response = $paciente_dao->persistPacienteRegistrationVM( $paciente_registration_vm,
			function () use ($paciente_registration_vm, $token, $email) {
				return $this->sendEmailConfirmationSelfRegister( $paciente_registration_vm, $email );
			} );
		$service_response = new ServiceResponse();
		switch ($dao_response->getStatus()) {
			case DAOResponseInterface::SUCESS :

				$service_response->setStatus( ServiceResponseInterface::SUCESS );
				$paciente = (function (Paciente $paciente): Paciente {
					return $paciente;
				})( $dao_response->getData() );
				$svc_response = new ServiceResponse();
				$svc_response = $this->getClienteIdByPacienteId( $paciente->getId() );
				if ($svc_response->getStatus() == DAOResponseInterface::SUCESS) {
					$cliente = $svc_response->getData();
					$service_response->setData( [ 
						'id_paciente' => $paciente->getId(),
						'id_cliente' => intval( $cliente ['cliente_id'] )
					] );
				} else {
					$service_response->setData( [ 
						'id_paciente' => $paciente->getId()
					] );
				}

				break;
			case DAOResponseInterface::FOREIGN_KEY_DONT_EXISTS :
				if (file_exists( $dir . $new_name ) === true) {
					unlink( $dir . $new_name );
				}
				break;
			case DAOResponseInterface::DUPLICATED_UNIQUE_KEY :
				$service_response->setStatus( ServiceResponseInterface::VALIDATION_PARAMS_ERROR );
				$service_response->setErrors( $dao_response->getErrors() );
				if (file_exists( $dir . $new_name ) === true) {
					unlink( $dir . $new_name );
				}
				break;
			case DAOResponseInterface::INTERNAL_DAO_ERROR :
				$service_response->setStatus( ServiceResponseInterface::SERVICE_INTERNAL_ERROR );
				if (file_exists( $dir . $new_name ) === true) {
					unlink( $dir . $new_name );
				}
				break;
			default :
				$service_response->setStatus( ServiceResponseInterface::DATABASE_CONNECTION_ERROR );
				if (file_exists( $dir . $new_name ) === true) {
					unlink( $dir . $new_name );
				}
				break;
		}
		return $service_response;

	}

	public function getPacienteList(?Pagination $pagination, ?QueryFilterCollection $query_filter_collection,
			?QueryOrderCollection $query_order_collection = null): ServiceResponse {

		$paciente_dao = new PacienteDAO();
		$dao_response = $paciente_dao->getPacienteList( $pagination, $query_filter_collection, $query_order_collection );
		switch ($dao_response->getStatus()) {
			case DAOResponseInterface::SUCESS :
				$service_response_status = ServiceResponseInterface::SUCESS;
				break;
			case DAOResponseInterface::RECORD_NOT_FOUND :
				$service_response_status = ServiceResponseInterface::RECORD_NOT_FOUND;
				break;
			case DAOResponseInterface::DATABASE_CONNECTION_ERROR :
				$service_response_status = ServiceResponseInterface::DATABASE_CONNECTION_ERROR;
				break;
		}
		$service_response = ServiceResponse::builder()->setStatus( $service_response_status )
			->build();
		if ($service_response_status === DAOResponseInterface::SUCESS) {
			$dao_response_data = $dao_response->getData();
			$paciente_details_array = PacienteMapper::getPacientesDetailsArrayFromPacienteDetailsVMList( $dao_response_data );
			$pagination = $dao_response->getPagination();
			$data = [ 
				'pacientes' => $paciente_details_array,
				'pagination' => $pagination ? $pagination->toResponseArray() : null
			];
			$service_response->setData( $data );
		}
		return $service_response;

	}

	public function getPaciente(QueryFilter $query_filter): ServiceResponse {

		$paciente_dao = new PacienteDAO();
		$query_filter_collection = new QueryFilterCollection();
		$query_filter_collection->push( $query_filter );
		$dao_response = $paciente_dao->getPaciente( $query_filter_collection );
		switch ($dao_response->getStatus()) {
			case DAOResponseInterface::SUCESS :
				$service_response_status = ServiceResponseInterface::SUCESS;
				break;
			case DAOResponseInterface::RECORD_NOT_FOUND :
				$service_response_status = ServiceResponseInterface::RECORD_NOT_FOUND;
				break;
			case DAOResponseInterface::DATABASE_CONNECTION_ERROR :
				$service_response_status = ServiceResponseInterface::DATABASE_CONNECTION_ERROR;
				break;
		}
		$service_response = ServiceResponse::builder()->setStatus( $service_response_status )
			->build();
		if ($service_response_status === DAOResponseInterface::SUCESS) {
			$service_response->setData( $dao_response->getData()
				->toArray() );
		}
		return $service_response;

	}

	private function sendEmailConfirmationRegister(PacienteRegistrationViewModel $paciente_registration_vm, string $passwd): int {

		$email_service = new EmailService();
		$data = [ 
			'senha' => $passwd,
			'email' => $paciente_registration_vm->getEmail()
		];
		$html_email = view( 'emails/paciente_adm_registration', $data );
		$email_service_response = $email_service->send_email( $paciente_registration_vm->getEmail(), 'Confirmação de email', $html_email );
		return $email_service_response->getStatus() === ServiceResponseInterface::SUCESS ? ServiceResponseInterface::SUCESS : ServiceResponseInterface::SERVICE_INTERNAL_ERROR;

	}

	private function sendEmailConfirmationSelfRegister(PacienteRegistrationViewModel $paciente_registration_vm, string $token): int {

		$email_service = new EmailService();
		$html_email = view( 'emails/paciente_self_registration', [ 
			'email' => $token
		] );
		$email_service_response = $email_service->send_email( $paciente_registration_vm->getEmail(), 'Confirmação de email', $html_email );

		return $email_service_response->getStatus() === ServiceResponseInterface::SUCESS ? ServiceResponseInterface::SUCESS : ServiceResponseInterface::SERVICE_INTERNAL_ERROR;

	}

	private function getClienteIdByPacienteId(int $idPaciente): ServiceResponse {

		$dao = new PacienteDAO();
		$daoResponse = $dao->getClienteIdByPacienteId( $idPaciente );
		$service_response = new ServiceResponse();
		$service_response = $this->setServiceResponse( $daoResponse );
		return $service_response;

	}

	public function updatePacienteData(int $id_paciente, array $data): ServiceResponse {

		try {
			$paciente_update_vm = PacienteMapper::getPacienteUpdateVMFromArray( $data );
			if (count( $paciente_update_vm->toArray( true ) ) === 0) {
				return ServiceResponse::builder()->setStatus( ServiceResponseInterface::INSUFFICIENT_DATA )
					->build();
			}
		} catch ( ResponseException $response_exception ) {
			$log_service = new LogService();
			$log_service->log_error( ServiceResponseInterface::SERVICE_INTERNAL_ERROR,
				'Ocorreu um erro durante o mapeamento da entidade PacienteUpdateViewModel', $response_exception->getData() );
			return ServiceResponse::builder()->setStatus( ServiceResponseInterface::SERVICE_INTERNAL_ERROR )
				->setErrors( $response_exception->getData() )
				->build();
		}

		$paciente_dao = new PacienteDAO();
		$image = $paciente_dao->getImagePaciente( $id_paciente );
		$dao_response = $paciente_dao->persistPacienteUpdateVM( $id_paciente, $paciente_update_vm );
		$service_response = new ServiceResponse();

		switch ($dao_response->getStatus()) {
			case DAOResponseInterface::SUCESS :
				$service_response->setStatus( ServiceResponseInterface::SUCESS );
				if (isset( $_FILES ['foto'] ) && file_exists( $image ) === true) {
					unlink( $image );
				}
				break;
			case DAOResponseInterface::FOREIGN_KEY_DONT_EXISTS :
				$service_response->setStatus( ServiceResponseInterface::VALIDATION_PARAMS_ERROR );
				$service_response->setErrors( $dao_response->getErrors() );
				break;
			case DAOResponseInterface::DATABASE_CONNECTION_ERROR :
				$service_response->setStatus( ServiceResponseInterface::DATABASE_CONNECTION_ERROR );
				break;
		}
		return $service_response;

	}

	# ----------------------------------------------------------------------------------------------------------------#
	public function updatePacienteObservacao(int $id_paciente, array $data): ServiceResponse {

		try {
			$paciente_update_vm = PacienteMapper::getPacienteUpdateObservacaoVMFromArray( $data );
			if (count( $paciente_update_vm->toArray( true ) ) === 0) {
				return ServiceResponse::builder()->setStatus( ServiceResponseInterface::INSUFFICIENT_DATA )
					->build();
			}
		} catch ( ResponseException $response_exception ) {
			$log_service = new LogService();
			$log_service->log_error( ServiceResponseInterface::SERVICE_INTERNAL_ERROR,
				'Ocorreu um erro durante o mapeamento da entidade PacienteUpdateViewModel', $response_exception->getData() );
			return ServiceResponse::builder()->setStatus( ServiceResponseInterface::SERVICE_INTERNAL_ERROR )
				->setErrors( $response_exception->getData() )
				->build();
		}

		$paciente_dao = new PacienteDAO();
		$dao_response = $paciente_dao->persistPacienteUpdateObservacaoVM( $id_paciente, $paciente_update_vm );
		$service_response = new ServiceResponse();

		switch ($dao_response->getStatus()) {
			case DAOResponseInterface::SUCESS :
				$service_response->setStatus( ServiceResponseInterface::SUCESS );
				break;
			case DAOResponseInterface::DUPLICATED_UNIQUE_KEY :
				break;
			case DAOResponseInterface::FOREIGN_KEY_DONT_EXISTS :
				$service_response->setStatus( ServiceResponseInterface::VALIDATION_PARAMS_ERROR );
				$service_response->setErrors( $dao_response->getErrors() );
				break;
			case DAOResponseInterface::DATABASE_CONNECTION_ERROR :
				$service_response->setStatus( ServiceResponseInterface::DATABASE_CONNECTION_ERROR );
				break;
		}
		return $service_response;

	}

	public function getDadosPacienteByIdPaciente(int $id_paciente): ServiceResponse {

		$service_response = new ServiceResponse();
		$dao = new PacienteDAO();
		$dao_response = $dao->getDadosPacienteByIdPaciente( $id_paciente );
		switch ($dao_response->getStatus()) {
			case DAOResponseInterface::SUCESS :
				$service_response->setStatus( ServiceResponseInterface::SUCESS );
				$service_response->setData( $dao_response->getData() );
				break;
			case DAOResponseInterface::DATABASE_CONNECTION_ERROR :
				$service_response->setStatus( ServiceResponseInterface::DATABASE_CONNECTION_ERROR );
				$service_response->setErrors( $dao_response->getErrors() );
				break;
			default :
				$service_response->setStatus( ServiceResponseInterface::UNKNOWN_ERROR );
				$service_response->setErrors( $dao_response->getErrors() );
				break;
		}
		return $service_response;

	}

	public function getDadosPacienteByIdCiente(int $id_cliente): ServiceResponse {

		$service_response = new ServiceResponse();
		$dao = new PacienteDAO();
		$dao_response = $dao->getDadosPacienteByIdCliente( $id_cliente );
		switch ($dao_response->getStatus()) {
			case DAOResponseInterface::SUCESS :
				$service_response->setStatus( ServiceResponseInterface::SUCESS );
				$service_response->setData( $dao_response->getData() );
				break;
			case DAOResponseInterface::DATABASE_CONNECTION_ERROR :
				$service_response->setStatus( ServiceResponseInterface::DATABASE_CONNECTION_ERROR );
				$service_response->setErrors( $dao_response->getErrors() );
				break;
			default :
				$service_response->setStatus( ServiceResponseInterface::UNKNOWN_ERROR );
				$service_response->setErrors( $dao_response->getErrors() );
				break;
		}
		return $service_response;

	}

	public function setClienteAceiteTermo(int $idcliente): ServiceResponse {

		$service_response = new ServiceResponse();
		$dao = new PacienteDAO();
		$dao_response = $dao->setClienteAceiteTermo( $idcliente );
		switch ($dao_response->getStatus()) {
			case DAOResponseInterface::SUCESS :
				$service_response->setStatus( ServiceResponseInterface::SUCESS );
				$service_response->setData( $dao_response->getData() );
				break;
			case DAOResponseInterface::DATABASE_CONNECTION_ERROR :
				$service_response->setStatus( ServiceResponseInterface::DATABASE_CONNECTION_ERROR );
				$service_response->setErrors( $dao_response->getErrors() );
				break;
			default :
				$service_response->setStatus( ServiceResponseInterface::UNKNOWN_ERROR );
				$service_response->setErrors( $dao_response->getErrors() );
				break;
		}
		return $service_response;

	}

	public function getAceitermoStatusByClienteId(int $idcliente): ServiceResponse {

		$service_response = new ServiceResponse();
		$dao = new PacienteDAO();
		$daoResponse = $dao->getAceitermoStatusByClienteId( $idcliente );

		$service_response = $this->setServiceResponse( $daoResponse );

		return $service_response;

	}

}