package vn.azteam.tpf.service;

import java.util.*;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.JoinType;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.jhipster.service.filter.LongFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import io.github.jhipster.service.QueryService;

import vn.azteam.tpf.domain.TBGuidelineDetails;
import vn.azteam.tpf.domain.*; // for static metamodels
import vn.azteam.tpf.repository.TBGuidelineDetailsRepository;
import vn.azteam.tpf.repository.search.TBGuidelineDetailsSearchRepository;
import vn.azteam.tpf.service.dto.*;
import vn.azteam.tpf.service.mapper.TBGuidelineActivityMapper;
import vn.azteam.tpf.service.mapper.TBGuidelineDetailsMapper;
import vn.azteam.tpf.web.rest.errors.BadRequestAlertException;
import vn.azteam.tpf.web.rest.errors.ErrorCodes;

/**
 * Service for executing complex queries for TBGuidelineDetails entities in the database.
 * The main input is a {@link TBGuidelineDetailsCriteria} which gets converted to {@link Specification},
 * in a way that all the filters must apply.
 * It returns a {@link List} of {@link TBGuidelineDetailsDTO} or a {@link Page} of {@link TBGuidelineDetailsDTO} which fulfills the criteria.
 */
@Service
@Transactional(readOnly = true)
public class TBGuidelineDetailsQueryService extends QueryService<TBGuidelineDetails> {

    private final Logger log = LoggerFactory.getLogger(TBGuidelineDetailsQueryService.class);

    private static final String ENTITY_NAME = "tBGuidelineDetails";

    private final TBGuidelineDetailsRepository tBGuidelineDetailsRepository;

    private final TBGuidelineDetailsService tBGuidelineDetailsService;

    private final TBGuidelineDetailsMapper tBGuidelineDetailsMapper;

    private final TBGuidelineDetailsSearchRepository tBGuidelineDetailsSearchRepository;

    private final TBLimitsQueryService tBLimitsQueryService;

    private final TBSuppliesUsingDetailsService tBSuppliesUsingDetailsService;

    private final TBSuppliesUsingDetailsQueryService tBSuppliesUsingDetailsQueryService;

    private final TBLimitsService tBLimitsService;

    private final EntityManager em;

    private final ObjectMapper objectMapper;

    private final TBActivityTypeQueryService tBActivityTypeQueryService;

    private final TBGuidelineActivityQueryService tbGuidelineActivityQueryService;

    private final TBGuidelinePeriodService tBGuidelinePeriodService;

    private final  TBGuidelineActivityService tbGuidelineActivityService;

    private final TBGuidelineDetailsObjectUpdateService tbGDObjectUpdateService;

    private final TBSuppliesUsingDetailsService tbSuppliesUsingDetailsService;

    private final TBGuidelineDetailsObjectUpdateQueryService tbGDObjectUpdateQueryService;

    private final TBSuppliesUsingDetailsQueryService tbSuppliesUsingDetailsQueryService;

    private final TBGuidelineActivityMapper tbGuidelineActivityMapper;

    public TBGuidelineDetailsQueryService(
        TBGuidelineDetailsRepository tBGuidelineDetailsRepository,
        TBGuidelineDetailsService tBGuidelineDetailsService, TBGuidelineDetailsMapper tBGuidelineDetailsMapper,
        TBGuidelineDetailsSearchRepository tBGuidelineDetailsSearchRepository,
        TBLimitsQueryService tBLimitsQueryService,
        TBSuppliesUsingDetailsService tBSuppliesUsingDetailsService,
        TBSuppliesUsingDetailsQueryService tBSuppliesUsingDetailsQueryService,
        TBLimitsService tBLimitsService,
        EntityManager em,
        ObjectMapper objectMapper,
        TBActivityTypeQueryService tBActivityTypeQueryService,
        TBGuidelineActivityQueryService tbGuidelineActivityQueryService,
        TBGuidelinePeriodService tBGuidelinePeriodService,
        TBGuidelineActivityService tbGuidelineActivityService,
        TBGuidelineDetailsObjectUpdateService tbGDObjectUpdateService,
        TBSuppliesUsingDetailsService tbSuppliesUsingDetailsService,
        TBGuidelineDetailsObjectUpdateQueryService tbGDObjectUpdateQueryService,
        TBSuppliesUsingDetailsQueryService tbSuppliesUsingDetailsQueryService,
        TBGuidelineActivityMapper tbGuidelineActivityMapper) {
        this.tBGuidelineDetailsRepository = tBGuidelineDetailsRepository;
        this.tBGuidelineDetailsService = tBGuidelineDetailsService;
        this.tBGuidelineDetailsMapper = tBGuidelineDetailsMapper;
        this.tBGuidelineDetailsSearchRepository = tBGuidelineDetailsSearchRepository;
        this.tBLimitsQueryService = tBLimitsQueryService;
        this.tBSuppliesUsingDetailsService = tBSuppliesUsingDetailsService;
        this.tBSuppliesUsingDetailsQueryService = tBSuppliesUsingDetailsQueryService;
        this.tBLimitsService = tBLimitsService;
        this.em = em;
        this.objectMapper = objectMapper;
        this.tBActivityTypeQueryService = tBActivityTypeQueryService;
        this.tbGuidelineActivityQueryService = tbGuidelineActivityQueryService;
        this.tBGuidelinePeriodService = tBGuidelinePeriodService;
        this.tbGuidelineActivityService = tbGuidelineActivityService;
        this.tbGDObjectUpdateService = tbGDObjectUpdateService;
        this.tbSuppliesUsingDetailsService = tbSuppliesUsingDetailsService;
        this.tbGDObjectUpdateQueryService = tbGDObjectUpdateQueryService;
        this.tbSuppliesUsingDetailsQueryService = tbSuppliesUsingDetailsQueryService;
        this.tbGuidelineActivityMapper = tbGuidelineActivityMapper;
    }

