package de.fete.application.service;

import de.fete.application.service.exception.EventAlreadyCancelledException;
import de.fete.application.service.exception.EventNotFoundException;
import de.fete.application.service.exception.InvalidOrganizerTokenException;
import de.fete.domain.model.CreateEventCommand;
import de.fete.domain.model.Event;
import de.fete.domain.model.EventToken;
import de.fete.domain.model.OrganizerToken;
import de.fete.domain.port.in.CreateEventUseCase;
import de.fete.domain.port.in.GetEventUseCase;
import de.fete.domain.port.in.UpdateEventUseCase;
import de.fete.domain.port.out.EventRepository;
import java.time.Clock;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.util.Optional;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/** Application service implementing event creation and retrieval. */
@Service
public class EventService implements CreateEventUseCase, GetEventUseCase, UpdateEventUseCase {

  private static final int EXPIRY_DAYS_AFTER_EVENT = 7;

  private final EventRepository eventRepository;
  private final Clock clock;

  /** Creates a new EventService with the given repository and clock. */
  public EventService(EventRepository eventRepository, Clock clock) {
    this.eventRepository = eventRepository;
    this.clock = clock;
  }

  @Override
  public Event createEvent(CreateEventCommand command) {
    LocalDate expiryDate = command.dateTime().toLocalDate().plusDays(EXPIRY_DAYS_AFTER_EVENT);

    var event = new Event(
        null,
        EventToken.generate(),
        OrganizerToken.generate(),
        command.title(),
        command.description(),
        command.dateTime(),
        command.timezone(),
        command.location(),
        expiryDate,
        OffsetDateTime.now(clock),
        false,
        null);

    return eventRepository.save(event);
  }

  @Override
  public Optional<Event> getByEventToken(EventToken eventToken) {
    return eventRepository.findByEventToken(eventToken);
  }

  @Transactional
  @Override
  public void cancelEvent(
      EventToken eventToken, OrganizerToken organizerToken,
      Boolean cancelled, String reason) {
    if (!Boolean.TRUE.equals(cancelled)) {
      return;
    }

    Event event = eventRepository.findByEventToken(eventToken)
        .orElseThrow(() -> new EventNotFoundException(eventToken.value()));

    if (!event.organizerToken().equals(organizerToken)) {
      throw new InvalidOrganizerTokenException();
    }

    if (event.cancelled()) {
      throw new EventAlreadyCancelledException(eventToken.value());
    }

    eventRepository.save(event.withCancellation(true, reason));
  }
}
