<?php


namespace App\Mappers;

// use App\DAO\AgendaMedicoDAO;
use App\Entities\AgendaMedico\AgendaMedico;
use App\Services\JWTService;
use App\Services\LogService;
use App\Utils\Collections\GeneralCollection;
use App\Utils\DateUtils;
use App\Utils\ResponseException;
use App\ViewModels\AgendaMedico\AgendaMedicoConflictingViewModel\AgendaMedicoConflictingViewModel;
use App\ViewModels\AgendaMedico\AgendaMedicoListingDisponivelViewModel\AgendaMedicoListingDisponivelViewModel;
use App\ViewModels\AgendaMedico\AgendaMedicoListingViewModel\AgendaMedicoListingViewModel;
use App\ViewModels\AgendaMedico\AgendaMedicoMultipleRegistrationViewModel\AgendaMedicoMultipleRegistrationViewModel;
use App\ViewModels\AgendaMedico\AgendaMedicoRegistrationViewModel\AgendaMedicoRegistrationViewModel;
use App\ViewModels\AgendaMedico\AgendaMedicoUpdateViewModel\AgendaMedicoUpdateViewModel;
use DateInterval;
use DatePeriod;
use DateTime;

class AgendaMedicoMapper {

	public static function getAgendaMedicoRegistrationVMFromArray(array $data): AgendaMedicoRegistrationViewModel {

		$agenda_registration_vm = AgendaMedicoRegistrationViewModel::builder()->setData( new DateTime( $data ['data'] ) )
			->setHoraInicio( new DateTime( $data ['hora_inicio'] ) )
			->setHoraFinal( new DateTime( $data ['hora_final'] ) )
			->setIdMedico( $data ['id_medico'] )
			->setIdUnidade( $data ['id_unidade'] )
			->setIdEspecialidade( $data ['id_especialidade'] )
			->setAdicionadoPor( JWTService::getTokenJWTPayload()->getUid() )
			->build();
		return $agenda_registration_vm;

	}

	public static function getAgendaMedicoFromAgendaMedicoRegistrationVM(AgendaMedicoRegistrationViewModel $agenda_registration_vm): AgendaMedico {

		$id_especialidade = $agenda_registration_vm->getIdEspecialidade();
		$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 );
		$codigo = $id_unidade . '-' . $id_especialidade . '-' . $codigo;
		try {
			$agenda = AgendaMedico::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() )
				->setIdEspecialidade( $agenda_registration_vm->getIdEspecialidade() )
				->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 getAgendaMedicoConflictingVMCollectionFromArray(array $agendas): GeneralCollection {

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

	}