    /**
     * Return a {@link List} of {@link TBGuidelineDetailsDTO} which matches the criteria from the database
     *
     * @param criteria The object which holds all the filters, which the entities should match.
     * @return the matching entities.
     */
    @Transactional(readOnly = true)
    public List<TBGuidelineDetailsDTO> findByCriteria(TBGuidelineDetailsCriteria criteria) {
        log.debug("find by criteria : {}", criteria);
        final Specification<TBGuidelineDetails> specification = createSpecification(criteria);
        return tBGuidelineDetailsMapper.toDto(tBGuidelineDetailsRepository.findAll(specification));
    }

    /**
     * Return a {@link Page} of {@link TBGuidelineDetailsDTO} which matches the criteria from the database
     *
     * @param criteria The object which holds all the filters, which the entities should match.
     * @param page     The page, which should be returned.
     * @return the matching entities.
     */
    @Transactional(readOnly = true)
    public Page<TBGuidelineDetailsDTO> findByCriteria(TBGuidelineDetailsCriteria criteria, Pageable page) {
        log.debug("find by criteria : {}, page: {}", criteria, page);
        final Specification<TBGuidelineDetails> specification = createSpecification(criteria);
        return tBGuidelineDetailsRepository.findAll(specification, page)
            .map(tBGuidelineDetailsMapper::toDto);
    }

    /**
     * Return the number of matching entities in the database
     *
     * @param criteria The object which holds all the filters, which the entities should match.
     * @return the number of matching entities.
     */
    @Transactional(readOnly = true)
    public long countByCriteria(TBGuidelineDetailsCriteria criteria) {
        log.debug("count by criteria : {}", criteria);
        final Specification<TBGuidelineDetails> specification = createSpecification(criteria);
        return tBGuidelineDetailsRepository.count(specification);
    }

    /**
     * Function to convert TBGuidelineDetailsCriteria to a {@link Specification}
     */
    private Specification<TBGuidelineDetails> createSpecification(TBGuidelineDetailsCriteria criteria) {
        Specification<TBGuidelineDetails> specification = Specification.where(null);
        if (criteria != null) {
            if (criteria.getId() != null) {
                specification = specification.and(buildSpecification(criteria.getId(), TBGuidelineDetails_.id));
            }
            if (criteria.getAgeDayStart() != null) {
                specification = specification.and(buildRangeSpecification(criteria.getAgeDayStart(), TBGuidelineDetails_.ageDayStart));
            }
            if (criteria.getAgeDayEnd() != null) {
                specification = specification.and(buildRangeSpecification(criteria.getAgeDayEnd(), TBGuidelineDetails_.ageDayEnd));
            }
            if (criteria.getTbGuidelineId() != null) {
                specification = specification.and(buildSpecification(criteria.getTbGuidelineId(),
                    root -> root.join(TBGuidelineDetails_.tbGuideline, JoinType.LEFT).get(TBGuideline_.id)));
            }
            if (criteria.getTbGuidelinePeriodId() != null) {
                specification = specification.and(buildSpecification(criteria.getTbGuidelinePeriodId(),
                    root -> root.join(TBGuidelineDetails_.tbGuidelinePeriod, JoinType.LEFT).get(TBGuidelinePeriod_.id)));
            }
        }
        return specification;
    }

