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

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yuchen.saas.park.api.commons.IdName;
import com.yuchen.saas.park.api.commons.IdTreeCount;
import com.yuchen.saas.park.api.commons.PageWrapper;
import com.yuchen.saas.park.api.commons.domain.DoorAccessControlConfigTarget;
import com.yuchen.saas.park.api.entity.door.config.DoorAccessControlConfigIssueTask;
import com.yuchen.saas.park.service.mapper.domain.config.details.DoorAccessControlConfigDetailsCondition;
import com.yuchen.saas.park.service.mapper.domain.config.details.DoorAccessControlConfigDetailsRO;
import com.yuchen.saas.park.service.mapper.domain.config.details.UserDoorAccessControlCondition;
import com.yuchen.saas.park.service.mapper.domain.config.details.UserDoorAccessControlRO;
import com.yuchen.saas.park.service.mapper.domain.config.mapping.*;
import com.yuchen.saas.park.service.mapper.domain.task.DoorAccessControlConfigIssueTaskCondition;
import com.yuchen.saas.park.service.mapper.domain.task.DoorAccessControlConfigIssueTaskRO;
import com.yuchen.saas.park.service.mapper.door.adapter.AdapterRepository;
import com.yuchen.saas.park.service.mapper.door.card.DoorAccessControlCardRepository;
import com.yuchen.saas.park.service.mapper.door.config.DoorAccessControlConfigIssueTaskDetailsRepository;
import com.yuchen.saas.park.service.mapper.door.config.DoorAccessControlConfigIssueTaskRepository;
import com.yuchen.saas.park.service.mapper.door.config.DoorAccessControlConfigMappingRepository;
import com.yuchen.saas.park.service.mapper.door.config.DoorAccessControlTimeConfigDetailsRepository;
import com.yuchen.saas.park.service.service.domain.DoorAccessControlConfigDetailsSO;
import com.yuchen.saas.park.service.service.domain.DoorAccessControlConfigMappingDepartmentGroupSO;
import com.yuchen.saas.park.service.service.domain.task.DoorAccessControlConfigIssueTaskSO;
import com.yuchen.saas.park.service.service.door.config.DoorAccessControlQueryService;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.nest.springwrap.core.tool.utils.Func;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Positive;
import javax.validation.constraints.PositiveOrZero;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;

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

@Service
@RequiredArgsConstructor
public class DoorAccessControlQueryServiceImpl implements DoorAccessControlQueryService {
    private final DoorAccessControlCardRepository doorAccessControlCardRepository;

    private final DoorAccessControlConfigMappingRepository doorAccessControlConfigMappingRepository;
    private final DoorAccessControlTimeConfigDetailsRepository doorAccessControlTimeConfigDetailsRepository;

    private final DoorAccessControlConfigIssueTaskRepository doorAccessControlConfigIssueTaskRepository;
    private final DoorAccessControlConfigIssueTaskDetailsRepository doorAccessControlConfigIssueTaskDetailsRepository;

    private final AdapterRepository adapterRepository;

    @Override
    public IPage<DoorAccessControlConfigIssueTaskSO> pageConfigIssueTaskByCondition(PageWrapper<DoorAccessControlConfigIssueTaskCondition> o) {
        val page = doorAccessControlConfigIssueTaskDetailsRepository.pageByCondition(o.getPage(), ofNullable(o.getT()).orElseGet(DoorAccessControlConfigIssueTaskCondition::new));
        val list = page.getRecords();

        if (ObjectUtils.isEmpty(list)) {
            return page.convert(x -> new DoorAccessControlConfigIssueTaskSO());
        }

        val userIds = list.stream().map(DoorAccessControlConfigIssueTaskRO::getUserId).collect(toSet());

        val userIdNameMap = ofNullable(adapterRepository.listDepartmentNameByUserIds(userIds)).orElseGet(List::of)
          .stream()
          .collect(toMap(IdName::getId, IdName::getName));

        val userIdDepartmentNameMap = ofNullable(adapterRepository.listDepartmentNameByUserIds(userIds)).orElseGet(List::of)
          .stream()
          .collect(toMap(IdName::getId, IdName::getName));

        return page.convert(ro -> {
            val so = new DoorAccessControlConfigIssueTaskSO();
            BeanUtils.copyProperties(ro, so);
            so.setUsername(userIdNameMap.get(ro.getUserId()));
            so.setUserDepartmentName(userIdDepartmentNameMap.get(ro.getUserId()));
            return so;
        });
    }

