package com.yuchen.saas.park.service.service.door.card.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yuchen.saas.park.api.commons.PageWrapper;
import com.yuchen.saas.park.api.entity.door.card.DoorAccessControlCard;
import com.yuchen.saas.park.api.entity.door.card.DoorAccessControlCardBiometrics;
import com.yuchen.saas.park.service.mapper.domain.*;
import com.yuchen.saas.park.service.mapper.door.adapter.AdapterRepository;
import com.yuchen.saas.park.service.mapper.door.card.DoorAccessControlCardBiometricsRepository;
import com.yuchen.saas.park.service.mapper.door.card.DoorAccessControlCardRepository;
import com.yuchen.saas.park.service.service.adapter.AdapterComponent;
import com.yuchen.saas.park.service.service.domain.*;
import com.yuchen.saas.park.service.service.door.card.DoorAccessControlCardService;
import com.yuchen.saas.park.service.service.door.communication.DoorAccessControlCommandService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.nest.springwrap.core.log.exception.ServiceException;
import org.nest.springwrap.core.tool.utils.Func;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Positive;
import java.time.LocalDateTime;
import java.util.*;

import static java.util.stream.Collectors.*;

@Transactional
@Service
@Slf4j
@RequiredArgsConstructor
public class DoorAccessControlCardServiceImpl implements DoorAccessControlCardService {
    private final DoorAccessControlCardRepository doorAccessControlCardRepository;
    private final DoorAccessControlCardBiometricsRepository doorAccessControlCardBiometricsRepository;

    private final AdapterRepository adapterRepository;

    private final AdapterComponent adapterComponent;

    private final DoorAccessControlCommandService doorAccessControlCommandService;

    @Override
    public void insert(DoorAccessControlCardInsert o) {
        if (doorAccessControlCardRepository.getByNo(o.getNo()).isPresent()) {
            throw new ServiceException("卡编号" + o.getNo() + "已存在");
        }

        val card = new DoorAccessControlCard().setUserId(o.getUserId()).setNo(o.getNo()).setPassword(o.getPassword());
        doorAccessControlCardRepository.insert(card);
    }

    @Override
    public void batchInsert(List<DoorAccessControlCardInsert> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }

        val nos = list.stream().map(DoorAccessControlCardInsert::getNo).collect(toSet());

        this.checkNo(nos);