    public Optional<TBGuidelineDetailsWrapperDTO> getTBGuidelineDetails(Long id) {
        Optional<TBGuidelineDetailsDTO> tbGuidelineDetailsDTO = tBGuidelineDetailsRepository.findById(id)
            .map(tBGuidelineDetailsMapper::toDto);
        if (!tbGuidelineDetailsDTO.isPresent())
            return Optional.empty();

        TBGuidelineDetailsDTO guidelineDetailsDTO = tbGuidelineDetailsDTO.get();
        TBGuidelineDetailsWrapperDTO tbGuidelineDetailsGetDTO = new TBGuidelineDetailsWrapperDTO();
        tbGuidelineDetailsGetDTO.setId(guidelineDetailsDTO.getId());
        tbGuidelineDetailsGetDTO.setAgeDayStart(guidelineDetailsDTO.getAgeDayStart());
        tbGuidelineDetailsGetDTO.setAgeDayEnd(guidelineDetailsDTO.getAgeDayEnd());
        tbGuidelineDetailsGetDTO.setTbGuidelineId(guidelineDetailsDTO.getTbGuidelineId());
        tbGuidelineDetailsGetDTO.setTbGuidelinePeriodId(guidelineDetailsDTO.getTbGuidelinePeriodId());

        // List of activity of guideline details
        List<Object> activityGuidelines = new ArrayList<>();
        TBGuidelineActivityCriteria guidelineDetailsCriteria = new TBGuidelineActivityCriteria();
        LongFilter guidelineDetailsIdFilter = new LongFilter();
        guidelineDetailsIdFilter.setEquals(guidelineDetailsDTO.getId());
        guidelineDetailsCriteria.setTbGuidelineDetailsId(guidelineDetailsIdFilter);
        List<TBGuidelineActivityDTO> tbGuidelineActivities = tbGuidelineActivityQueryService.findByCriteria(guidelineDetailsCriteria);
        activityGuidelines.addAll(tbGuidelineActivities);
        tbGuidelineDetailsGetDTO.setActivityGuidelines(activityGuidelines);

        // Limits info
        TBLimitsCriteria limitsCriteria = new TBLimitsCriteria();
        LongFilter guidelineDetailLimitFilter = new LongFilter();
        guidelineDetailLimitFilter.setEquals(id);
        limitsCriteria.setTbGuidelineDetailsId(guidelineDetailLimitFilter);
        tbGuidelineDetailsGetDTO.setLimits(tBLimitsQueryService.findByCriteria(limitsCriteria));

        return Optional.of(tbGuidelineDetailsGetDTO);
    }

    @Transactional()
    public TBGuidelineDetailsDTO createTBGuidelineDetails(TBGuidelineDetailsWrapperDTO tbGuidelineDetailsWrapperDTO) {
        log.debug("Request to create TBGuidelineDetails : {}", tbGuidelineDetailsWrapperDTO);

        List<TBGuidelineDetailsDTO> guidelineDetailsDTOS =
            getTbGuidelineDetailsByGuidelineId(tbGuidelineDetailsWrapperDTO);
        if (findIntervalOverlaps(guidelineDetailsDTOS, tbGuidelineDetailsWrapperDTO, true)) {
            throw new BadRequestAlertException(
                "1034",
                "tBGuidelineDetails",
                "1034");
        }
        return saveGuidelineDetail(tbGuidelineDetailsWrapperDTO);
    }

    @Transactional()
    public TBGuidelineDetailsDTO updateTBGuidelineDetails(TBGuidelineDetailsWrapperDTO tbGuidelineDetailsWrapperDTO) {
        log.debug("Request to update TBGuidelineDetails : {}", tbGuidelineDetailsWrapperDTO);

        Optional<TBGuidelineDetailsDTO> tbGuidelineDetailsDTO = tBGuidelineDetailsRepository
            .findById(tbGuidelineDetailsWrapperDTO.getId())
            .map(tBGuidelineDetailsMapper::toDto);

        if (!tbGuidelineDetailsDTO.isPresent())
            return null;

        List<TBGuidelineDetailsDTO> guidelineDetailsDTOS =
            getTbGuidelineDetailsByGuidelineId(tbGuidelineDetailsWrapperDTO);
        if (findIntervalOverlaps(guidelineDetailsDTOS, tbGuidelineDetailsWrapperDTO, false)) {
            throw new BadRequestAlertException(
                "1034",
                "tBGuidelineDetails",
                "1034");
        }

        TBGuidelineDetailsDTO guidelineDetailsDTO = tbGuidelineDetailsDTO.get();
        guidelineDetailsDTO.setAgeDayStart(tbGuidelineDetailsWrapperDTO.getAgeDayStart());
        guidelineDetailsDTO.setAgeDayEnd(tbGuidelineDetailsWrapperDTO.getAgeDayEnd());
        guidelineDetailsDTO.setTbGuidelineId(tbGuidelineDetailsWrapperDTO.getTbGuidelineId());
        guidelineDetailsDTO.setTbGuidelinePeriodId(tbGuidelineDetailsWrapperDTO.getTbGuidelinePeriodId());

        guidelineDetailsDTO = tBGuidelineDetailsService.save(guidelineDetailsDTO);

        //Delete and create new limit environment of guideline details
        deleteLimitByGuidelineDetail(tbGuidelineDetailsWrapperDTO.getId());
        createLimitGuidelineDetail(tbGuidelineDetailsWrapperDTO, guidelineDetailsDTO);
        return guidelineDetailsDTO;
    }

    @Transactional()
    public void deleteTBGuidelineDetails(Long id) {
        deleteAllCommonActivityGuidelineDetails(id);
        deleteLimitByGuidelineDetail(id);
        deleteGuidelineDetail(id);
    }

