package it.sw.pa.comune.artegna.service.impl;

import static it.sw.pa.comune.artegna.domain.enumeration.TipoEventoNotifica.ANNULLAMENTO;

import io.seruco.encoding.base62.Base62;
import it.sw.pa.comune.artegna.domain.Conferma;
import it.sw.pa.comune.artegna.domain.Prenotazione;
import it.sw.pa.comune.artegna.domain.User;
import it.sw.pa.comune.artegna.domain.enumeration.StatoPrenotazione;
import it.sw.pa.comune.artegna.domain.enumeration.TipoConferma;
import it.sw.pa.comune.artegna.repository.ConfermaRepository;
import it.sw.pa.comune.artegna.repository.PrenotazioneRepository;
import it.sw.pa.comune.artegna.service.ConfermaService;
import it.sw.pa.comune.artegna.service.UserService;
import it.sw.pa.comune.artegna.service.UtenteAppService;
import it.sw.pa.comune.artegna.service.dto.ConfermaDTO;
import it.sw.pa.comune.artegna.service.dto.UtenteAppDTO;
import it.sw.pa.comune.artegna.service.mapper.ConfermaMapper;
import it.sw.pa.comune.artegna.service.mapper.UserMapper;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * Service Implementation for managing {@link it.sw.pa.comune.artegna.domain.Conferma}.
 */
@Service
@Transactional
public class ConfermaServiceImpl implements ConfermaService {

    private static final Logger LOG = LoggerFactory.getLogger(ConfermaServiceImpl.class);

    private final ConfermaRepository confermaRepository;

    private final ConfermaMapper confermaMapper;

    private final PrenotazioneRepository prenotazioneRepository;

    private final UtenteAppService utenteAppService;

    public ConfermaServiceImpl(
        ConfermaRepository confermaRepository,
        ConfermaMapper confermaMapper,
        PrenotazioneRepository prenotazioneRepository,
        UtenteAppService utenteAppService
    ) {
        this.confermaRepository = confermaRepository;
        this.confermaMapper = confermaMapper;
        this.prenotazioneRepository = prenotazioneRepository;
        this.utenteAppService = utenteAppService;
    }

    @Override
    public ConfermaDTO save(ConfermaDTO confermaDTO) {
        LOG.debug("Request to save Conferma : {}", confermaDTO);
        confermaDTO.setConfermataDa(utenteAppService.fetchUtenteAppFromUser());
        Conferma conferma = confermaMapper.toEntity(confermaDTO);
        Long idPrenotazione = null;
        if (conferma.getId() != null) {
            idPrenotazione = conferma.getId();
            conferma.setId(null);
            conferma.setCodice(createCodice());
            if (conferma.getTipoConferma() == null) {
                conferma.setTipoConferma(TipoConferma.RIFIUTATA);
            }
        }
        conferma = confermaRepository.saveAndFlush(conferma);
        if (idPrenotazione != null) {
            Prenotazione prenotazioneCollegata = prenotazioneRepository.findById(idPrenotazione).orElse(null);
            if (prenotazioneCollegata != null) {
                switch (conferma.getTipoConferma()) {
                    case CONFERMATA:
                        prenotazioneCollegata.setStato(StatoPrenotazione.CONFERMATA);
                        break;
                    case RIFIUTATA:
                        prenotazioneCollegata.setStato(StatoPrenotazione.ANNULLATA);
                        break;
                    default:
                        throw new IllegalArgumentException("Tipo conferma non valido: " + conferma.getTipoConferma());
                }
                prenotazioneCollegata.setConferma(conferma);
                prenotazioneRepository.saveAndFlush(prenotazioneCollegata);
            }
        }
        return confermaMapper.toDto(conferma);
    }

    private String createCodice() {
        Base62 base62 = Base62.createInstance();
        String currentDateTime = String.valueOf(java.time.Instant.now().toEpochMilli());

        return new String(
            base62.encode(currentDateTime.getBytes(java.nio.charset.StandardCharsets.UTF_8)),
            java.nio.charset.StandardCharsets.UTF_8
        );
    }

    @Override
    public ConfermaDTO update(ConfermaDTO confermaDTO) {
        LOG.debug("Request to update Conferma : {}", confermaDTO);
        Conferma conferma = confermaMapper.toEntity(confermaDTO);
        conferma = confermaRepository.save(conferma);
        return confermaMapper.toDto(conferma);
    }

    @Override
    public Optional<ConfermaDTO> partialUpdate(ConfermaDTO confermaDTO) {
        LOG.debug("Request to partially update Conferma : {}", confermaDTO);

        return confermaRepository
            .findById(confermaDTO.getId())
            .map(existingConferma -> {
                confermaMapper.partialUpdate(existingConferma, confermaDTO);

                return existingConferma;
            })
            .map(confermaRepository::save)
            .map(confermaMapper::toDto);
    }

    public Page<ConfermaDTO> findAllWithEagerRelationships(Pageable pageable) {
        return confermaRepository.findAllWithEagerRelationships(pageable).map(confermaMapper::toDto);
    }

    /**
     *  Get all the confermas where Prenotazione is {@code null}.
     *  @return the list of entities.
     */
    @Transactional(readOnly = true)
    public List<ConfermaDTO> findAllWherePrenotazioneIsNull() {
        LOG.debug("Request to get all confermas where Prenotazione is null");
        return StreamSupport.stream(confermaRepository.findAll().spliterator(), false)
            .filter(conferma -> conferma.getPrenotazione() == null)
            .map(confermaMapper::toDto)
            .collect(Collectors.toCollection(LinkedList::new));
    }

    @Override
    @Transactional(readOnly = true)
    public Optional<ConfermaDTO> findOne(Long id) {
        LOG.debug("Request to get Conferma : {}", id);
        return confermaRepository.findOneWithEagerRelationships(id).map(confermaMapper::toDto);
    }

    @Override
    public void delete(Long id) {
        LOG.debug("Request to delete Conferma : {}", id);
        confermaRepository.deleteById(id);
    }
}
