package vn.azteam.tpf.service;

import java.time.LocalDate;
import java.time.ZoneId;
import java.util.List;

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

import io.github.jhipster.service.filter.LongFilter;
import io.github.jhipster.service.filter.StringFilter;
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 org.springframework.web.multipart.MultipartFile;
import vn.azteam.tpf.Constants.Constants;
import vn.azteam.tpf.domain.TBObjectUpdate;
import vn.azteam.tpf.domain.*; // for static metamodels
import vn.azteam.tpf.repository.TBObjectUpdateRepository;
import vn.azteam.tpf.repository.search.TBObjectUpdateSearchRepository;
//import vn.azteam.tpf.service.dto.ActivityObjectUpdateDTO;
//import vn.azteam.tpf.service.dto.TBObjectUpdateCriteria;
//import vn.azteam.tpf.service.dto.TBObjectUpdateDTO;
import vn.azteam.tpf.service.mapper.TBObjectParameterMapper;
import vn.azteam.tpf.service.mapper.TBObjectUpdateMapper;
//import vn.azteam.tpf.service.dto.TBActivityDTO;
import vn.azteam.tpf.service.dto.*;

import java.time.Instant;
import java.util.Optional;

import static java.time.temporal.ChronoUnit.DAYS;

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

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

    private final TBObjectUpdateRepository tBObjectUpdateRepository;

    private final TBObjectUpdateMapper tBObjectUpdateMapper;

    private final TBObjectUpdateSearchRepository tBObjectUpdateSearchRepository;

    private final UserService userService;

    private final TBActivityService tBActivityService;
    private final FileStorageService fileStorageService;
    private final TBCropService tBCropService;
    private final TBActivityQueryService tBActivityQueryService;
    private final TBActivityTypeQueryService tBActivityTypeQueryService;
    private final TBObjectParameterQueryService tBObjectParameterQueryService;
    private final TBObjectParameterService tBObjectParameterService;
    private final TBObjectParameterMapper tbObjectParameterMapper;

    private final TBObjectUpdateService tBObjectUpdateService;

    private final EntityManager entityManager;

    public TBObjectUpdateQueryService(
        TBObjectUpdateRepository tBObjectUpdateRepository,
        TBObjectUpdateMapper tBObjectUpdateMapper,
        TBObjectUpdateSearchRepository tBObjectUpdateSearchRepository,
        UserService userService, TBActivityService tBActivityService,
        FileStorageService fileStorageService,
        TBCropService tBCropService,
        TBActivityQueryService tBActivityQueryService,
//        TBObjectUpdateQueryService tBObjectUpdateQueryService,
        TBActivityTypeQueryService tBActivityTypeQueryService, TBObjectParameterQueryService tBObjectParameterQueryService,
        TBObjectParameterService tBObjectParameterService, TBObjectParameterMapper tbObjectParameterMapper, TBObjectUpdateService tBObjectUpdateService,
        EntityManager entityManager
    ) {
        this.tBObjectUpdateRepository = tBObjectUpdateRepository;
        this.tBObjectUpdateMapper = tBObjectUpdateMapper;
        this.tBObjectUpdateSearchRepository = tBObjectUpdateSearchRepository;
        this.userService = userService;
        this.tBActivityService = tBActivityService;
        this.fileStorageService = fileStorageService;
        this.tBCropService = tBCropService;
        this.tBActivityQueryService = tBActivityQueryService;
        this.tBActivityTypeQueryService = tBActivityTypeQueryService;
//        this.tBObjectUpdateQueryService = tBObjectUpdateQueryService;
        this.tBObjectParameterQueryService = tBObjectParameterQueryService;
        this.tBObjectParameterService = tBObjectParameterService;
        this.tbObjectParameterMapper = tbObjectParameterMapper;
        this.tBObjectUpdateService = tBObjectUpdateService;
        this.entityManager = entityManager;
    }

    /**
     * Return a {@link List} of {@link TBObjectUpdateDTO} 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<TBObjectUpdateDTO> findByCriteria(TBObjectUpdateCriteria criteria) {
        log.debug("find by criteria : {}", criteria);
        final Specification<TBObjectUpdate> specification = createSpecification(criteria);
        return tBObjectUpdateMapper.toDto(tBObjectUpdateRepository.findAll(specification));
    }

    /**
     * Return a {@link Page} of {@link TBObjectUpdateDTO} 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<TBObjectUpdateDTO> findByCriteria(TBObjectUpdateCriteria criteria, Pageable page) {
        log.debug("find by criteria : {}, page: {}", criteria, page);
        final Specification<TBObjectUpdate> specification = createSpecification(criteria);
        return tBObjectUpdateRepository.findAll(specification, page)
            .map(tBObjectUpdateMapper::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(TBObjectUpdateCriteria criteria) {
        log.debug("count by criteria : {}", criteria);
        final Specification<TBObjectUpdate> specification = createSpecification(criteria);
        return tBObjectUpdateRepository.count(specification);
    }

    /**
     * Function to convert TBObjectUpdateCriteria to a {@link Specification}
     */
    private Specification<TBObjectUpdate> createSpecification(TBObjectUpdateCriteria criteria) {
        Specification<TBObjectUpdate> specification = Specification.where(null);
        if (criteria != null) {
            if (criteria.getId() != null) {
                specification = specification.and(buildSpecification(criteria.getId(), TBObjectUpdate_.id));
            }
            if (criteria.getIndex() != null) {
                specification = specification.and(buildStringSpecification(criteria.getIndex(), TBObjectUpdate_.index));
            }
            if (criteria.getTimes() != null) {
                specification = specification.and(buildRangeSpecification(criteria.getTimes(), TBObjectUpdate_.times));
            }
            if (criteria.getTbObjectParameterId() != null) {
                specification = specification.and(buildSpecification(criteria.getTbObjectParameterId(),
                    root -> root.join(TBObjectUpdate_.tbObjectParameter, JoinType.LEFT).get(TBObjectParameter_.id)));
            }
            if (criteria.getTbActivityId() != null) {
                specification = specification.and(buildSpecification(criteria.getTbActivityId(),
                    root -> root.join(TBObjectUpdate_.tbActivity, JoinType.LEFT).get(TBActivity_.id)));
            }
        }
        return specification;
    }

