<?php

namespace App\Mappers;

use App\DAO\AgendamentoDAO;
use App\Entities\Agenda\Agenda;
use App\Services\JWTService;
use App\Services\LogService;
use App\Utils\Collections\GeneralCollection;
use App\Utils\DateUtils;
use App\Utils\ResponseException;
use App\ViewModels\Agenda\AgendaConflictingViewModel\AgendaConflictingViewModel;
use App\ViewModels\Agenda\AgendaListingViewModel\AgendaListingViewModel;
use App\ViewModels\Agenda\AgendaMultipleRegistrationViewModel\AgendaMultipleRegistrationViewModel;
use App\ViewModels\Agenda\AgendaRegistrationViewModel\AgendaRegistrationViewModel;
use App\ViewModels\Agenda\AgendaUpdateViewModel\AgendaUpdateViewModel;
use DateInterval;
use DatePeriod;
use DateTime;
use App\Services\JWT\UsuarioLogado;

class AgendaMapper {

	public static function getAgendaRegistrationVMFromArray(array $data): AgendaRegistrationViewModel {

		$agenda_registration_vm = AgendaRegistrationViewModel::builder()->setData( new DateTime( $data ['data'] ) )
			->setHoraInicio( new DateTime( $data ['hora_inicio'] ) )
			->setHoraFinal( new DateTime( $data ['hora_final'] ) )
			->setIdMedico( $data ['id_medico'] )
			->setIdAssistente( $data ['id_assistente'] )
			->setIdUnidade( $data ['id_unidade'] )
			->setIdTipoProcedimento( $data ['id_tipo_procedimento'] )
			->setAdicionadoPor( JWTService::getTokenJWTPayload()->getUid() )
			->build();

		return $agenda_registration_vm;

	}

	public static function getAgendaFromAgendaRegistrationVM(AgendaRegistrationViewModel $agenda_registration_vm): Agenda {

		$id_medico = $agenda_registration_vm->getIdMedico();
		$id_assistente = UsuarioLogado::getUsuarioId();		
		$id_unidade = $agenda_registration_vm->getIdUnidade();

		$data = $agenda_registration_vm->getData()
			->format( 'Y-m-d' );

		$hora_inicio = $agenda_registration_vm->getHoraInicio()
			->format( 'H:i' );

		$hora_final = $agenda_registration_vm->getHoraFinal()
			->format( 'H:i' );

		$codigo = preg_replace( '/\D/', '', $data . $hora_inicio . $hora_final );
		if($id_medico > 0) {
			$codigo = $id_medico . '-' . $id_unidade . '-' . $codigo;
		} else {
			$codigo = $id_assistente . '-' . $id_unidade . '-' . $codigo;
		}
		

		try {
			if ($agenda_registration_vm->getIdMedico() > 0) {
				$agenda = Agenda::builder()->setData( $data )
					->setCodigo( $codigo )
					->setAdicionadoPor( $agenda_registration_vm->getAdicionadoPor() )
					->setHoraInicio( $hora_inicio )
					->setHoraFinal( $hora_final )
					->setIdMedico( $agenda_registration_vm->getIdMedico() )
					->setIdUnidade( $agenda_registration_vm->getIdUnidade() )
					->setIdTipoProcedimento( $agenda_registration_vm->getIdTipoProcedimento() )
					->setCriadoEm( DateUtils::today() )
					->build();
			}
			if ($agenda_registration_vm->getIdAssistente() > 0) {
				$agenda = Agenda::builder()->setData( $data )
					->setCodigo( $codigo )
					->setAdicionadoPor( $agenda_registration_vm->getAdicionadoPor() )
					->setHoraInicio( $hora_inicio )
					->setHoraFinal( $hora_final )
					->setIdAssistente( $agenda_registration_vm->getIdAssistente() )
					->setIdUnidade( $agenda_registration_vm->getIdUnidade() )
					->setIdTipoProcedimento( $agenda_registration_vm->getIdTipoProcedimento() )
					->setCriadoEm( DateUtils::today() )
					->build();
			}
		} catch ( ResponseException $e ) {
			$log_service = new LogService();
			$log_service->log_error( $e->getCode(), $e->getMessage(), $e->getData() );
		}

		return $agenda;

	}