    @Override
    public IPage<DoorAccessControlConfigDetailsSO> pageForDoorAccessControlConfigDetails(PageWrapper<DoorAccessControlConfigDetailsCondition> o) {
        val condition = ofNullable(o.getT()).orElseGet(DoorAccessControlConfigDetailsCondition::new).parse();

        val page = doorAccessControlConfigIssueTaskRepository.pageForDoorAccessControlConfigDetails(o.getPage(), condition);

        val list = page.getRecords();
        if (ObjectUtils.isEmpty(list)) {
            return page.convert(x -> new DoorAccessControlConfigDetailsSO());
        }

        return page.convert(this::map);
    }

    @Override
    public IPage<DoorAccessControlConfigMappingDepartmentGroupSO> pageForDepartmentGroup(PageWrapper<DoorAccessControlConfigMappingDepartmentGroupCondition> o) {
        val page = doorAccessControlConfigMappingRepository.pageForDepartmentGroup(o.getPage(), o.getT());
        val list = page.getRecords();
        if (ObjectUtils.isEmpty(list)) {
            return page.convert(x -> new DoorAccessControlConfigMappingDepartmentGroupSO());
        }

        val doorAccessControlGroupIds = list.stream().map(DoorAccessControlConfigMappingDepartmentGroupRO::getDoorAccessControlGroupId).collect(toSet());
        val map = ofNullable(adapterRepository.listDoorAccessControlGroupCountByGroupIds(doorAccessControlGroupIds)).orElse(List.of())
          .stream()
          .collect(toMap(IdTreeCount::getId, IdTreeCount::getCount));

        return page.convert(ro -> this.map(ro, map));
    }

    private DoorAccessControlConfigMappingDepartmentGroupSO map(DoorAccessControlConfigMappingDepartmentGroupRO ro, Map<@Positive Long, @PositiveOrZero Integer> map) {
        val so = new DoorAccessControlConfigMappingDepartmentGroupSO();
        BeanUtils.copyProperties(ro, so);
        so.setDoorAccessControlGroupCount(map.getOrDefault(ro.getDoorAccessControlGroupId(), 0));
        return so;
    }

    @Override
    public IPage<UserDoorAccessControlRO> pageUserDoorAccessControl(PageWrapper<UserDoorAccessControlCondition> o) {
        return doorAccessControlCardRepository.pageUserDoorAccessControl(o.getPage(), ofNullable(o.getT()).orElse(new UserDoorAccessControlCondition()));
    }

    @Override
    public IPage<DoorAccessControlConfigMappingDepartmentPersonalRO> pageForDepartmentPersonal(PageWrapper<DoorAccessControlConfigMappingDepartmentPersonalCondition> o) {
        return doorAccessControlConfigMappingRepository.pageForDepartmentPersonal(o.getPage(), o.getT());
    }

    @Override
    public IPage<DoorAccessControlConfigMappingGroupGroupRO> pageForGroupGroup(PageWrapper<DoorAccessControlConfigMappingGroupGroupCondition> o) {
        return doorAccessControlConfigMappingRepository.pageForGroupGroup(o.getPage(), o.getT());
    }

    @Override
    public IPage<DoorAccessControlConfigMappingGroupPersonalRO> pageForGroupPersonal(PageWrapper<DoorAccessControlConfigMappingGroupPersonalCondition> o) {
        return doorAccessControlConfigMappingRepository.pageForGroupPersonal(o.getPage(), o.getT());
    }