    private int deleteGuidelineDetail(Long guidelineDetailId) {
        String sqlString = String.format(
            "delete from tb_guideline_details where id = :guidelineDetailId");

        return em.createNativeQuery(sqlString)
            .setParameter("guidelineDetailId", guidelineDetailId)
            .executeUpdate();
    }

    private TBGuidelineDetailsDTO saveGuidelineDetail(TBGuidelineDetailsWrapperDTO tbGuidelineDetailsWrapperDTO) {
        TBGuidelineDetailsDTO guidelineDetailsDTO = new TBGuidelineDetailsDTO();
        guidelineDetailsDTO.setAgeDayStart(tbGuidelineDetailsWrapperDTO.getAgeDayStart());
        guidelineDetailsDTO.setAgeDayEnd(tbGuidelineDetailsWrapperDTO.getAgeDayEnd());
        guidelineDetailsDTO.setTbGuidelineId(tbGuidelineDetailsWrapperDTO.getTbGuidelineId());
        guidelineDetailsDTO.setTbGuidelinePeriodId(tbGuidelineDetailsWrapperDTO.getTbGuidelinePeriodId());

        TBGuidelineDetails tBGuidelineDetails = tBGuidelineDetailsMapper.toEntity(guidelineDetailsDTO);
        tBGuidelineDetails = tBGuidelineDetailsRepository.save(tBGuidelineDetails);
        TBGuidelineDetailsDTO result = tBGuidelineDetailsMapper.toDto(tBGuidelineDetails);
        tBGuidelineDetailsSearchRepository.save(tBGuidelineDetails);
        return result;
    }

    private void createLimitGuidelineDetail(TBGuidelineDetailsWrapperDTO tbGuidelineDetailsWrapperDTO,
                                            TBGuidelineDetailsDTO result) {
        List<TBLimitsDTO> limits = tbGuidelineDetailsWrapperDTO.getLimits();
        for (TBLimitsDTO limitsDTO : limits) {
            limitsDTO.setTbGuidelineDetailsId(result.getId());
            tBLimitsService.save(limitsDTO);
        }
    }

    private void deleteLimitByGuidelineDetail(Long guidelineDetailId) {
        String sqlString = String.format(
            "delete from tb_limits \n" +
                " where tb_guideline_details_id = :guidelineDetailId");
        Query query = em
            .createNativeQuery(sqlString)
            .setParameter("guidelineDetailId", guidelineDetailId);

        query.executeUpdate();
    }

    private List<TBGuidelineDetailsDTO> getTbGuidelineDetailsByGuidelineId(TBGuidelineDetailsWrapperDTO tbGuidelineDetailsWrapperDTO) {
        TBGuidelineDetailsCriteria guidelineDetailsCriteria = new TBGuidelineDetailsCriteria();
        LongFilter guidelineFilter = new LongFilter();
        guidelineFilter.setEquals(tbGuidelineDetailsWrapperDTO.getTbGuidelineId());
        guidelineDetailsCriteria.setTbGuidelineId(guidelineFilter);
        return findByCriteria(guidelineDetailsCriteria);
    }

    public static Boolean findIntervalOverlaps(List<TBGuidelineDetailsDTO> tbGuidelineDetailsDTOS,
                                               TBGuidelineDetailsWrapperDTO tbGuidelineDetailsWrapperDTO,
                                               Boolean isAdd) {
        List<TBGuidelineDetailsDTO> intervals = new ArrayList<>(tbGuidelineDetailsDTOS);
        if (isAdd) {
            TBGuidelineDetailsDTO guidelineDetailsDTO = new TBGuidelineDetailsDTO();
            guidelineDetailsDTO.setAgeDayStart(tbGuidelineDetailsWrapperDTO.getAgeDayStart());
            guidelineDetailsDTO.setAgeDayEnd(tbGuidelineDetailsWrapperDTO.getAgeDayEnd());
            intervals.add(guidelineDetailsDTO);
        } else {
            TBGuidelineDetailsDTO guidelineDetailsResult = intervals.stream()
                .filter(guidelineDetailsDTO -> tbGuidelineDetailsWrapperDTO.getId().equals(guidelineDetailsDTO.getId()))
                .findAny()
                .orElse(null);
            if (guidelineDetailsResult != null) {
                guidelineDetailsResult.setAgeDayStart(tbGuidelineDetailsWrapperDTO.getAgeDayStart());
                guidelineDetailsResult.setAgeDayEnd(tbGuidelineDetailsWrapperDTO.getAgeDayEnd());
            }
        }

        for (TBGuidelineDetailsDTO guidelineDetailsDTO : intervals) {
            if (guidelineDetailsDTO.getAgeDayEnd() == null) {
                guidelineDetailsDTO.setAgeDayEnd(guidelineDetailsDTO.getAgeDayStart());
            }
        }

        if (intervals.size() <= 1) {
            return false;
        }

        // find the last end time
        int lastTime = -1;
        for (int i = 0; i < intervals.size(); i++) {
            if (lastTime < intervals.get(i).getAgeDayEnd())
                lastTime = intervals.get(i).getAgeDayEnd();
        }

        int[] count = new int[lastTime + 2];
        boolean isIntersect = false;
        for (int i = 0; i < intervals.size(); i++) {
            TBGuidelineDetailsDTO current = intervals.get(i);
            // mark the count +1 for start time and -1 for end time
            count[current.getAgeDayStart()]++;
            count[current.getAgeDayEnd() + 1]--;
        }

        // now iterate the count array and keep adding values and
        // if at any index sum is > 1 that means another interval started before the previous one ended
        // means intervals are overlapping
        int sum = 0;
        for (int i = 0; i < count.length; i++) {
            sum += count[i];
            if (sum > 1) {
                isIntersect = true;
                break;
            }
        }
        return isIntersect;
    }