	public static function getAgendaConflictingVMCollectionFromArray(array $agendas): GeneralCollection {

		$agenda_conflicting_vm_collection = new GeneralCollection();
		foreach ( $agendas as $agenda ) {
			$agenda_conflicting_vm = AgendaMapper::getAgendaConflictingVMFromArray( $agenda );
			$agenda_conflicting_vm_collection->push( $agenda_conflicting_vm );
		}
		return $agenda_conflicting_vm_collection;

	}

	public static function getAgendaConflictingVMFromArray(array $agenda): AgendaConflictingViewModel {

		try {
			$agenda_conflicting_vm = AgendaConflictingViewModel::builder()->setId( $agenda ['id'] )
				->setData( $agenda ['data'] )
				->setHoraInicio( $agenda ['hora_inicio'] )
				->setHoraFinal( $agenda ['hora_final'] )
				->build();
		} catch ( ResponseException $exception ) {
			$log_service = new LogService();
			$log_service->log_error( $exception->getCode(), 'Ocorre um erro durante a busca de horários conflitantes no banco de dados.', $exception->getData() );
		}
		return $agenda_conflicting_vm;

	}

	public static function getAgendaListingVMCollectionFromArray(array $agendas) {

		$agenda_details_vm_collection = new GeneralCollection();
		$j = 0;
		foreach ( $agendas as $agenda ) {
			$agenda_details_vm = self::getAgendaListingVMFromArray( $agenda );
			if ($j === 0 || $agenda_details_vm->getId() !== $agenda_details_vm_collection->get( $j - 1 )
				->getId()) {
				$agenda_details_vm_collection->push( $agenda_details_vm );
				$j++;
			}
		}
		return $agenda_details_vm_collection;

	}

	public static function getAgendaListingVMFromArray(array $agenda): AgendaListingViewModel {

		try {
			$agenda_details_vm = AgendaListingViewModel::builder()->setId( $agenda ['id'] )
				->setData( $agenda ['data'] )
				->setHoraInicio( $agenda ['hora_inicio'] )
				->setHoraFinal( $agenda ['hora_final'] )
				->setIdTipoProcedimento( $agenda ['id_tipo_procedimento'] )
				->setIdAgendamento( $agenda ['id_agendamento'] )
				->setIdPaciente( $agenda ['id_paciente'] )
				->setIdCliente( $agenda ['id_cliente'] )
				->setIdProcedimento( $agenda ['id_procedimento'] )
				->setTipoProcedimento( $agenda ['tipo_procedimento'] )
				->setProcedimento( $agenda ['procedimento'] )
				->setCanceladoEm( $agenda ['cancelado_em'] ?? '')
				->setIdConsulta( $agenda ['id_consulta'] )
				->setIdMedico( $agenda ['id_medico'] )
				->setIdUnidade( $agenda ['id_unidade'] )
				->setNomePet( $agenda ['nome_pet'] )
				->setNomeCliente( $agenda ['nome_cliente'] )
				->build();
		} catch ( ResponseException $exception ) {
			$log_service = new LogService();
			$log_service->log_error( $exception->getCode(), $exception->getMessage(), $exception->getData() );
		}
		return $agenda_details_vm;

	}

	public static function getAgendaListingArrayFromAgendaListingVMCollection(GeneralCollection $agenda_details_vm_collection): array {

		return $agenda_details_vm_collection->toArray();

	}