        list.forEach(this::insert);
    }

    @Override
    public void batchInsertByUser(DoorAccessControlCardUserBatchInsert o) {
        val list = o.getList();

        if (CollectionUtils.isEmpty(list)) {
            return;
        }

        val nos = list.stream().map(DoorAccessControlCardMetadata::getNo).collect(toSet());
        this.checkNo(nos);

        val userId = o.getUserId();

        list.stream()
          .map(d -> new DoorAccessControlCardInsert().setUserId(userId).setNo(d.getNo()).setPassword(d.getPassword()))
          .distinct()
          .forEach(this::insert);
    }

    @Override
    public void update(DoorAccessControlCardUpdate o) {
        val exists = doorAccessControlCardRepository.selectById(o.getId());

        if (exists == null) {
            throw new ServiceException("卡不存在");
        }

        exists.setPassword(o.getPassword());

        doorAccessControlCardRepository.updateById(exists);
    }

    @Override
    public void delete(@Positive long id) {
        doorAccessControlCardBiometricsRepository.deleteByDoorAccessControlCardId(id);
        doorAccessControlCardRepository.deleteById(id);
    }

    @Override
    public void batchDelete(List<@Positive Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }
        //List<DoorAccessControlCard> cards = doorAccessControlCardRepository.selectBatchIds(ids);

        doorAccessControlCardBiometricsRepository.deleteByDoorAccessControlCardIds(Set.copyOf(ids));
        doorAccessControlCardRepository.deleteBatchIds(Set.copyOf(ids));
        //iot删除卡
        /* if(Func.isNotEmpty(cards)){
            for (DoorAccessControlCard card : cards) {
                doorAccessControlCommandService.deleteCard(,card.getNo());
            }
        } */
    }

    @Override
    public void rebind(DoorAccessControlCardRebind o) {
        val exists = doorAccessControlCardRepository.selectById(o.getId());

        if (exists == null) {
            throw new ServiceException("卡不存在");
        }

        boolean noExists = doorAccessControlCardRepository.getByNo(o.getNo())
          .map(DoorAccessControlCard::getId)
          .map(id -> id != o.getId())
          .orElse(false);

        if (noExists) {
            throw new ServiceException("卡编号已存在");
        }

        exists.setNo(o.getNo()).setPassword(o.getPassword());
        doorAccessControlCardRepository.updateById(exists);
    }

    @Override
    public void lossReport(@Positive long id) {
        val exists = doorAccessControlCardRepository.selectById(id);

        if (exists == null) {
            throw new ServiceException("卡不存在");
        }

        exists.setLossReport(true).setLossReportTime(LocalDateTime.now());
        doorAccessControlCardRepository.updateById(exists);
    }

    @Override
    public void cancelLossReport(@Positive long id) {
        val exists = doorAccessControlCardRepository.selectById(id);

        if (exists == null) {
            throw new ServiceException("卡不存在");
        }

        exists.setLossReport(false).setCancelLossReportTime(LocalDateTime.now());
        doorAccessControlCardRepository.updateById(exists);
    }

    @Override
    public void bindFacial(DoorAccessControlCardFacialBind o) {
        val doorAccessControlCard = doorAccessControlCardRepository.selectById(o.getId());
        if (doorAccessControlCard == null) {
            throw new ServiceException("卡不存在");
        }

        val userId = doorAccessControlCard.getUserId();
        adapterRepository.getFacialByUserId(userId).orElseThrow(() -> new ServiceException("用户未设置人脸"));

        val exists = doorAccessControlCardBiometricsRepository.getByDoorAccessControlCardId(o.getId());

        if (exists == null) {
            doorAccessControlCardBiometricsRepository.insert(new DoorAccessControlCardBiometrics().setDoorAccessControlCardId(o.getId()).setUserFacialId(userId));
            return;
        }

        exists.setUserFacialId(userId);
        doorAccessControlCardBiometricsRepository.updateById(exists);
    }

    @Override
    public IPage<DoorAccessControlCardRO> page(PageWrapper<DoorAccessControlCardUserCondition> o) {
        return doorAccessControlCardRepository.page(o.getPage(), o.getT());
    }

    @Override
    public IPage<DoorAccessControlCardUserAggregationRO> pageForUser(PageWrapper<DoorAccessControlCardConditionSO> o) {
        return doorAccessControlCardRepository.pageForUser(o.getPage(), this.from(o.getT()));
    }

    @Override
    public DoorAccessControlCardStateAggregation stateAggregation(DoorAccessControlCardStateAggregationCondition o) {
        val map = Optional.ofNullable(doorAccessControlCardRepository.groupByLossReport(o))
          .map(list -> list.stream().collect(toMap(DoorAccessControlCardStateAggregationRO::isLossReport, DoorAccessControlCardStateAggregationRO::getCount)))
          .orElse(Map.of());

        val lossReport = map.getOrDefault(Boolean.TRUE, 0L);
        return DoorAccessControlCardStateAggregation.of(map.getOrDefault(Boolean.FALSE, 0L) + lossReport, lossReport);
    }

    private void checkNo(@NotEmpty Set<@NotBlank String> nos) {
        val list = doorAccessControlCardRepository.listByNos(nos);

        if (!CollectionUtils.isEmpty(list)) {
            var existsNos = list.stream().map(DoorAccessControlCard::getNo).collect(toSet());
            throw new ServiceException("卡编号" + String.join(",", existsNos) + "已存在");
        }
    }

    private DoorAccessControlCardConditionRO from(DoorAccessControlCardConditionSO o) {
        val ro = new DoorAccessControlCardConditionRO();

        val departmentIds = new HashSet<Long>();
        ro.setDepartmentIds(departmentIds);

        ro.setUsername(o.getUsername());
        ro.setUserGender(o.getUserGender());
        ro.setFacial(o.getFacial());
        ro.setFingerprint(o.getFingerprint());
        ro.setMinCardCount(o.getMinCardCount());
        ro.setMaxCardCount(o.getMaxCardCount());

        val departmentId = o.getDepartmentId();

        if (departmentId == null || departmentId < 0) {
            return ro;
        }

        departmentIds.add(departmentId);

        if (!o.isRecursive()) {
            return ro;
        }

        val childrenDepartmentIds = adapterComponent.listDepartmentIdsByDepartmentIdsRecursive(Set.of(departmentId))
          .values()
          .stream()
          .flatMap(Collection::stream)
          .collect(toSet());

        departmentIds.addAll(childrenDepartmentIds);

        return ro;
    }
}
