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

import ba.unsa.etf.si.bbqms.admin_service.api.BranchService;
import ba.unsa.etf.si.bbqms.domain.Branch;
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.queue_service.api.QueueService;
import ba.unsa.etf.si.bbqms.repository.BranchRepository;
import ba.unsa.etf.si.bbqms.repository.ServiceRepository;
import ba.unsa.etf.si.bbqms.repository.TellerStationRepository;
import ba.unsa.etf.si.bbqms.repository.TicketRepository;
import ba.unsa.etf.si.bbqms.ticket_service.api.TicketService;
import jakarta.persistence.EntityNotFoundException;

import java.time.Instant;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@org.springframework.stereotype.Service
public class DefaultTicketService implements TicketService {
    private final TicketRepository ticketRepository;
    private final ServiceRepository serviceRepository;
    private final BranchRepository branchRepository;
    private final TellerStationRepository tellerStationRepository;
    private final BranchService branchService;
    private final QueueService queueService;

    public DefaultTicketService(final TicketRepository ticketRepository,
                                final ServiceRepository serviceRepository,
                                final BranchRepository branchRepository,
                                final TellerStationRepository tellerStationRepository,
                                final BranchService branchService,
                                final QueueService queueService) {
        this.ticketRepository = ticketRepository;
        this.serviceRepository = serviceRepository;
        this.branchRepository = branchRepository;
        this.tellerStationRepository = tellerStationRepository;
        this.branchService = branchService;
        this.queueService = queueService;
    }

    @Override
    public Ticket create(final long serviceId, final long branchId, final String deviceToken) {
        final Service service = this.serviceRepository.findById(serviceId)
                .orElseThrow(() -> new EntityNotFoundException("No service with id: " + serviceId));

        final Branch branch = this.branchRepository.findById(branchId)
                .orElseThrow(() -> new EntityNotFoundException("No branch with id: " + branchId));

        final Set<Service> possibleServices = this.branchService.extractPossibleServices(branch);
        if (!possibleServices.contains(service)) {
            throw new IllegalArgumentException("Branch with id: " + branchId + " does not offer service with id: " + serviceId);
        }

        int serviceIndex = 0;
        for (Service possibleService : possibleServices) {
            if (possibleService.getId() == serviceId) {
                break;
            }
            serviceIndex++;
        }

        final char serviceLetter = (char) ('A' + serviceIndex);
        final String formattedNumber = serviceLetter + String.valueOf(getNextTicketNumber(possibleServices, branch));
        final Ticket newTicket = new Ticket(formattedNumber, Instant.now(), deviceToken, service, branch);

        return this.ticketRepository.save(newTicket);
    }

    @Override
    public List<Ticket> getByDevice(final String deviceToken) {
        return this.ticketRepository.findByDeviceToken(deviceToken);
    }

    @Override
    public void cancelTicket(final long ticketId) {
        final Ticket ticket = this.ticketRepository.findById(ticketId)
                .orElseThrow(() -> new EntityNotFoundException("No ticket with id: " + ticketId));

        final TellerStation tellerStation = ticket.getTellerStation();
        if (tellerStation != null) {
            this.queueService.advanceQueueState(tellerStation);
        } else {
            this.ticketRepository.delete(ticket);
        }
    }

    @Override
    public Ticket get(final long id) {
        return this.ticketRepository.get(id);
    }

    @Override
    public void deleteAll() {
        this.ticketRepository.deleteAll();
    }

    @Override
    public List<Ticket> findWithStation(final long stationId, final boolean deleted) {
        final TellerStation tellerStation = this.tellerStationRepository.get(stationId);
        final List<Long> serviceIds = tellerStation.getServices().stream().map(Service::getId).toList();

        return this.ticketRepository.findByService_IdInAndBranch_IdAndDeletedOrderByCreatedAtAsc(
                serviceIds,
                tellerStation.getBranch().getId(),
                deleted
        );
    }

    private long getNextTicketNumber(final Set<Service> possibleServices, final Branch branch) {
        final List<Ticket> tickets = this.ticketRepository.findByService_IdInAndBranch_Id(
                possibleServices.stream().map(Service::getId).toList(),
                branch.getId()
        );

        final Set<String> ticketNumbers = tickets.stream()
                .map(Ticket::getNumber)
                .map(this::extractNumericPart)
                .collect(Collectors.toSet());

        final long maxNumber = ticketNumbers.isEmpty() ? 0 : ticketNumbers.stream().mapToLong(Long::parseLong).max().getAsLong();

        return maxNumber + 1;
    }

    private String extractNumericPart(final String ticketNumber) {
        return ticketNumber.replaceAll("[^0-9]", "");
    }

    @Override
    public <T> T unwrap(final Class<T> tClass) {
        if (tClass.isAssignableFrom(TicketRepository.class)) {
            return tClass.cast(this.ticketRepository);
        }

        return TicketService.super.unwrap(tClass);
    }
}