	public static function getAgendaMultipleRegistrationVMFromArray(array $data): AgendaMultipleRegistrationViewModel {

		$data_inicial = new DateTime( $data ['data_inicial'] );
		$data_final = new DateTime( $data ['data_final'] );
		$hora_inicio = new DateTime( $data ['hora_inicio'] );
		$hora_fim = new DateTime( $data ['hora_fim'] );
		list ( $hour, $minute ) = explode( ":", $data ['tempo_atendimento'] );
		$tempo_atendimento = new DateInterval( 'PT' . $hour . 'H' . $minute . 'M' );
		$dias_semana = new GeneralCollection( $data ['dias_semana'] );
		$dias_semana->map( function ($number_str) {
			return ( int ) $number_str;
		}, true );
		$agenda_multiple_registration_vm = AgendaMultipleRegistrationViewModel::builder()->setIdMedico( $data ['id_medico'] )
			->setIdUnidade( $data ['id_unidade'] )
			->setDataInicial( $data_inicial )
			->setDataFinal( $data_final )
			->setHoraInicio( $hora_inicio )
			->setHoraFim( $hora_fim )
			->setIdTipoProcedimento( $data ['id_tipo_procedimento'] )
			->setTempoAtendimento( $tempo_atendimento )
			->setDiasSemana( $dias_semana )
			->setAdicionadoPor( JWTService::getTokenJWTPayload()->getUid() )
			->build();
		return $agenda_multiple_registration_vm;

	}

	public static function getDailySchedulesByAgendaMultipleRegistrationVM(AgendaMultipleRegistrationViewModel $agenda_multiple_registration_vm): GeneralCollection {

		$period = new DatePeriod( $agenda_multiple_registration_vm->getHoraInicio(), $agenda_multiple_registration_vm->getTempoAtendimento(),
				$agenda_multiple_registration_vm->getHoraFim() );
		$daily_schedules_collection = new GeneralCollection();
		foreach ( $period as $timetable ) {
			$daily_schedules_collection->push( $timetable );
		}
		return $daily_schedules_collection;

	}

	public static function getServiceDatesCollectionByAgendaMultipleRegistrationVM(AgendaMultipleRegistrationViewModel $agenda_multiple_registration_vm): GeneralCollection {

		$final_date = $agenda_multiple_registration_vm->getDataFinal();
		$weekdays = $agenda_multiple_registration_vm->getDiasSemana();
		$service_dates_collection = new GeneralCollection();
		$date = clone $agenda_multiple_registration_vm->getDataInicial();
		while ( $date->format( 'Y-m-d' ) <= $final_date->format( 'Y-m-d' ) ) {
			// $weekday = (int) date( 'w', $date->format('Y-m-d'));
			$weekday = ( int ) date( 'w', strtotime( $date->format( 'Y-m-d' ) ) );
			if ($weekdays->get( $weekday )) {
				$service_dates_collection->push( $date );
			}
			$time_next_day = strtotime( '+ 1 day', $date->getTimestamp() );
			$date = new DateTime( date( 'Y-m-d', $time_next_day ) );
		}
		return $service_dates_collection;

	}

	public static function getAgendaBySchedulesAndDates(AgendaMultipleRegistrationViewModel $agenda_multiple_registration_vm, DateTime $initial_time,
			DateTime $final_time, DateTime $date): Agenda {

		$id_medico = $agenda_multiple_registration_vm->getIdMedico();
		$id_unidade = $agenda_multiple_registration_vm->getIdUnidade();
		$codigo = preg_replace( '/\D/', '', $date->format( 'Y-m-d' ) . $initial_time->format( 'H:i' ) . $final_time->format( 'H:i' ) );
		$codigo = $id_medico . '-' . $id_unidade . '-' . $codigo;
		try {
			$agenda = Agenda::builder()->setData( $date->format( 'Y-m-d' ) )
				->setCodigo( $codigo )
				->setAdicionadoPor( $agenda_multiple_registration_vm->getAdicionadoPor() )
				->setHoraInicio( $initial_time->format( 'H:i' ) )
				->setHoraFinal( $final_time->format( 'H:i' ) )
				->setIdMEDICO( $agenda_multiple_registration_vm->getIdMedico() )
				->setIdUnidade( $agenda_multiple_registration_vm->getIdUnidade() )
				->setIdTIPOPROCEDIMENTO( $agenda_multiple_registration_vm->getIdTipoProcedimento() )
				->setCriadoEm( DateUtils::today() )
				->build();
		} catch ( ResponseException $e ) {
			$log_service = new LogService();
			$log_service->log_error( $e->getCode(), $e->getMessage(), $e->getData() );
		}
		return $agenda;

	}

