package ba.unsa.etf.si.bbqms.queue_service.implementation;

import ba.unsa.etf.si.bbqms.domain.Service;
import ba.unsa.etf.si.bbqms.domain.TellerStation;
import ba.unsa.etf.si.bbqms.domain.Ticket;
import ba.unsa.etf.si.bbqms.notification_service.api.Notification;
import ba.unsa.etf.si.bbqms.notification_service.api.NotificationService;
import ba.unsa.etf.si.bbqms.queue_service.api.QueueService;
import ba.unsa.etf.si.bbqms.repository.TellerStationRepository;
import ba.unsa.etf.si.bbqms.repository.TicketRepository;
import jakarta.transaction.Transactional;

import java.util.List;
import java.util.Optional;

@org.springframework.stereotype.Service
public class DefaultQueueService implements QueueService {
    private final TicketRepository ticketRepository;
    private final TellerStationRepository tellerStationRepository;
    private final NotificationService notificationService;

    public DefaultQueueService(final TicketRepository ticketRepository,
                               final TellerStationRepository tellerStationRepository,
                               final NotificationService notificationService) {
        this.ticketRepository = ticketRepository;
        this.tellerStationRepository = tellerStationRepository;
        this.notificationService = notificationService;
    }

    @Override
    public Optional<Ticket> findNextTicketForStation(final TellerStation tellerStation) {
        final List<Long> serviceIds = tellerStation.getServices().stream().map(Service::getId).toList();
        final List<Ticket> tickets = this.ticketRepository
                .findByService_IdInAndBranch_IdAndTellerStationIsNullAndDeletedFalseOrderByCreatedAtAsc(
                        serviceIds,
                        tellerStation.getBranch().getId()
                );

        return tickets.stream().findFirst();
    }

    @Override
    @Transactional
    public Optional<Ticket> advanceQueueState(final TellerStation tellerStation) {
        final Optional<Ticket> optionalTicketAtStation =
                this.ticketRepository.findByTellerStation_IdAndDeletedFalse(tellerStation.getId());

        if (optionalTicketAtStation.isPresent()) {
            final Ticket ticketAtStation = optionalTicketAtStation.get();
            ticketAtStation.setDeleted(true);
            ticketRepository.save(ticketAtStation);
        }

        final Optional<Ticket> optionalNextTicket = findNextTicketForStation(tellerStation);
        if (optionalNextTicket.isEmpty()) {
            return Optional.empty();
        }

        final Ticket nextTicket = optionalNextTicket.get();
        nextTicket.setTellerStation(tellerStation);

        final Notification newNotification = new Notification(
                nextTicket.getDeviceToken(),
                "It's your turn!",
                "Please approach station: " + nextTicket.getTellerStation().getName()
        );
        this.notificationService.sendNotification(newNotification);

        return Optional.of(this.ticketRepository.save(nextTicket));
    }

    @Override
    public Optional<Ticket> findCurrentTicketForStation(final TellerStation tellerStation) {
        return this.ticketRepository.findByTellerStation_IdAndDeletedFalse(tellerStation.getId());
    }

    @Override
    public Optional<Ticket> undoStationState(final long stationId) {
        final TellerStation station = this.tellerStationRepository.get(stationId);

        final Optional<Ticket> optionalCurrentlyAssigned = findCurrentTicketForStation(station);

        if (optionalCurrentlyAssigned.isEmpty()) {
            throw new IllegalStateException("Detected attempt to undo station state to previous ticket while there is no current ticket. Nothing to undo.");
        }

        final Ticket currentlyAssigned = optionalCurrentlyAssigned.get();

        currentlyAssigned.setTellerStation(null);
        this.ticketRepository.save(currentlyAssigned);

        final List<Ticket> previousTickets = this.ticketRepository
                .findByTellerStation_IdAndDeletedTrueAndCreatedAtLessThanOrderByCreatedAtDesc(
                        station.getId(),
                        currentlyAssigned.getCreatedAt()
                );

        final Optional<Ticket> optionalPreviousTicket = previousTickets.stream().findFirst();

        if (optionalPreviousTicket.isPresent()) {
            final Ticket previousTicket = optionalPreviousTicket.get();

            previousTicket.setDeleted(false);
            previousTicket.setTellerStation(station);

            this.ticketRepository.save(previousTicket);
            return Optional.of(previousTicket);
        }

        return Optional.empty();
    }
}