    public void validateAgeDayRange(TBGuidelineDetailsWrapperDTO tBGuidelineDetailsDTO) {
        if (tBGuidelineDetailsDTO.getAgeDayStart() == null) {
            throw new BadRequestAlertException("1035", "tbGuidelineDetails", "1035");
        }
        if (tBGuidelineDetailsDTO.getAgeDayEnd() != null &&
            tBGuidelineDetailsDTO.getAgeDayEnd() < tBGuidelineDetailsDTO.getAgeDayStart()) {
            throw new BadRequestAlertException("1036", ENTITY_NAME, "1036");
        }
    }

    public TBGuidelinePeriodDTO validatePeriod(TBGuidelineDetailsWrapperDTO tBGuidelineDetailsDTO) {
        TBGuidelinePeriodDTO tbGuidelinePeriodDTO = tBGuidelinePeriodService.findOne(tBGuidelineDetailsDTO.getTbGuidelinePeriodId())
            .orElseThrow(() -> new BadRequestAlertException(ErrorCodes.ACCESS_GUIDELINE_DENIED, ENTITY_NAME, ErrorCodes.ACCESS_GUIDELINE_DENIED));

        if (tBGuidelineDetailsDTO.getAgeDayStart() != null &&
            tBGuidelineDetailsDTO.getAgeDayStart() < tbGuidelinePeriodDTO.getAgeDayStart()) {
            throw new BadRequestAlertException("1037", ENTITY_NAME, "1037");
        }

        if (tBGuidelineDetailsDTO.getAgeDayStart() != null &&
            tBGuidelineDetailsDTO.getAgeDayStart() > tbGuidelinePeriodDTO.getAgeDayEnd()) {
            throw new BadRequestAlertException("1038", ENTITY_NAME, "1038");
        }

        return tbGuidelinePeriodDTO;
    }

    @Transactional(readOnly = false)
    public Long createCommonActivityGuidelineDetails(TBCommonActivityGDDTO tbCommonActivityGDDTO) {
        //Save guideline activity
        TBGuidelineActivityDTO tbGuidelineActivityDTO = new TBGuidelineActivityDTO();
        tbGuidelineActivityDTO.setTbActivityTypeId(tbCommonActivityGDDTO.getTbGuidelineActivityDTO().getTbActivityTypeId());
        tbGuidelineActivityDTO.setTbGuidelineDetailsId(tbCommonActivityGDDTO.getTbGuidelineActivityDTO().getTbGuidelineDetailsId());
        tbGuidelineActivityDTO.setExternalTable(tbCommonActivityGDDTO.getTbGuidelineActivityDTO().getExternalTable());
        TBGuidelineActivityDTO result = tbGuidelineActivityService.save(tbGuidelineActivityDTO);

        //Save list guideline details object update
        if(tbCommonActivityGDDTO.getTbGDObjectUpdateDTOList() != null
            && tbCommonActivityGDDTO.getTbGDObjectUpdateDTOList().size() > 0) {
            for (TBGuidelineDetailsObjectUpdateDTO tbGDObjectUpdateDTO : tbCommonActivityGDDTO.getTbGDObjectUpdateDTOList()) {
                tbGDObjectUpdateDTO.setTbGuidelineActivityId(result.getId());
                tbGDObjectUpdateService.save(tbGDObjectUpdateDTO);
            }
        }

        //Save list tb_supplies_using_details
        if(tbCommonActivityGDDTO.getTbSuppliesUsingDetailsDTOs() != null
            && tbCommonActivityGDDTO.getTbSuppliesUsingDetailsDTOs().size() > 0) {
            for (TBSuppliesUsingDetailsDTO tbSuppliesUsingDetailsDTO : tbCommonActivityGDDTO.getTbSuppliesUsingDetailsDTOs()) {
                tbSuppliesUsingDetailsDTO.setTbGuidelineActivityId(result.getId());
                tbSuppliesUsingDetailsService.save(tbSuppliesUsingDetailsDTO);
            }
        }
        return result.getId();
    }