	public static function getAgendaCollectionBySchedulesAndDates(AgendaMultipleRegistrationViewModel $agenda_multiple_registration_vm,
			GeneralCollection $daily_schedules_collection, GeneralCollection $service_dates_collection, GeneralCollection $potential_conflicts,
			GeneralCollection $conflicts): GeneralCollection {

		$agenda_collection = new GeneralCollection();
		$tempo_atendimento = $agenda_multiple_registration_vm->getTempoAtendimento();
		$minutes_service = DateUtils::dateIntervalMinutes( $tempo_atendimento );
		for($i = 0; $i < $service_dates_collection->length; $i++) {
			$current_date = DateUtils::datetime_to_date( $service_dates_collection->get( $i ) );
			$potential_conflicts_filtered_by_date = new GeneralCollection();
			for($k = 0; $k < $potential_conflicts->length; $k++) {
				$agenda_conflicting_vm = $potential_conflicts->get( $k );
				if ($agenda_conflicting_vm->getData() === $current_date) {
					$potential_conflicts_filtered_by_date->push( $agenda_conflicting_vm );
				} else if ($potential_conflicts_filtered_by_date->length > 0 &&
						$agenda_conflicting_vm->getData() !== $potential_conflicts_filtered_by_date->get( 0 )
							->getData()) {
					break;
				}
			}
			for($j = 0; $j < $daily_schedules_collection->length; $j++) {
				$initial_schedule = DateUtils::getDateTime( $daily_schedules_collection->get( $j ) );
				$time_next_schedule = strtotime( "+ $minutes_service minute", $initial_schedule->getTimestamp() );
				$final_schedule = new DateTime( date( 'H:i', $time_next_schedule ) );
				$time_initial_schedule = DateUtils::datetime_to_time( $initial_schedule );
				$time_final_schedule = DateUtils::datetime_to_time( $final_schedule );
				if ($time_initial_schedule > $time_final_schedule) {
					continue;
				}
				$id_agenda_conflict = DateUtils::calculateConflictsOnSchedule( $time_initial_schedule, $time_final_schedule,
					$potential_conflicts_filtered_by_date );
				if ($id_agenda_conflict) {
					$conflicts->push( $id_agenda_conflict );
				} else {
					$agenda = AgendaMapper::getAgendaBySchedulesAndDates( $agenda_multiple_registration_vm, $initial_schedule, $final_schedule,
						$service_dates_collection->get( $i ) );
					$agenda_collection->push( $agenda );
				}
			}
		}
		$conflicts->removeDuplicates( true );
		return $agenda_collection;

	}

	public static function getAgendaUpdateVMFromArray(array $data): AgendaUpdateViewModel {

		return AgendaUpdateViewModel::builder()->setIdTipoProcedimento( $data ['id_tipo_procedimento'] )
			->setAlteradoPor( JWTService::getTokenJWTPayload()->getUid() )
			->build();

	}

	public static function getAgendaFromAgendaUpdateVM(AgendaUpdateViewModel $agenda_update_vm): Agenda {

		return Agenda::builder()->setIdTIPOPROCEDIMENTO( $agenda_update_vm->getIdTipoProcedimento() )
			->setAlteradoPor( $agenda_update_vm->getAlteradoPor() )
			->build();

	}

	public static function getAgendaByArray(array $result_array): Agenda {

		try {
			$agenda = Agenda::builder()->setid( ( int ) $result_array ['id'] )
				->setcodigo( $result_array ['codigo'] )
				->setidMEDICO( $result_array ['id_MEDICO'] )
				->setData( $result_array ['data'] )
				->sethorainicio( $result_array ['hora_inicio'] )
				->sethorafinal( $result_array ['hora_final'] )
				->setidTIPOPROCEDIMENTO( $result_array ['id_TIPO_PROCEDIMENTO'] )
				->build();
			return $agenda;
		} catch ( ResponseException $response_exception ) {
			(new LogService())->log_response_exception( $response_exception );
		}

	}

}