//    @Transactional
//    public TBActivityDTO createActiveObjectUpdate(ActivityObjectUpdateDTO activityDTO, MultipartFile[] images, String activityName, Pageable pageable) {
//        TBActivityTypeDTO tbActivityTypeDTO = new TBActivityTypeDTO();
//        String pathNameUpload = "";
//        if(activityName == Constants.ENUM_ACTIVITY_TYPE.ACTIVE_TYPE_STATUS_CROP.name()){
//            tbActivityTypeDTO = tBActivityTypeQueryService.findActivityTypeByName(activityName, pageable);
//            pathNameUpload = "StatusCrop";
//        } else if(activityName == Constants.ENUM_ACTIVITY_TYPE.ACTIVE_TYPE_PESTS_INVESTIGATION.name()){
//            tbActivityTypeDTO = tBActivityTypeQueryService.findActivityTypeByName(activityName, pageable);
//            pathNameUpload = "PestsInvestigation";
//        } else {}
//
//        Optional<UserDTO> currentUser = userService.getCurrentUserDTO();
//        TBActivityDTO tBActivityDTO = new TBActivityDTO();
//        tBActivityDTO.setCropId(activityDTO.getCropId());
//        tBActivityDTO.setDescription(activityDTO.getDescription());
//        log.info("==========execute date =========" + tBActivityDTO.getExecuteDate());
//        tBActivityDTO.setAgeDay(this.getAgeDayByCropID(activityDTO.getCropId(), tBActivityDTO.getExecuteDate()));
//        tBActivityDTO.setActivityTypeId(tbActivityTypeDTO.getId());
//        tBActivityDTO.setExecuteDate(activityDTO.getExecuteDate() == null ? Instant.now() : activityDTO.getExecuteDate());
//        tBActivityDTO.setCreatedById(currentUser.get().getId());
//        tBActivityDTO.setCreatedDate(Instant.now());
//        tBActivityDTO = tBActivityQueryService.saveUploadedFiles(tBActivityDTO, images, pathNameUpload);
//        TBActivityDTO result = tBActivityService.save(tBActivityDTO);
//
//
//        for(ActivityObjectUpdateDetailDTO item: activityDTO.getObjectUpdateDetail()){
//            if(item.getIndex()!=null && item.getName()!=null) {
//                TBObjectParameterDTO tbObjectParameterDTO = findObjectParameterByName(item.getName(), pageable);
//                if(tbObjectParameterDTO!=null) {
//                    TBObjectUpdateDTO tBObjectUpdateDTO = new TBObjectUpdateDTO();
//                    tBObjectUpdateDTO.setTbActivityId(result.getId());
//                    tBObjectUpdateDTO.setIndex(item.getIndex());
//                    tBObjectUpdateDTO.setTbObjectParameterId(tbObjectParameterDTO.getId());
//                    tBObjectUpdateDTO.setTimes(getTimesByDateAndParamId(Instant.now(), tbObjectParameterDTO.getId()));
//                    tBObjectUpdateService.save(tBObjectUpdateDTO);
//                }
//            }
//        }
//        return result;
//    }
//
//
//    @Transactional
//    public TBActivityDTO updateActiveObjectUpdate(ActivityObjectUpdateDTO activityDTO,
//                                                MultipartFile[] images,
//                                                  String activityName,
//                                                Pageable pageable) {
//
//        String pathNameUpload = "";
//        if(activityName == Constants.ENUM_ACTIVITY_TYPE.ACTIVE_TYPE_STATUS_CROP.name()){
//            pathNameUpload = "StatusCrop";
//        } else if(activityName == Constants.ENUM_ACTIVITY_TYPE.ACTIVE_TYPE_PESTS_INVESTIGATION.name()){
//            pathNameUpload = "PestsInvestigation";
//        } else {}
//
//        Optional<UserDTO> currentUser = userService.getCurrentUserDTO();
//        Optional<TBActivityDTO> tBActivityDTOOptional = tBActivityService.findOne(activityDTO.getActivityId());
//        TBActivityDTO tBActivityDTO = tBActivityDTOOptional.get();
//        TBActivityDTO result = new TBActivityDTO();
//        if (tBActivityDTO != null) {
//            tBActivityDTO.setModifiedById(currentUser.get().getId());
//
//            tBActivityDTO.setModifiedDate(Instant.now());
//
//            if (activityDTO.getDescription() != null) {
//                tBActivityDTO.setDescription(activityDTO.getDescription());
//            }
//            if (activityDTO.getExecuteDate() != null) {
//                tBActivityDTO.setExecuteDate(activityDTO.getExecuteDate());
//                if(tBActivityDTO.getExecuteDate() != activityDTO.getExecuteDate()){
//                    tBActivityDTO.setAgeDay(this.getAgeDayByCropID(tBActivityDTO.getCropId(), activityDTO.getExecuteDate()));
//                }
//            }
//            tBActivityDTO = tBActivityQueryService.updateUploadedFiles(tBActivityDTO, images,pathNameUpload, activityDTO.getMedia_del());
//            result = tBActivityService.save(tBActivityDTO);
//
//
//            for(ActivityObjectUpdateDetailDTO item: activityDTO.getObjectUpdateDetail()){
//                if(item.getIndex()!=null && item.getName()!=null) {
//                    TBObjectParameterDTO tbObjectParameterDTO = findObjectParameterByName(item.getName(), pageable);
////                    Optional<TBObjectParameter> tbObjectParameter = tBObjectParameterService.findObjectParameterByName(item.getName());
////                    TBObjectParameterDTO tbObjectParameterDTO = tbObjectParameterMapper.toDto(tbObjectParameter.get());
//                    if(tbObjectParameterDTO!=null) {
//                        TBObjectUpdateDTO  tbObjectUpdateDTO =
//                            findObjectUpdateByActivityIdAndObjectParameterId(result.getId(), tbObjectParameterDTO.getId(), pageable);
//                        if(tbObjectUpdateDTO!=null){
//                            tbObjectUpdateDTO.setIndex(item.getIndex());
//                            tBObjectUpdateService.save(tbObjectUpdateDTO);
//                        }else {
//                            TBObjectUpdateDTO tBObjectUpdateDTO = new TBObjectUpdateDTO();
//                            tBObjectUpdateDTO.setTbActivityId(result.getId());
//                            tBObjectUpdateDTO.setIndex(item.getIndex());
//                            tBObjectUpdateDTO.setTbObjectParameterId(tbObjectParameterDTO.getId());
//                            tBObjectUpdateDTO.setTimes(getTimesByDateAndParamId(Instant.now(), tbObjectParameterDTO.getId()));
//                            tBObjectUpdateService.save(tBObjectUpdateDTO);
//                        }
//                    }
//                }
//            }
//
//        } else {
//            result = tBActivityDTOOptional.get();
//        }
//        return result;
//
//    }

    private TBObjectParameterDTO findObjectParameterByName(String name, Pageable pageable) {
        TBObjectParameterCriteria criteria = new TBObjectParameterCriteria();
        StringFilter nameFilter = new StringFilter();
        nameFilter.setEquals(name);
        criteria.setName(nameFilter);
        Page<TBObjectParameterDTO> page = tBObjectParameterQueryService.findByCriteria(criteria, pageable);
        return page.getContent().size() == 0 ? null : page.getContent().get(0);
    }

    private int getTimesByDateAndParamId(Instant date, Long paramId) {
        String query = "select ou.id from tb_activity as a \n" +
            "join tb_object_update as ou \n" +
            "on a.id = ou.tb_activity_id\n" +
            "where ou.tb_object_parameter_id = :paramId and DATE(execute_date) = DATE_FORMAT(:date, '%Y-%m-%d')";

        Query envStandardQuery = entityManager.createNativeQuery(query);
        envStandardQuery.setParameter("paramId", paramId);
        envStandardQuery.setParameter("date", date);
        int time_current = envStandardQuery.getResultList().size();
        return time_current + 1;
    }

    public int getAgeDayByCropID(Long id, Instant executeDate) {
        Optional<TBCropDTO> tBCropDTOOptional = tBCropService.findOne(id);
        TBCropDTO tBCropDTO = tBCropDTOOptional.get();
        LocalDate startDate = tBCropDTO.getStartDate().atZone(ZoneId.systemDefault()).toLocalDate();
        if(executeDate == null) {
            executeDate = Instant.now();
        }
        LocalDate endDate = executeDate.atZone(ZoneId.systemDefault()).toLocalDate();
        int daysBetween = (int) DAYS.between(startDate, endDate);

        System.out.println("Days: " + daysBetween);

        int ageDay = 0;
        if (tBCropDTO.getAgeDayStartAt() != null) {
            ageDay = daysBetween + tBCropDTO.getAgeDayStartAt();
        } else {
            ageDay = daysBetween;
        }
        return ageDay;
    }

    private TBObjectUpdateDTO findObjectUpdateByActivityIdAndObjectParameterId(Long activityId, Long objectParameterId, Pageable pageable) {
        TBObjectUpdateCriteria criteria = new TBObjectUpdateCriteria();
        LongFilter activityIdFilter = new LongFilter();
        LongFilter objectParameterIdFilter = new LongFilter();
        activityIdFilter.setEquals(activityId);
        objectParameterIdFilter.setEquals(objectParameterId);
        criteria.setTbActivityId(activityIdFilter);
        criteria.setTbObjectParameterId(objectParameterIdFilter);
        Page<TBObjectUpdateDTO> page = this.findByCriteria(criteria, pageable);
        return page.getContent().size() == 0 ? null : page.getContent().get(0);
    }
}