    public TBCommonActivityGDDTO getDetailCommonActivityGuidelineDetails(Long tbGuidelineActivityId) {
        TBCommonActivityGDDTO tbCommonActivityGDDTO = new TBCommonActivityGDDTO();

        // Get TBGuidelineActivity
        TBGuidelineActivityDTO tbGDActivityDTO = tbGuidelineActivityService.findOne(tbGuidelineActivityId).get();
        tbCommonActivityGDDTO.setTbGuidelineActivityDTO(tbGDActivityDTO);

        // Get TBSuppliesUsingDetails by tbActivityId
        TBSuppliesUsingDetailsCriteria tbSuppliesUsingDetailsCriteria = new TBSuppliesUsingDetailsCriteria();
        LongFilter tbGDActivityFilter = new LongFilter();
        tbGDActivityFilter.setEquals(tbGuidelineActivityId);
        tbSuppliesUsingDetailsCriteria.setTbGuidelineActivityId(tbGDActivityFilter);
        List<TBSuppliesUsingDetailsDTO> tbSuppliesUsingDetailsDTOs = tbSuppliesUsingDetailsQueryService.findByCriteria(tbSuppliesUsingDetailsCriteria);
        tbCommonActivityGDDTO.setTbSuppliesUsingDetailsDTOs(tbSuppliesUsingDetailsDTOs);

        // Delete TBObjectUpdate by tbActivityId
        TBGuidelineDetailsObjectUpdateCriteria tbGDObjectUpdateCriteria = new TBGuidelineDetailsObjectUpdateCriteria();
        tbGDObjectUpdateCriteria.setTbGuidelineActivityId(tbGDActivityFilter);
        List<TBGuidelineDetailsObjectUpdateDTO> tbGDObjectUpdateDTOs = tbGDObjectUpdateQueryService.findByCriteria(tbGDObjectUpdateCriteria);
        tbCommonActivityGDDTO.setTbGDObjectUpdateDTOList(tbGDObjectUpdateDTOs);
        return tbCommonActivityGDDTO;
    }

    @Transactional(readOnly = false)
    public Long updateCommonActivityGuidelineDetails(TBCommonActivityGDDTO tbCommonActivityGDDTO) {
        //Update guideline activity
        TBGuidelineActivityDTO tbGDActivityDTO = tbGuidelineActivityService.findOne(tbCommonActivityGDDTO.getTbGuidelineActivityDTO().getId()).get();
        tbGDActivityDTO.setTbActivityTypeId(tbCommonActivityGDDTO.getTbGuidelineActivityDTO().getTbActivityTypeId());
        tbGDActivityDTO.setTbGuidelineDetailsId(tbCommonActivityGDDTO.getTbGuidelineActivityDTO().getTbGuidelineDetailsId());
        tbGDActivityDTO.setExternalTable(tbCommonActivityGDDTO.getTbGuidelineActivityDTO().getExternalTable());
        TBGuidelineActivityDTO result = tbGuidelineActivityService.save(tbGDActivityDTO);

        // Delete TBSuppliesUsingDetails by tbGuidelineActivityId
        TBSuppliesUsingDetailsCriteria tbSuppliesUsingDetailsCriteria = new TBSuppliesUsingDetailsCriteria();
        LongFilter tbGuidelineActivityFilter = new LongFilter();
        tbGuidelineActivityFilter.setEquals(result.getId());
        tbSuppliesUsingDetailsCriteria.setTbGuidelineActivityId(tbGuidelineActivityFilter);
        List<TBSuppliesUsingDetailsDTO> tbSuppliesUsingDetailsDTOs = tbSuppliesUsingDetailsQueryService.findByCriteria(tbSuppliesUsingDetailsCriteria);
        if(tbSuppliesUsingDetailsDTOs.size() > 0) {
            for(TBSuppliesUsingDetailsDTO tbSuppliesUsingDetailsDTO : tbSuppliesUsingDetailsDTOs) {
                tbSuppliesUsingDetailsService.delete(tbSuppliesUsingDetailsDTO.getId());
            }
        }

        //Create list tb_supplies_using_details
        if(tbCommonActivityGDDTO.getTbSuppliesUsingDetailsDTOs() != null
            && tbCommonActivityGDDTO.getTbSuppliesUsingDetailsDTOs().size() > 0) {
            for (TBSuppliesUsingDetailsDTO tbSuppliesUsingDetailsDTO : tbCommonActivityGDDTO.getTbSuppliesUsingDetailsDTOs()) {
                tbSuppliesUsingDetailsDTO.setTbGuidelineActivityId(result.getId());
                tbSuppliesUsingDetailsService.save(tbSuppliesUsingDetailsDTO);
            }
        }

        // Delete TBGuidelineDetailsObjectUpdate by tbGuidelineActivityId
        TBGuidelineDetailsObjectUpdateCriteria tbGDObjectUpdateCriteria = new TBGuidelineDetailsObjectUpdateCriteria();
        tbGDObjectUpdateCriteria.setTbGuidelineActivityId(tbGuidelineActivityFilter);
        List<TBGuidelineDetailsObjectUpdateDTO> tbGDObjectUpdateDTOs = tbGDObjectUpdateQueryService.findByCriteria(tbGDObjectUpdateCriteria);
        if(tbGDObjectUpdateDTOs.size() > 0) {
            for(TBGuidelineDetailsObjectUpdateDTO tbGDObjectUpdateDTO : tbGDObjectUpdateDTOs) {
                tbGDObjectUpdateService.delete(tbGDObjectUpdateDTO.getId());
            }
        }

        // Create list guideline details object update
        if(tbCommonActivityGDDTO.getTbGDObjectUpdateDTOList() != null
            && tbCommonActivityGDDTO.getTbGDObjectUpdateDTOList().size() > 0) {
            for (TBGuidelineDetailsObjectUpdateDTO tbGDObjectUpdateDTO : tbCommonActivityGDDTO.getTbGDObjectUpdateDTOList()) {
                tbGDObjectUpdateDTO.setTbGuidelineActivityId(result.getId());
                tbGDObjectUpdateService.save(tbGDObjectUpdateDTO);
            }
        }
        return result.getId();
    }