    @Override
    public IPage<DoorAccessControlConfigMappingPersonalGroupRO> pageForPersonalGroup(PageWrapper<DoorAccessControlConfigMappingPersonalGroupCondition> o) {
        val page = doorAccessControlConfigMappingRepository.pageForPersonalGroup(o.getPage(), ofNullable(o.getT()).orElse(new DoorAccessControlConfigMappingPersonalGroupCondition()));

        val list = page.getRecords();
        if (ObjectUtils.isEmpty(list)) {
            return page;
        }

        this.fixUserDepartmentName(list, DoorAccessControlConfigMappingPersonalGroupRO::getUserId, DoorAccessControlConfigMappingPersonalGroupRO::setUserDepartmentName);

        return page;
    }

    @Override
    public IPage<DoorAccessControlConfigMappingPersonalPersonalRO> pageForPersonalPersonal(PageWrapper<DoorAccessControlConfigMappingPersonalPersonalCondition> o) {
        val page = doorAccessControlConfigMappingRepository.pageForPersonalPersonal(o.getPage(), ofNullable(o.getT()).orElse(new DoorAccessControlConfigMappingPersonalPersonalCondition()));

        val list = page.getRecords();
        if (ObjectUtils.isEmpty(list)) {
            return page;
        }
        for (DoorAccessControlConfigMappingPersonalPersonalRO personalRO : list) {
            List<DoorAccessControlConfigIssueTask> selectList = doorAccessControlConfigIssueTaskRepository.selectList(new LambdaQueryWrapper<DoorAccessControlConfigIssueTask>()
                    .eq(DoorAccessControlConfigIssueTask::getDoorAccessControlId, personalRO.getDoorAccessControlId())
                    .eq(DoorAccessControlConfigIssueTask::getUserId, personalRO.getUserId())
                    .eq(DoorAccessControlConfigIssueTask::getConfigId, personalRO.getTimeConfigId()));
            if(Func.isNotEmpty(selectList)){
                for (DoorAccessControlConfigIssueTask issueTask : selectList) {
                    if((issueTask.getType().getCode() == 0 || issueTask.getType().getCode() == 2) && issueTask.isProcessed()){
                        personalRO.setCard(true);
                        personalRO.setFacial(true);
                        personalRO.setFingerprint(true);
                    } else if(issueTask.getType().getCode() == 3){
                        personalRO.setCard(true);
                    } else if(issueTask.getType().getCode() == 4){
                        personalRO.setFacial(true);
                    } else if(issueTask.getType().getCode() == 5){
                        personalRO.setFingerprint(true);
                    }
                }
            }
        }

        this.fixUserDepartmentName(list, DoorAccessControlConfigMappingPersonalPersonalRO::getUserId, DoorAccessControlConfigMappingPersonalPersonalRO::setUserDepartmentName);

        return page;
    }

    private <T> void fixUserDepartmentName(@NotEmpty List<T> list, Function<T, @Positive Long> userIdExtractor, BiConsumer<T, String> consumer) {
        // 将list中的每个元素通过userIdExtractor方法转换为一个Set集合
        val userIds = list.stream().map(userIdExtractor).collect(toSet());

        // 获取adapterRepository.listDepartmentNameByUserIds(userIds)返回的Collection，并将其转换为Stream
        val userIdDepartmentNameMap = ofNullable(adapterRepository.listDepartmentNameByUserIds(userIds))
          .stream()
          .flatMap(Collection::stream)
          .collect(toMap(IdName::getId, IdName::getName));

        // 遍历list，将每个元素ro和userIdDepartmentNameMap中对应userIdExtractor.apply(ro)的值作为参数传递给consumer.accept方法
        list.forEach(ro -> consumer.accept(ro, userIdDepartmentNameMap.get(userIdExtractor.apply(ro))));

    }

    private DoorAccessControlConfigDetailsSO map(DoorAccessControlConfigDetailsRO ro) {
        val so = new DoorAccessControlConfigDetailsSO();
        BeanUtils.copyProperties(ro, so);

        List<DoorAccessControlConfigIssueTask> list = doorAccessControlConfigIssueTaskRepository.listByTarget(DoorAccessControlConfigTarget.of(ro.getUserId(), ro.getDoorAccessControlId()));
        so.setTasks(list);

        return so;
    }
}
