package vn.azteam.tpf.service;
import static vn.azteam.tpf.web.rest.util.ConvertCharacterUtil.removeAccent;
import com.google.common.collect.Lists;
import io.github.jhipster.service.filter.LongFilter;
import io.github.jhipster.service.filter.StringFilter;
import org.springframework.data.domain.PageImpl;
import vn.azteam.tpf.Constants.Constants;
import vn.azteam.tpf.repository.TBEntityRepository;
import vn.azteam.tpf.repository.TBEquipmentHistoryRepository;
import vn.azteam.tpf.service.dto.*;

import java.time.Instant;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;
import javax.persistence.Query;
import javax.persistence.criteria.JoinType;

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.TBEquipmentOfCustomer;
import vn.azteam.tpf.domain.*; // for static metamodels
import vn.azteam.tpf.repository.TBEquipmentOfCustomerRepository;
import vn.azteam.tpf.repository.search.TBEquipmentOfCustomerSearchRepository;
import vn.azteam.tpf.service.dto.TBEquipmentOfCustomerCriteria;
import vn.azteam.tpf.service.dto.TBEquipmentOfCustomerDTO;
import vn.azteam.tpf.service.mapper.TBEquipmentHistoryMapper;
import vn.azteam.tpf.service.mapper.TBEquipmentOfCustomerMapper;
import vn.azteam.tpf.service.util.UserRoleUtil;
import vn.azteam.tpf.web.rest.errors.BadRequestAlertException;

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

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

    private final TBEquipmentOfCustomerRepository tBEquipmentOfCustomerRepository;

    private final TBEquipmentOfCustomerMapper tBEquipmentOfCustomerMapper;

    private final TBEquipmentOfCustomerSearchRepository tBEquipmentOfCustomerSearchRepository;

    private final UserService userService;
    @PersistenceUnit
    private EntityManagerFactory entityManagerFactory;

    private final EntityManager entityManager;

    private final TBEntityService tBEntityService;

    private final TBCommandQueryService tBCommandQueryService;

    private final TBEquipmentHistoryService tBEquipmentHistoryService;

    private final TBEquipmentOfCustomerService tBEquipmentOfCustomerService;

    private final TBEntityRepository tBEntityRepository;

    private final TBEquipmentHistoryRepository tbEquipmentHistoryRepository;

    private final TBEquipmentHistoryMapper tbEquipmentHistoryMapper;

    public TBEquipmentOfCustomerQueryService(TBEquipmentOfCustomerRepository tBEquipmentOfCustomerRepository,
                                             TBEquipmentOfCustomerMapper tBEquipmentOfCustomerMapper,
                                             TBEquipmentOfCustomerSearchRepository tBEquipmentOfCustomerSearchRepository,
                                             UserService userService, EntityManagerFactory entityManagerFactory,
                                             EntityManager entityManager,
                                             TBEntityService tBEntityService,
                                             TBCommandQueryService tBCommandQueryService,
                                             TBEquipmentHistoryService tBEquipmentHistoryService,
                                             TBEquipmentOfCustomerService tBEquipmentOfCustomerService,
                                             TBEntityRepository tBEntityRepository,
                                             TBEquipmentHistoryRepository tbEquipmentHistoryRepository,
                                             TBEquipmentHistoryMapper tbEquipmentHistoryMapper) {
        this.tBEquipmentOfCustomerRepository = tBEquipmentOfCustomerRepository;
        this.tBEquipmentOfCustomerMapper = tBEquipmentOfCustomerMapper;
        this.tBEquipmentOfCustomerSearchRepository = tBEquipmentOfCustomerSearchRepository;
        this.userService = userService;
        this.entityManagerFactory = entityManagerFactory;
        this.entityManager = entityManager;
        this.tBEntityService = tBEntityService;
        this.tBCommandQueryService = tBCommandQueryService;
        this.tBEquipmentHistoryService = tBEquipmentHistoryService;
        this.tBEquipmentOfCustomerService = tBEquipmentOfCustomerService;
        this.tBEntityRepository = tBEntityRepository;
        this.tbEquipmentHistoryRepository = tbEquipmentHistoryRepository;
        this.tbEquipmentHistoryMapper = tbEquipmentHistoryMapper;
    }

    /**
     * Return a {@link List} of {@link TBEquipmentOfCustomerDTO} 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<TBEquipmentOfCustomerDTO> findByCriteria(TBEquipmentOfCustomerCriteria criteria) {
        log.debug("find by criteria : {}", criteria);
        final Specification<TBEquipmentOfCustomer> specification = createSpecification(criteria);
        return tBEquipmentOfCustomerMapper.toDto(tBEquipmentOfCustomerRepository.findAll(specification));
    }

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

    /**
     * Function to convert TBEquipmentOfCustomerCriteria to a {@link Specification}
     */
    private Specification<TBEquipmentOfCustomer> createSpecification(TBEquipmentOfCustomerCriteria criteria) {
        Specification<TBEquipmentOfCustomer> specification = Specification.where(null);
        if (criteria != null) {
            if (criteria.getId() != null) {
                specification = specification.and(buildSpecification(criteria.getId(), TBEquipmentOfCustomer_.id));
            }
            if (criteria.getQrCode() != null) {
                specification = specification.and(buildStringSpecification(criteria.getQrCode(), TBEquipmentOfCustomer_.qrCode));
            }
            if (criteria.getName() != null) {
                specification = specification.and(buildStringSpecification(criteria.getName(), TBEquipmentOfCustomer_.name));
            }
            if (criteria.getStatus() != null) {
                specification = specification.and(buildStringSpecification(criteria.getStatus(), TBEquipmentOfCustomer_.status));
            }
            if (criteria.getMode() != null) {
                specification = specification.and(buildRangeSpecification(criteria.getMode(), TBEquipmentOfCustomer_.mode));
            }
            if (criteria.getSerial() != null) {
                specification = specification.and(buildStringSpecification(criteria.getSerial(), TBEquipmentOfCustomer_.serial));
            }
            if (criteria.getCreatedDate() != null) {
                specification = specification.and(buildRangeSpecification(criteria.getCreatedDate(), TBEquipmentOfCustomer_.createdDate));
            }
            if (criteria.getModifiedDate() != null) {
                specification = specification.and(buildRangeSpecification(criteria.getModifiedDate(), TBEquipmentOfCustomer_.modifiedDate));
            }
            if (criteria.getDeletedDate() != null) {
                specification = specification.and(buildRangeSpecification(criteria.getDeletedDate(), TBEquipmentOfCustomer_.deletedDate));
            }
            if (criteria.getTbEquipmentId() != null) {
                specification = specification.and(buildSpecification(criteria.getTbEquipmentId(),
                    root -> root.join(TBEquipmentOfCustomer_.tbEquipment, JoinType.LEFT).get(TBEquipment_.id)));
            }
            if (criteria.getTbEntityId() != null) {
                specification = specification.and(buildSpecification(criteria.getTbEntityId(),
                    root -> root.join(TBEquipmentOfCustomer_.tbEntity, JoinType.LEFT).get(TBEntity_.id)));
            }
            if (criteria.getTbCustomerId() != null) {
                specification = specification.and(buildSpecification(criteria.getTbCustomerId(),
                    root -> root.join(TBEquipmentOfCustomer_.tbCustomer, JoinType.LEFT).get(TBCustomer_.id)));
            }
            if (criteria.getCreatedById() != null) {
                specification = specification.and(buildSpecification(criteria.getCreatedById(),
                    root -> root.join(TBEquipmentOfCustomer_.createdBy, JoinType.LEFT).get(TBDetailUser_.id)));
            }
            if (criteria.getModifiedById() != null) {
                specification = specification.and(buildSpecification(criteria.getModifiedById(),
                    root -> root.join(TBEquipmentOfCustomer_.modifiedBy, JoinType.LEFT).get(TBDetailUser_.id)));
            }
            if (criteria.getDeletedById() != null) {
                specification = specification.and(buildSpecification(criteria.getDeletedById(),
                    root -> root.join(TBEquipmentOfCustomer_.deletedBy, JoinType.LEFT).get(TBDetailUser_.id)));
            }
        }
        return specification;
    }

    public Page<TBEquipmentOfCustomerDTO> getDevicesOfUserCustomers(TBEquipmentOfCustomerCriteria criteria, Pageable page, String query, Long equipmentTypeId, Long equipmentId) {

        List<TBEquipmentOfCustomer> data = tBEquipmentOfCustomerRepository.findAll().stream()
                .filter(item -> item.getDeletedBy() == null && item.getDeletedDate() == null)
                .collect(Collectors.toList());

        if (equipmentTypeId != null) {
            data = data.stream()
                .filter(item -> item.getTbEquipment() != null
                    && item.getTbEquipment().getTbEquipmentType() != null
                    && item.getTbEquipment().getTbEquipmentType().getId() == equipmentTypeId)
                .collect(Collectors.toList());
        }

        if (equipmentId != null) {
            data = data.stream()
                .filter(item -> item.getTbEquipment() != null && item.getTbEquipment().getId() == equipmentId)
                .collect(Collectors.toList());
        }

        if (query != null && query.equals("") == Boolean.FALSE) {
            data = data.stream()
                .filter(item ->
                    queryContain(item.getName(), query) || queryContain(item.getSerial(), query)
                        || (item.getTbEquipment() != null && (
                            queryContain(item.getTbEquipment().getManufacturer(), query)
                            || queryContain(item.getTbEquipment().getSku(), query)
                            || queryContain(item.getTbEquipment().getName(), query)))
                        || (item.getTbEquipment() != null && item.getTbEquipment().getTbEquipmentType() != null &&
                        queryContain(item.getTbEquipment().getTbEquipmentType().getName(), query))
                        || (item.getTbEntity() != null && queryContain(item.getTbEntity().getName(), query))
                )
                .collect(Collectors.toList());
        }

        List<Long> tbEquipmentOfCustomerId = data.stream().map(TBEquipmentOfCustomer::getId).collect(Collectors.toList());
        if (tbEquipmentOfCustomerId.size() > 0) {
            LongFilter equipmentOfCustomerId = new LongFilter();
            equipmentOfCustomerId.setIn(tbEquipmentOfCustomerId);
            criteria.setId(equipmentOfCustomerId);

            final Specification<TBEquipmentOfCustomer> specification = createSpecification(criteria);
            Page<TBEquipmentOfCustomerDTO> pages = tBEquipmentOfCustomerRepository.findAll(specification, page)
                .map(tBEquipmentOfCustomerMapper::toDto);

            pages.forEach(item -> {
                if (item.getTbEntityId() != null) {
                    TBEntityDTO tBEntityDTO = tBEntityService.findOne(item.getTbEntityId()).get();
                    item.setLocation(tBEntityDTO.getName());
                } else {
                    item.setLocation(null);
                }
            });
            return pages;
        } else {
            return new PageImpl<>(Lists.newArrayList(), page, 0);
        }
    }

    public List<TBEquipmentOfCustomerDTO> getListDeviceForActivity(String q) {
        UserDTO currentUser = userService.getCurrentUserDTO().get();
        List<TBEquipmentOfCustomer> data = tBEquipmentOfCustomerRepository.findAll().stream()
            .filter(item ->
                item.getTbCustomer() == null
                    || (item.getTbCustomer() != null && item.getTbCustomer().getId() == currentUser.getCustomerId())
            )
            .collect(Collectors.toList());
        return tBEquipmentOfCustomerMapper.toDto(data);
    }

    public List<TBEquipmentOfCustomerDTO> getAllDeviceInEntity(Long id) {
        UserDTO currentUser = userService.getCurrentUserDTO().get();
        List<TBEquipmentOfCustomer> tbEquipmentOfCustomer;

        tbEquipmentOfCustomer = tBEquipmentOfCustomerRepository.findAll().stream()
            .filter(item -> (item.getTbCustomer() == null ||
                (item.getTbEntity() != null
                 && item.getTbEntity().getId() == id
                )) && item.getDeletedBy() == null && item.getDeletedDate() == null
            )
            .collect(Collectors.toList());

        List<TBEquipmentOfCustomerDTO> result = tBEquipmentOfCustomerMapper.toDto(tbEquipmentOfCustomer);
        result.forEach(item -> {
            if (item.getTbEntityId()!=null){
                if(tBEntityService.findOne(item.getTbEntityId()).isPresent()) {
                    TBEntityDTO tBEntityDTO = tBEntityService.findOne(item.getTbEntityId()).get();
                    item.setLocation(tBEntityDTO.getName());
                }
            } else {
                item.setLocation(null);
            }
        });

        return result;
    }
    public List<TBEquipmentOfCustomerDTO> getAllDeviceOfUserCustomersControl(String query) {
        List<TBEquipmentOfCustomer> tbEquipmentOfCustomer;

        tbEquipmentOfCustomer = tBEquipmentOfCustomerRepository.findAll().stream()
            .filter(item -> item.getTbEntity() != null
                && item.getDeletedBy() == null
                && item.getDeletedDate() == null
                && item.getTbEquipment() != null
                && item.getTbEquipment().getTbEquipmentType() != null
                && !item.getTbEquipment().getTbEquipmentType().getName().equals("other")
            )
            .collect(Collectors.toList());

        if (query != null && !query.isEmpty()) {
            tbEquipmentOfCustomer = tbEquipmentOfCustomer.stream().filter(item ->
                (item.getTbEntity() != null && queryContain(item.getTbEntity().getName(), query))
                || queryContain(item.getName(), query)
            ).collect(Collectors.toList());
        }

        List<TBEquipmentOfCustomerDTO> result = tBEquipmentOfCustomerMapper.toDto(tbEquipmentOfCustomer);
        result.forEach(item -> {
            if (item.getTbEntityId()!=null){
                if(tBEntityService.findOne(item.getTbEntityId()).isPresent()) {
                    TBEntityDTO tBEntityDTO = tBEntityService.findOne(item.getTbEntityId()).get();
                    item.setLocation(tBEntityDTO.getName());
                }
            }
            else {
                item.setLocation(null);
            }
        });

        return result;
    }
    public boolean check(Long id, List<Long> listIds) {
        boolean flag = Boolean.FALSE;
        int i;
        for (i = 0; i < listIds.size(); i++) {
            if (listIds.get(i).longValue() == id) {
                flag = Boolean.TRUE;
                break;
            }
        }
        return flag;
    }

    @Transactional(readOnly = false)
    public synchronized ControlTBEquipmentOfCustomerDTO equipmentOfCustomerOnOff(Long id, String isOn) throws Exception {
        ControlTBEquipmentOfCustomerDTO controlTBEquipmentOfCustomerDTO = new ControlTBEquipmentOfCustomerDTO();
        TBEquipmentOfCustomerDTO equipmentOfCustomerDTO = getLatestEquipmentOfCustomer(id);

        //Check mode of device
        if (equipmentOfCustomerDTO.getMode() != null && equipmentOfCustomerDTO.getMode() == 0) {
            controlTBEquipmentOfCustomerDTO.setTbEquipmentOfCustomerDTO(equipmentOfCustomerDTO);
            controlTBEquipmentOfCustomerDTO.setStatusCode(400);
            controlTBEquipmentOfCustomerDTO.setMessage(Constants.FAILED_AUTO_MODE_MESSAGE_CONTROL_EQUIPMENT);
            return controlTBEquipmentOfCustomerDTO;
        }
        //Check connect to device
        boolean isConnected = false;
        //Get latest equipmentOfCustomerDTO
        TBEquipmentOfCustomerDTO latestEquipmentOfCustomerDTO = getLatestEquipmentOfCustomer(equipmentOfCustomerDTO.getId());

        if(latestEquipmentOfCustomerDTO.getModifiedDate() != null) {
            Long modifiedSec = latestEquipmentOfCustomerDTO.getModifiedDate().getEpochSecond();
            Long currentSec = Instant.now().getEpochSecond();
            isConnected = currentSec - modifiedSec > Constants.DEFAULT_TIMEOUT_RECONNECT ? false : true;
        }

        if (!isConnected) {
            controlTBEquipmentOfCustomerDTO.setTbEquipmentOfCustomerDTO(equipmentOfCustomerDTO);
            controlTBEquipmentOfCustomerDTO.setStatusCode(400);
            controlTBEquipmentOfCustomerDTO.setMessage(Constants.FAILED_ERROR_CONNECT_MESSAGE_CONTROL_EQUIPMENT);
            return  controlTBEquipmentOfCustomerDTO;
        }

        //Check status device
        if (equipmentOfCustomerDTO.getStatus().equals("2")) {
            controlTBEquipmentOfCustomerDTO.setTbEquipmentOfCustomerDTO(equipmentOfCustomerDTO);
            controlTBEquipmentOfCustomerDTO.setStatusCode(400);
            controlTBEquipmentOfCustomerDTO.setMessage(Constants.FAILED_FAULTY_MESSAGE_CONTROL_EQUIPMENT);
            return controlTBEquipmentOfCustomerDTO;
        }

        if(equipmentOfCustomerDTO.getStatus().equals(isOn)) {
            controlTBEquipmentOfCustomerDTO.setTbEquipmentOfCustomerDTO(equipmentOfCustomerDTO);
            controlTBEquipmentOfCustomerDTO.setStatusCode(400);
            if (isOn.equals("0")) {
                controlTBEquipmentOfCustomerDTO.setMessage(Constants.FAILED_ERROR_OFF_MESSAGE_CONTROL_EQUIPMENT);
            }
            if (isOn.equals("1")) {
                controlTBEquipmentOfCustomerDTO.setMessage(Constants.FAILED_ERROR_ON_MESSAGE_CONTROL_EQUIPMENT);
            }
            return controlTBEquipmentOfCustomerDTO;
        } else {
            TBEquipmentHistoryDTO equipmentHistoryDTO = new TBEquipmentHistoryDTO();
            Instant currentTime = Instant.now();
            equipmentHistoryDTO.setDateOccurred(currentTime);
            AtomicLong eventMilliseconds = new AtomicLong(currentTime.toEpochMilli());
            equipmentHistoryDTO.setEvent(eventMilliseconds.toString());
            Long userId = userService.getCurrentUserDTO().get().getId();
            equipmentHistoryDTO.setTbDetailUserId(userId);
            equipmentHistoryDTO.setTbEquipmentOfCustomerId(equipmentOfCustomerDTO.getId());

            //Get command id by isOn variable
            TBCommandCriteria commandCriteria = new TBCommandCriteria();
            StringFilter keyFilter = new StringFilter();
            keyFilter.setEquals(isOn);
            commandCriteria.setKey(keyFilter);
            List<TBCommandDTO> commandDTOs = tBCommandQueryService.findByCriteria(commandCriteria);
            if (commandDTOs.size() > 0) {
                equipmentHistoryDTO.setTbCommandId(commandDTOs.get(0).getId());
            }
            equipmentHistoryDTO.setStatus(Constants.READY_STATUS_EQUIPMENT_HISTORY);

            //save TBEquipmentHistory immediately
            TBEquipmentHistory tbEquipmentHistory = tbEquipmentHistoryMapper.toEntity(equipmentHistoryDTO);

            EntityManager newEntityManager1 = entityManagerFactory.createEntityManager();
            newEntityManager1.getTransaction().begin();
            newEntityManager1.persist(tbEquipmentHistory);
            newEntityManager1.getTransaction().commit();
            newEntityManager1.close();

            //waiting system process 15 seconds
            try {
               wait(15000);
            } catch (Exception e) {
            }

            //Verify status of equipment of customer
            EntityManager newEntityManager = entityManagerFactory.createEntityManager();
            TBEquipmentOfCustomerDTO finalResult = getNewLatestEquipmentOfCustomer(equipmentOfCustomerDTO.getId(), newEntityManager);
            controlTBEquipmentOfCustomerDTO.setTbEquipmentOfCustomerDTO(finalResult);
            if(finalResult.getStatus().equals(isOn)) {
                controlTBEquipmentOfCustomerDTO.setStatusCode(200);
                controlTBEquipmentOfCustomerDTO.setMessage(Constants.SUCCEED_MESSAGE_CONTROL_EQUIPMENT);
            } else {
                controlTBEquipmentOfCustomerDTO.setStatusCode(400);
                controlTBEquipmentOfCustomerDTO.setMessage(Constants.FAILED_MESSAGE_CONTROL_EQUIPMENT);
            }
        }
        return controlTBEquipmentOfCustomerDTO;
    }

    private TBEquipmentOfCustomerDTO getNewLatestEquipmentOfCustomer(Long id, EntityManager newEntityManager) {
        Query equipmentCustomersQuery = newEntityManager.createNativeQuery(
            String.format(
                "select tb_equipment_of_customer.*\n" +
                    " from tb_equipment_of_customer\n" +
                    " where tb_equipment_of_customer.id = :id\n"), TBEquipmentOfCustomer.class);
        equipmentCustomersQuery.setParameter("id", id);

        TBEquipmentOfCustomerDTO equipmentCustomersDTO = tBEquipmentOfCustomerMapper.toDto((TBEquipmentOfCustomer) equipmentCustomersQuery.getResultList().get(0));

        newEntityManager.close();
        return equipmentCustomersDTO;
    }

    private TBEquipmentOfCustomerDTO getLatestEquipmentOfCustomer(Long id) {
        Query equipmentCustomersQuery = entityManager.createNativeQuery(
            String.format(
                "select tb_equipment_of_customer.*\n" +
                    " from tb_equipment_of_customer\n" +
                    " where tb_equipment_of_customer.id = :id\n"), TBEquipmentOfCustomer.class);
        equipmentCustomersQuery.setParameter("id", id);

        TBEquipmentOfCustomerDTO equipmentCustomersDTO = tBEquipmentOfCustomerMapper.toDto((TBEquipmentOfCustomer) equipmentCustomersQuery.getResultList().get(0));
        return equipmentCustomersDTO;
    }

    private Integer getCountLatestEquipmentHistory() {
        Query equipmentHistoriesQuery = entityManager.createNativeQuery(
            String.format(
                "select tb_equipment_history.*\n" +
                    " from tb_equipment_history\n" ), TBEquipmentHistory.class);
        return equipmentHistoriesQuery.getResultList().size();
    }

    private Boolean queryContain(String data, String query) {
        if (data == null) {
            return Boolean.FALSE;
        } else {
            return removeAccent(data.toLowerCase()).contains(removeAccent(query.toLowerCase()));
        }
    }

    public Long totalDeviceInWarehouse(Long equipmentId) {
        Long tolal = tBEquipmentOfCustomerRepository.findAll().stream()
            .filter(item ->item.getTbEquipment() != null
                && item.getTbEquipment().getId() == equipmentId)
            .distinct()
            .count();
        return tolal;
    }
    public List<TBEquipmentOfCustomer>  listDeviceInWarehouse(Long equipmentId) {
        return tBEquipmentOfCustomerRepository.findAll().stream()
            .filter(item ->
                item.getTbEntity() != null
                &&
                item.getTbEquipment() != null
                && item.getTbEquipment().getId() == equipmentId)
            .distinct()
            .collect(Collectors.toList());
    }

     public List<TBEntityDTO>  listWarehouse(Long equipmentId) {
       List<TBEntityDTO> tbEntityDTOs = new ArrayList<>();
       List<TBEquipmentOfCustomer>  aa = tBEquipmentOfCustomerRepository.findAll().stream()
           .filter(item -> item.getTbEntity() != null
               && item.getTbEquipment() != null
               && item.getTbEquipment().getId() == equipmentId)
           .sorted((o1, o2) -> (o2.getTbEntity().getId().compareTo(o1.getTbEntity().getId())))
           .filter(distinctByKey(pr -> Arrays.asList(pr.getTbEntity().getId())))
           .collect(Collectors.toList());
       aa.forEach(item ->{
        TBEntityDTO tbEntityDTO = new TBEntityDTO();
            tbEntityDTO.setName(item.getTbEntity().getName());
            tbEntityDTO.setTotalDevice(listEquipmentInWarehouse(equipmentId, item.getTbEntity().getId()));
            tbEntityDTOs.add(tbEntityDTO);
       });
       return tbEntityDTOs;
   }

    private Long listEquipmentInWarehouse(Long equipmentId, Long warehouseId) {
       return tBEquipmentOfCustomerRepository.findAll().stream()
           .filter(item -> item.getTbEntity() != null
               && item.getTbEntity().getId().equals(warehouseId)
               && item.getTbEquipment() != null
               && item.getTbEquipment().getId().equals(equipmentId))
           .distinct()
           .count();
   }

    private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    public List<LocationDevicesDTO> listLocationDevices() {
        List<LocationDevicesDTO> result = new ArrayList<>();

        List<TBEntity> tbEntity = tBEntityRepository.findAll();

        tbEntity.forEach(item -> {
            LocationDevicesDTO data = new LocationDevicesDTO();
            data.setId(item.getId());
            data.setName(item.getName());
            data.setType(Constants.LOCATION.ENTITY.getValue());
            data.setCustomerId(item.getTbCustomer() != null? item.getTbCustomer().getId(): null);
            result.add(data);
        });
        return result;
    }

    @Transactional
    public TBEquipmentOfCustomerDTO createTBEquipmentOfCustomer(DevicesDTO devicesDTO) {
        UserDTO currentUser = userService.getCurrentUserDTO().get();
        if (checkNameEquipmentOfCustomer(devicesDTO.getName())) {
            throw new BadRequestAlertException("1032", "TBEquipmentOfCustomer", "1032");
        }
        TBEquipmentOfCustomerDTO data = new TBEquipmentOfCustomerDTO();
        data.setName(devicesDTO.getName());
        data.setTbEquipmentId(devicesDTO.getTbEquipmentId());
        data.setStatus(devicesDTO.getStatus());
        data.setCreatedById(currentUser.getId());
        data.setCreatedDate(Instant.now());
        data.setSerial(devicesDTO.getSerial());
        TBEntityDTO tbEntity = tBEntityService.findOne(devicesDTO.getLocation().getId()).get();
        if(currentUser.getCustomerId() != null && currentUser.getCustomerId() != tbEntity.getTbCustomerId()) {
            throw new BadRequestAlertException("1057", "TBEquipmentOfCustomer", "1057");
        }
        data.setTbEntityId(tbEntity.getId());

        data.setTbCustomerId(tbEntity.getTbCustomerId());
        return tBEquipmentOfCustomerService.save(data);
    }

    @Transactional
    public TBEquipmentOfCustomerDTO updateTBEquipmentOfCustomer(DevicesDTO devicesDTO) {
        if (tBEquipmentOfCustomerService.findOne(devicesDTO.getId()).isPresent()) {

            UserDTO currentUser = userService.getCurrentUserDTO().get();
            TBEquipmentOfCustomerDTO data = tBEquipmentOfCustomerService.findOne(devicesDTO.getId()).get();
            if (checkNameEquipmentOfCustomer(devicesDTO.getName())
                && devicesDTO.getId() != data.getId()) {
                throw new BadRequestAlertException("1032", "TBEquipmentOfCustomer", "1032");
            }
            data.setName(devicesDTO.getName());
            data.setTbEquipmentId(devicesDTO.getTbEquipmentId());
            data.setStatus(devicesDTO.getStatus());
            data.setModifiedById(currentUser.getId());
            data.setModifiedDate(Instant.now());
            data.setSerial(devicesDTO.getSerial());
            TBEntityDTO tbEntity = tBEntityService.findOne(devicesDTO.getLocation().getId()).get();
            data.setTbEntityId(tbEntity.getId());
            if(currentUser.getCustomerId() != null && currentUser.getCustomerId() != tbEntity.getTbCustomerId()) {
                throw new BadRequestAlertException("1057", "TBEquipmentOfCustomer", "1057");
            }
            data.setTbCustomerId(tbEntity.getTbCustomerId());
            return tBEquipmentOfCustomerService.save(data);
        } else {
            throw new BadRequestAlertException("1033", "TBEquipmentOfCustomer", "1033");
        }
    }

    public boolean checkNameEquipmentOfCustomer(String name) {
        return tBEquipmentOfCustomerRepository.findAll().stream()
            .anyMatch(item -> item.getName().equals(name));
    }

    @Transactional
    public boolean deleteEquipmentOfCustomer(Long id){
        try {
            UserDTO currentUser = userService.getCurrentUserDTO().get();
            TBEquipmentOfCustomerDTO dataEquipmentsOfCustomer = tBEquipmentOfCustomerService.findOne(id).get();
            dataEquipmentsOfCustomer.setDeletedById(currentUser.getId());
            dataEquipmentsOfCustomer.setDeletedDate(Instant.now());
            tBEquipmentOfCustomerService.save(dataEquipmentsOfCustomer);
            return Boolean.TRUE;
        } catch (Exception e) {
            return Boolean.FALSE;
        }
    }
}
