package it.micegroup.gestioneferie.pianificazioneferie.service;

import java.util.Map;
import java.util.List;
import java.util.Locale;
import java.util.HashMap;
import java.util.Optional;
import java.util.ArrayList;
import java.util.Collection;
import java.util.ResourceBundle;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;

import org.springframework.context.i18n.LocaleContextHolder;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import net.sf.jasperreports.engine.JRParameter;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;

import it.micegroup.gestioneferie.pianificazioneferie.entity.DpianiFerie;

import it.micegroup.gestioneferie.pianificazioneferie.entity.DstatiPianoFerie;
import it.micegroup.gestioneferie.pianificazioneferie.entity.DstatiPianoFerieKey;

import it.micegroup.gestioneferie.pianificazioneferie.entity.DpianiFerie_;
import it.micegroup.gestioneferie.pianificazioneferie.criteria.DpianiFerieCriteria;
import it.micegroup.gestioneferie.pianificazioneferie.repository.DpianiFerieRepository;
import it.micegroup.voila2runtime.specification.FilterService;

import it.micegroup.gestioneferie.pianificazioneferie.entity.DpianiFerie_;
import it.micegroup.gestioneferie.pianificazioneferie.entity.DstatiPianoFerie_;
import it.micegroup.gestioneferie.pianificazioneferie.entity.DstatiPianoFerieKey_;
import lombok.RequiredArgsConstructor;
import it.micegroup.gestioneferie.libs.service.BaseServiceImpl;

import java.util.stream.Collectors;
import it.micegroup.gestioneferie.pianificazioneferie.entity.DrichiesteApprovazPf;
import it.micegroup.gestioneferie.pianificazioneferie.entity.DrichAdesionePf;
import it.micegroup.gestioneferie.pianificazioneferie.entity.DammtiPianiFerie;
import it.micegroup.voila2runtime.entity.GenericEntity;
import it.micegroup.voila2runtime.exception.BusinessException;
import net.sf.jasperreports.engine.JRException;

@RequiredArgsConstructor
@Service
@Transactional
public class DpianiFerieServiceImpl extends BaseServiceImpl implements DpianiFerieService {

	private final DpianiFerieRepository dpianiFerieRepository;
	private final FilterService<DpianiFerie> filterService;

	// CHILD SERVICES
	private final DrichiesteApprovazPfService drichiesteApprovazPfService;
	private final DrichAdesionePfService drichAdesionePfService;
	private final DammtiPianiFerieService dammtiPianiFerieService;