	public static function getAgendaMedicoConflictingVMFromArray(array $agenda): AgendaMedicoConflictingViewModel {

		try {
			$agenda_conflicting_vm = AgendaMedicoConflictingViewModel::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 getAgendaMedicoListingVMCollectionFromArray(array $agendas) {

		$agenda_details_vm_collection = new GeneralCollection();
		$j = 0;
		foreach ( $agendas as $agenda ) {
			$agenda_details_vm = self::getAgendaMedicoListingVMFromArray( $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 getAgendaMedicoListingVMFromArray(array $agenda): AgendaMedicoListingViewModel {

		try {
			$agenda_details_vm = AgendaMedicoListingViewModel::builder()->setId( $agenda ['id'] )
				->setData( $agenda ['data'] )
				->setHoraInicio( $agenda ['hora_inicio'] )
				->setHoraFinal( $agenda ['hora_final'] )
				->setIdMedico( $agenda ['id_especialidade'] )
				->setNomeMedico( $agenda ['nome_especialidade'] )
				->setIdAgendamento( $agenda ['id_agendamento'] )
				->setIdPaciente( $agenda ['id_paciente'] )
				->setIdCliente( $agenda ['id_cliente'] )
				->setCanceladoEm( $agenda ['cancelado_em'] ?? '')
				->setIdConsulta( $agenda ['id_consulta'] )
				->setIdMedico( 0 )
				->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 getAgendaMedicoListingArrayFromAgendaMedicoListingVMCollection(GeneralCollection $agenda_details_vm_collection): array {

		return $agenda_details_vm_collection->toArray();

	}

	public static function getAgendaMedicoMultipleRegistrationVMFromArray(array $data): AgendaMedicoMultipleRegistrationViewModel {

		$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 = AgendaMedicoMultipleRegistrationViewModel::builder()->setIdMedico( 0 )
			->setIdUnidade( $data ['id_unidade'] )
			->setDataInicial( $data_inicial )
			->setDataFinal( $data_final )
			->setHoraInicio( $hora_inicio )
			->setHoraFim( $hora_fim )
			->setIdEspecialidade( $data['id_especialidade'] )
			->setIdMedico($data['id_medico'] )
			->setTempoAtendimento( $tempo_atendimento )
			->setDiasSemana( $dias_semana )
			->setAdicionadoPor( JWTService::getTokenJWTPayload()->getUid() )
			->setEncaixe($data['encaixe'])
			->build();
		return $agenda_multiple_registration_vm;

	}

	public static function getDailySchedulesByAgendaMedicoMultipleRegistrationVM(
			AgendaMedicoMultipleRegistrationViewModel $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 getServiceDatesCollectionByAgendaMedicoMultipleRegistrationVM(
			AgendaMedicoMultipleRegistrationViewModel $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 getAgendaMedicoBySchedulesAndDates(AgendaMedicoMultipleRegistrationViewModel $agenda_multiple_registration_vm,
			DateTime $initial_time, DateTime $final_time, DateTime $date): AgendaMedico {

		$id_especialidade = $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_unidade . '-' . $id_especialidade . '-' . $codigo;
		try {
			$agenda = AgendaMedico::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() )
				->setIdEspecialidade( $agenda_multiple_registration_vm->getIdEspecialidade() )
				->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 getAgendaMedicoCollectionBySchedulesAndDates(
			AgendaMedicoMultipleRegistrationViewModel $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 = AgendaMedicoMapper::getAgendaMedicoBySchedulesAndDates( $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 getAgendaMedicoUpdateVMFromArray(array $data): AgendaMedicoUpdateViewModel {

		return AgendaMedicoUpdateViewModel::builder()->setIdMedico( $data ['id_especialidade'] )
			->setAlteradoPor( JWTService::getTokenJWTPayload()->getUid() )
			->build();

	}

	public static function getAgendaMedicoFromAgendaMedicoUpdateVM(AgendaMedicoUpdateViewModel $agenda_update_vm): AgendaMedico {

		return AgendaMedico::builder()->setIdMedico( $agenda_update_vm->getIdMedico() )
			->setAlteradoPor( $agenda_update_vm->getAlteradoPor() )
			->build();

	}

	public static function getAgendaMedicoByArray(array $result_array): AgendaMedico {

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

	}

    public static function getAgendaDateVMCollectionFromArray (array $response_array) : GeneralCollection {
        $agenda_details_vm_collection = new GeneralCollection();
        foreach ($response_array as $row) {
            $agenda_details_vm = AgendaMedicoMapper::getAgendaDateVMFromArray($row);
            $agenda_details_vm_collection->push($agenda_details_vm);
        }
        return $agenda_details_vm_collection;
    }

    public static function getAgendaDateVMFromArray (array $agenda) : AgendaMedicoListingDisponivelViewModel {
        return AgendaMedicoListingDisponivelViewModel::builder()
            ->setId($agenda['idagenda'])
            ->setHoraInicio($agenda['hora_inicio'])
            ->setHoraFinal($agenda['hora_final'])
            ->build();
    }

    public static function getAgendaDateArrayFromAgendaVMCollection (GeneralCollection $agenda_details_vm_collection) : array {
        return $agenda_details_vm_collection->toArray();
    }

}