content: package io.vividcode.happyride.tripservice.domain;
  
  import com.google.common.collect.ImmutableList;
  import io.eventuate.tram.events.aggregates.ResultWithDomainEvents;
  import io.eventuate.tram.sagas.orchestration.SagaInstanceFactory;
  import io.vividcode.happyride.common.PositionVO;
  import io.vividcode.happyride.tripservice.api.events.CancellationParty;
  import io.vividcode.happyride.tripservice.api.events.DriverAcceptTripDetails;
  import io.vividcode.happyride.tripservice.api.events.DriverAcceptTripEvent;
  import io.vividcode.happyride.tripservice.api.events.TripDetails;
  import io.vividcode.happyride.tripservice.api.events.TripDomainEvent;
  import io.vividcode.happyride.tripservice.api.web.TripVO;
  import io.vividcode.happyride.tripservice.dataaccess.TripRepository;
  import io.vividcode.happyride.tripservice.sagas.createtrip.CreateTripSaga;
  import io.vividcode.happyride.tripservice.sagas.createtrip.CreateTripSagaState;
  import java.math.BigDecimal;
  import java.util.Optional;
  import java.util.function.Consumer;
  import java.util.function.Function;
  import jakarta.transaction.Transactional;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.stereotype.Service;
  
  @Service
  @Transactional
  public class TripService {
  
    @Autowired
    TripRepository tripRepository;
  
    @Autowired
    TripFareService tripFareService;
  
    @Autowired
    TripDomainEventPublisher eventPublisher;
  
    @Autowired
    SagaInstanceFactory sagaInstanceFactory;
  
    @Autowired
    CreateTripSaga createTripSaga;
  
    public TripVO createTrip(final String passengerId, final PositionVO startPos,
        final PositionVO endPos) {
      final ResultWithDomainEvents<Trip, TripDomainEvent> tripAndEvents = Trip
          .createTrip(passengerId, startPos, endPos);
      final BigDecimal fare = this.tripFareService.calculate(startPos, endPos);
      final Trip trip = tripAndEvents.result;
      trip.setFare(fare);
      this.tripRepository.save(trip);
      this.eventPublisher.publish(trip, tripAndEvents.events);
  
      final TripDetails tripDetails = new TripDetails(passengerId, startPos,
          endPos);
      final CreateTripSagaState data = new CreateTripSagaState(trip.getId(),
          tripDetails, fare);
      this.sagaInstanceFactory.create(this.createTripSaga, data);
      return trip.toTripVO();
    }
  
    public Optional<TripVO> getTrip(final String id) {
      return this.tripRepository.findById(id).map(Trip::toTripVO);
    }
  
    public void markTripAsDispatched(final String tripId) {
      this.updateTrip(tripId, Trip::markAsDispatched);
    }
  
    public void driverAcceptTrip(final String tripId, final String driverId,
        final BigDecimal posLng,
        final BigDecimal posLat) {
      this.withTrip(tripId, trip -> this.eventPublisher.publish(trip,
          ImmutableList
              .of(new DriverAcceptTripEvent(
                  new DriverAcceptTripDetails(driverId, posLng, posLat)))));
    }
  
    public void markTripAsAccepted(final String tripId, final String driverId) {
      this.updateTrip(tripId, trip -> trip.acceptByDriver(driverId));
    }
  
    public void markTripAsStarted(final String tripId) {
      this.updateTrip(tripId, Trip::startTrip);
    }
  
    public void markTripAsFinished(final String tripId) {
      this.updateTrip(tripId, Trip::finishTrip);
    }
  
    public void markTripAsFailed(final String tripId) {
      this.updateTrip(tripId, Trip::markAsFailed);
    }
  
    public void rejectTrip(final String tripId) {
      this.updateTrip(tripId, Trip::rejectTrip);
    }
  
    public void confirmTrip(final String tripId) {
      this.updateTrip(tripId, Trip::confirmTrip);
    }
  
    public void shouldCancel(final String tripId,
        final CancellationParty initiator) {
      this.updateTrip(tripId, trip -> trip.shouldCancel(initiator));
    }
  
    public void shouldNotCancel(final String tripId,
        final CancellationParty initiator) {
      this.updateTrip(tripId, trip -> trip.shouldNotCancel(initiator));
    }
  
    private void updateTrip(final String tripId,
        final Function<Trip, ResultWithDomainEvents<Trip, TripDomainEvent>> updater) {
      this.withTrip(tripId,
          trip -> this.saveAndPublishEvents(updater.apply(trip)));
    }
  
    private void withTrip(final String tripId, final Consumer<Trip> consumer) {
      this.tripRepository.findById(tripId).ifPresent(consumer);
    }
  
    private void saveAndPublishEvents(
        final ResultWithDomainEvents<Trip, TripDomainEvent> result) {
      final Trip trip = result.result;
      this.tripRepository.save(trip);
      this.eventPublisher.publish(trip, result.events);
    }
  }
  