    @Transactional(readOnly = false)
    public void deleteCommonActivityGuidelineDetails(Long tbGuidelineActivityId) {
        // Delete TBSuppliesUsingDetails by tbGuidelineActivityId
        TBSuppliesUsingDetailsCriteria tbSuppliesUsingDetailsCriteria = new TBSuppliesUsingDetailsCriteria();
        LongFilter tbGuidelineActivityFilter = new LongFilter();
        tbGuidelineActivityFilter.setEquals(tbGuidelineActivityId);
        tbSuppliesUsingDetailsCriteria.setTbGuidelineActivityId(tbGuidelineActivityFilter);
        List<TBSuppliesUsingDetailsDTO> tbSuppliesUsingDetailsDTOs = tbSuppliesUsingDetailsQueryService.findByCriteria(tbSuppliesUsingDetailsCriteria);
        if(tbSuppliesUsingDetailsDTOs.size() > 0) {
            for(TBSuppliesUsingDetailsDTO tbSuppliesUsingDetailsDTO : tbSuppliesUsingDetailsDTOs) {
                tbSuppliesUsingDetailsService.delete(tbSuppliesUsingDetailsDTO.getId());
            }
        }

        // Delete TBGuidelineDetailsObjectUpdate by tbGuidelineActivityId
        TBGuidelineDetailsObjectUpdateCriteria tbGDObjectUpdateCriteria = new TBGuidelineDetailsObjectUpdateCriteria();
        tbGDObjectUpdateCriteria.setTbGuidelineActivityId(tbGuidelineActivityFilter);
        List<TBGuidelineDetailsObjectUpdateDTO> tbGDObjectUpdateDTOs = tbGDObjectUpdateQueryService.findByCriteria(tbGDObjectUpdateCriteria);
        if(tbGDObjectUpdateDTOs.size() > 0) {
            for(TBGuidelineDetailsObjectUpdateDTO tbGDObjectUpdateDTO : tbGDObjectUpdateDTOs) {
                tbGDObjectUpdateService.delete(tbGDObjectUpdateDTO.getId());
            }
        }
        // Delete TBGuidelineActivity
        tbGuidelineActivityService.delete(tbGuidelineActivityId);
    }

    @Transactional(readOnly = false)
    public void deleteAllCommonActivityGuidelineDetails(Long tbGuidelineDetailsId) {
        LongFilter tbGuidelineDetailIdFilter = new LongFilter();
        tbGuidelineDetailIdFilter.setEquals(tbGuidelineDetailsId);
        TBGuidelineActivityCriteria criteria = new TBGuidelineActivityCriteria();
        criteria.setTbGuidelineDetailsId(tbGuidelineDetailIdFilter);
        List<TBGuidelineActivityDTO> tbGuidelineActivityDTOs = tbGuidelineActivityQueryService.findByCriteria(criteria);
        for (TBGuidelineActivityDTO tbGuidelineActivityDTO: tbGuidelineActivityDTOs) {
            deleteCommonActivityGuidelineDetails(tbGuidelineActivityDTO.getId());
        }
    }

    @Transactional(readOnly = false)
    public int duplicateTBLimits(Long tbGuidelineDetailsId, Long duplicateTBGuidelineDetailsId) {
        String duplicateSqlTBLimits = "INSERT INTO tb_limits \n" +
                                      "(upper_limit, lower_limit, tb_guideline_details_id, tb_environmental_parameter_id) \n" +
                                      "SELECT \n" +
                                       "upper_limit,lower_limit, :duplicateTBGuidelineDetailsId, tb_environmental_parameter_id \n" +
                                      "FROM \n" +
                                       "tb_limits \n" +
                                       "WHERE \n" +
                                       "tb_guideline_details_id = :tbGuidelineDetailsId";
        Query query = em.createNativeQuery(duplicateSqlTBLimits);
        query.setParameter("duplicateTBGuidelineDetailsId", duplicateTBGuidelineDetailsId);
        query.setParameter("tbGuidelineDetailsId", tbGuidelineDetailsId);
        return query.executeUpdate();
    }