	@SuppressWarnings("unused")
	private static final Logger LOGGER = LogManager.getLogger(DpianiFerieServiceImpl.class);

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * it.micegroup.gestioneferie.pianificazioneferie.service.GenericEntityService#
	 * findAll(org. springframework.data.domain.Pageable)
	 */
	@Override
	@Transactional(readOnly = true)
	public Page<DpianiFerie> findAll(Pageable pageable) {
		return dpianiFerieRepository.findAll(pageable);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * it.micegroup.gestioneferie.pianificazioneferie.service.GenericEntityService#
	 * findAll()
	 */
	@Override
	@Transactional(readOnly = true)
	public List<DpianiFerie> findAll() {
		return dpianiFerieRepository.findAll();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * it.micegroup.gestioneferie.pianificazioneferie.service.GenericEntityService#
	 * findByCriteria(java. lang.Object, org.springframework.data.domain.Pageable)
	 */
	@Override
	@Transactional(readOnly = true)
	public Page<DpianiFerie> findByCriteria(DpianiFerieCriteria criteria, Pageable pageable) {
		final Specification<DpianiFerie> specification = createSpecification(criteria);
		return dpianiFerieRepository.findAll(specification, pageable);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * it.micegroup.gestioneferie.pianificazioneferie.service.GenericEntityService#
	 * findByCriteria(java. lang.Object)
	 */
	@Override
	@Transactional(readOnly = true)
	public List<DpianiFerie> findByCriteria(DpianiFerieCriteria criteria) {
		final Specification<DpianiFerie> specification = createSpecification(criteria);
		return dpianiFerieRepository.findAll(specification);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * it.micegroup.gestioneferie.pianificazioneferie.service.GenericEntityService#
	 * findById(java.lang. Object)
	 */
	@Override
	@Transactional(readOnly = true)
	public Optional<DpianiFerie> findById(Long id) {
		return dpianiFerieRepository.findBySequIdPianoFerie(id);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * it.micegroup.gestioneferie.pianificazioneferie.service.GenericEntityService#
	 * exists(java.lang. Object)
	 */
	@Override
	public boolean exists(Long id) {
		return dpianiFerieRepository.existsById(id);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * it.micegroup.gestioneferie.pianificazioneferie.service.GenericEntityService#
	 * insert(java. lang.Object)
	 */
	@Override
	public DpianiFerie insert(@Valid DpianiFerie entity) {
		return dpianiFerieRepository.save(entity);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * it.micegroup.gestioneferie.pianificazioneferie.service.GenericEntityService#
	 * update(java. lang.Object)
	 */
	@Override
	public DpianiFerie update(@Valid DpianiFerie entity) {
		return dpianiFerieRepository.save(entity);
	}

	@Override
	public void delete(DpianiFerie entity) {
		dpianiFerieRepository.delete(entity);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * it.micegroup.gestioneferie.pianificazioneferie.service.GenericEntityService#
	 * deleteById(java.lang. Object)
	 */
	@Override
	public void deleteById(Long id) {
		dpianiFerieRepository.deleteById(id);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * it.micegroup.gestioneferie.pianificazioneferie.service.GenericEntityService#
	 * deleteByIdIn(java.lang. Object)
	 */
	@Override
	public void deleteByIdIn(Collection<Long> ids) {
		dpianiFerieRepository.deleteByIdIn(ids);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * it.micegroup.gestioneferie.pianificazioneferie.service.GenericEntityService#
	 * findByObjectKey(java.lang.String)
	 */
	@Override
	@Transactional(readOnly = true)
	public Optional<DpianiFerie> findByObjectKey(String id) {
		DpianiFerie dpianiFerie = new DpianiFerie(id);

		return findById(dpianiFerie.getSequIdPianoFerie());
	}

	@Override
	public Page<DpianiFerie> findByTheDstatiPianoFerie(DstatiPianoFerie parentEntity, Pageable pageable) {
		return dpianiFerieRepository.findByTheDstatiPianoFerie(parentEntity, pageable);
	}

	/**
	 * Convert DpianiFerieCriteria to a {@link Specification}
	 * 
	 * @param criteria
	 * @return
	 */
	private Specification<DpianiFerie> createSpecification(DpianiFerieCriteria criteria) {
		Specification<DpianiFerie> specification = Specification.where(null);

		if (criteria != null) {
			// Attribute filters
			if (criteria.getSequIdPianoFerie() != null) {
				specification = specification.and(filterService
						.generateRangeSpecification(criteria.getSequIdPianoFerie(), DpianiFerie_.sequIdPianoFerie));
			}
			if (criteria.getCodiPianoFerie() != null) {
				specification = specification.and(filterService
						.generateStringSpecification(criteria.getCodiPianoFerie(), DpianiFerie_.codiPianoFerie));
			}
			if (criteria.getDescPianoFerie() != null) {
				specification = specification.and(filterService
						.generateStringSpecification(criteria.getDescPianoFerie(), DpianiFerie_.descPianoFerie));
			}
			if (criteria.getNumeMinGgPresenza() != null) {
				specification = specification.and(filterService
						.generateRangeSpecification(criteria.getNumeMinGgPresenza(), DpianiFerie_.numeMinGgPresenza));
			}
			if (criteria.getNumeMinGgFerie() != null) {
				specification = specification.and(filterService.generateRangeSpecification(criteria.getNumeMinGgFerie(),
						DpianiFerie_.numeMinGgFerie));
			}
			if (criteria.getNumeMinGgDaPianificare() != null) {
				specification = specification.and(filterService.generateRangeSpecification(
						criteria.getNumeMinGgDaPianificare(), DpianiFerie_.numeMinGgDaPianificare));
			}
			if (criteria.getNumeMaxGgDaPianificare() != null) {
				specification = specification.and(filterService.generateRangeSpecification(
						criteria.getNumeMaxGgDaPianificare(), DpianiFerie_.numeMaxGgDaPianificare));
			}
			if (criteria.getDataScadenzaPresentazione() != null) {
				specification = specification.and(filterService.generateRangeSpecification(
						criteria.getDataScadenzaPresentazione(), DpianiFerie_.dataScadenzaPresentazione));
			}
			if (criteria.getDataInizioPeriodo() != null) {
				specification = specification.and(filterService
						.generateRangeSpecification(criteria.getDataInizioPeriodo(), DpianiFerie_.dataInizioPeriodo));
			}
			if (criteria.getDataFinePeriodo() != null) {
				specification = specification.and(filterService
						.generateRangeSpecification(criteria.getDataFinePeriodo(), DpianiFerie_.dataFinePeriodo));
			}
			if (criteria.getFkAmministrazionePk() != null) {
				specification = specification.and(filterService.generateRangeSpecification(
						criteria.getFkAmministrazionePk(), DpianiFerie_.fkAmministrazionePk));
			}
			if (criteria.getFkRegolaPianoFeriePk() != null) {
				specification = specification.and(filterService.generateRangeSpecification(
						criteria.getFkRegolaPianoFeriePk(), DpianiFerie_.fkRegolaPianoFeriePk));
			}
			if (criteria.getFkStatoPianoFerie() != null) {
				specification = specification.and(filterService
						.generateRangeSpecification(criteria.getFkStatoPianoFerie(), DpianiFerie_.fkStatoPianoFerie));
			}
			if (criteria.getFkTipoOrganigramma() != null) {
				specification = specification.and(filterService
						.generateRangeSpecification(criteria.getFkTipoOrganigramma(), DpianiFerie_.fkTipoOrganigramma));
			}
			if (criteria.getFkUnitaFunzionale() != null) {
				specification = specification.and(filterService
						.generateRangeSpecification(criteria.getFkUnitaFunzionale(), DpianiFerie_.fkUnitaFunzionale));
			}
			if (criteria.getFkUnitaOrganizzativaPk() != null) {
				specification = specification.and(filterService.generateRangeSpecification(
						criteria.getFkUnitaOrganizzativaPk(), DpianiFerie_.fkUnitaOrganizzativaPk));
			}
			if (criteria.getFlagEliminato() != null) {
				specification = specification.and(filterService.generateStringSpecification(criteria.getFlagEliminato(),
						DpianiFerie_.flagEliminato));
			}
			if (criteria.getUtenteUltimaModifica() != null) {
				specification = specification.and(filterService.generateStringSpecification(
						criteria.getUtenteUltimaModifica(), DpianiFerie_.utenteUltimaModifica));
			}
			if (criteria.getDataUltimaModifica() != null) {
				specification = specification.and(filterService
						.generateRangeSpecification(criteria.getDataUltimaModifica(), DpianiFerie_.dataUltimaModifica));
			}
			if (criteria.getDataInizioRecord() != null) {
				specification = specification.and(filterService
						.generateRangeSpecification(criteria.getDataInizioRecord(), DpianiFerie_.dataInizioRecord));
			}
			if (criteria.getDataFineRecord() != null) {
				specification = specification.and(filterService.generateRangeSpecification(criteria.getDataFineRecord(),
						DpianiFerie_.dataFineRecord));
			}

			if (criteria.getTheDstatiPianoFerieObjectKey() != null) {
				DstatiPianoFerie dstatiPianoFerie = new DstatiPianoFerie(criteria.getTheDstatiPianoFerieObjectKey());

			}

		}
		return specification;
	}

	@Override
	@Transactional()
	public DpianiFerie bulkUpdate(DpianiFerie dpianiFerie) {
		List<DrichiesteApprovazPf> updateTheDrichiesteApprovazPf = dpianiFerie.getTheDrichiesteApprovazPf().stream()
				.filter(child -> !child.isDeletedEntityState()).collect(Collectors.toList());
		List<DrichiesteApprovazPf> deleteTheDrichiesteApprovazPf = dpianiFerie.getTheDrichiesteApprovazPf().stream()
				.filter(GenericEntity::isDeletedEntityState).collect(Collectors.toList());

		dpianiFerie.setTheDrichiesteApprovazPf(updateTheDrichiesteApprovazPf);
		List<DrichAdesionePf> updateTheDrichAdesionePf = dpianiFerie.getTheDrichAdesionePf().stream()
				.filter(child -> !child.isDeletedEntityState()).collect(Collectors.toList());
		List<DrichAdesionePf> deleteTheDrichAdesionePf = dpianiFerie.getTheDrichAdesionePf().stream()
				.filter(GenericEntity::isDeletedEntityState).collect(Collectors.toList());

		dpianiFerie.setTheDrichAdesionePf(updateTheDrichAdesionePf);
		List<DammtiPianiFerie> updateTheDammtiPianiFerie = dpianiFerie.getTheDammtiPianiFerie().stream()
				.filter(child -> !child.isDeletedEntityState()).collect(Collectors.toList());
		List<DammtiPianiFerie> deleteTheDammtiPianiFerie = dpianiFerie.getTheDammtiPianiFerie().stream()
				.filter(GenericEntity::isDeletedEntityState).collect(Collectors.toList());

		dpianiFerie.setTheDammtiPianiFerie(updateTheDammtiPianiFerie);

		DpianiFerie update = this.update(dpianiFerie);

		deleteTheDrichiesteApprovazPf
				.forEach(child -> drichiesteApprovazPfService.deleteById(child.getDrichiesteApprovazPfKey()));
		deleteTheDrichAdesionePf.forEach(child -> drichAdesionePfService.deleteById(child.getDrichAdesionePfKey()));
		deleteTheDammtiPianiFerie.forEach(child -> dammtiPianiFerieService.deleteById(child.getDammtiPianiFerieKey()));
		return update;
	}
}