    @Transactional(readOnly = false)
    public void duplicateTBGuidelineActivity(Long tbGuidelineDetailsId, Long duplicateTBGuidelineDetailsId) {
        String tbGuidelineActivities = "SELECT * \n" +
            "FROM \n" +
            "tb_guideline_activity \n" +
            "WHERE \n" +
            "tb_guideline_details_id = :tbGuidelineDetailsId";
        Query query = em.createNativeQuery(tbGuidelineActivities, TBGuidelineActivity.class);
        query.setParameter("tbGuidelineDetailsId", tbGuidelineDetailsId);
        List<TBGuidelineActivityDTO> tbGuidelineActivityDTOs = tbGuidelineActivityMapper.toDto(query.getResultList());
        for(TBGuidelineActivityDTO tbGuidelineActivityDTO : tbGuidelineActivityDTOs) {
            //Duplicate TBGuidelineActivity
            TBGuidelineActivityDTO duplicatedGuidelineActivityDTO = new TBGuidelineActivityDTO();
            duplicatedGuidelineActivityDTO.setTbActivityTypeId(tbGuidelineActivityDTO.getTbActivityTypeId());
            duplicatedGuidelineActivityDTO.setTbGuidelineDetailsId(duplicateTBGuidelineDetailsId);
            duplicatedGuidelineActivityDTO.setExternalTable(tbGuidelineActivityDTO.getExternalTable());
            duplicatedGuidelineActivityDTO = tbGuidelineActivityService.save(duplicatedGuidelineActivityDTO);

            LongFilter tbGuidelineActivityIdFilter = new LongFilter();
            tbGuidelineActivityIdFilter.setEquals(tbGuidelineActivityDTO.getId());

            //Duplicate Guideline Object Update

            TBGuidelineDetailsObjectUpdateCriteria tbGDObjectUpdateCriteria = new TBGuidelineDetailsObjectUpdateCriteria();
            tbGDObjectUpdateCriteria.setTbGuidelineActivityId(tbGuidelineActivityIdFilter);
            List<TBGuidelineDetailsObjectUpdateDTO> tbGDObjectUpdateDTOs = tbGDObjectUpdateQueryService.findByCriteria(tbGDObjectUpdateCriteria);

            for (TBGuidelineDetailsObjectUpdateDTO tbGDObjectUpdateDTO : tbGDObjectUpdateDTOs) {
                TBGuidelineDetailsObjectUpdateDTO duplicatedTBGDObjectUpdateDTO = new TBGuidelineDetailsObjectUpdateDTO();
                duplicatedTBGDObjectUpdateDTO.setTbGuidelineActivityId(duplicatedGuidelineActivityDTO.getId());
                duplicatedTBGDObjectUpdateDTO.setIndex(tbGDObjectUpdateDTO.getIndex());
                duplicatedTBGDObjectUpdateDTO.setTbObjectParameterId(tbGDObjectUpdateDTO.getTbObjectParameterId());
                tbGDObjectUpdateService.save(duplicatedTBGDObjectUpdateDTO);
            }

            //Duplicate Supplies Using Details
            TBSuppliesUsingDetailsCriteria tbSuppliesUsingDetailsCriteria = new TBSuppliesUsingDetailsCriteria();
            tbSuppliesUsingDetailsCriteria.setTbGuidelineActivityId(tbGuidelineActivityIdFilter);
            List<TBSuppliesUsingDetailsDTO> tbSuppliesUsingDetailsDTOs = tbSuppliesUsingDetailsQueryService.findByCriteria(tbSuppliesUsingDetailsCriteria);

            for(TBSuppliesUsingDetailsDTO tbSuppliesUsingDetailsDTO : tbSuppliesUsingDetailsDTOs) {
                TBSuppliesUsingDetailsDTO duplicatedSuppliesUsingDetailsDTO = new TBSuppliesUsingDetailsDTO();
                duplicatedSuppliesUsingDetailsDTO.setDosage(tbSuppliesUsingDetailsDTO.getDosage());
                duplicatedSuppliesUsingDetailsDTO.setQuantity(tbSuppliesUsingDetailsDTO.getQuantity());
                duplicatedSuppliesUsingDetailsDTO.setCost(tbSuppliesUsingDetailsDTO.getCost());
                duplicatedSuppliesUsingDetailsDTO.setHowToUse(tbSuppliesUsingDetailsDTO.getHowToUse());
                duplicatedSuppliesUsingDetailsDTO.setTbEquipmentOfCustomerId(tbSuppliesUsingDetailsDTO.getTbEquipmentOfCustomerId());
                duplicatedSuppliesUsingDetailsDTO.setTbSuppliesInWarehouseId(tbSuppliesUsingDetailsDTO.getTbSuppliesInWarehouseId());
                duplicatedSuppliesUsingDetailsDTO.setTbGuidelineActivityId(duplicatedGuidelineActivityDTO.getId());
                tbSuppliesUsingDetailsService.save(duplicatedSuppliesUsingDetailsDTO);
            }
        }
    }
}
