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

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yuchen.saas.park.api.commons.domain.BatchDoorAccessControl;
import com.yuchen.saas.park.api.commons.domain.DepartmentGroup;
import com.yuchen.saas.park.api.commons.domain.DoorAccessControlConfigSetting;
import com.yuchen.saas.park.api.commons.entity.SimpleEntity;
import com.yuchen.saas.park.api.commons.enums.DoorAccessControlConfigMappingType;
import com.yuchen.saas.park.api.commons.enums.DoorAccessControlConfigState;
import com.yuchen.saas.park.api.entity.door.config.DoorAccessControlConfigMapping;
import com.yuchen.saas.park.api.entity.door.config.DoorAccessControlConfigTrigger;
import com.yuchen.saas.park.api.entity.door.config.DoorAccessControlTimeConfigDetails;
import com.yuchen.saas.park.service.mapper.door.config.DoorAccessControlConfigMappingRepository;
import com.yuchen.saas.park.service.mapper.door.config.DoorAccessControlConfigTriggerRepository;
import com.yuchen.saas.park.service.mapper.door.config.DoorAccessControlTimeConfigDetailsRepository;
import com.yuchen.saas.park.service.service.adapter.AdapterComponent;
import com.yuchen.saas.park.service.service.domain.config.DoorAccessControlConfigMappingDetailsSaveSO;
import com.yuchen.saas.park.service.service.domain.config.DoorAccessControlConfigMappingSaveSO;
import com.yuchen.saas.park.service.service.domain.config.DoorAccessControlConfigTriggerSaveSO;
import com.yuchen.saas.park.service.service.door.config.DoorAccessControlConfigMappingComponent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Positive;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;

import static com.yuchen.saas.park.api.commons.enums.DoorAccessControlConfigMappingType.*;
import static com.yuchen.saas.park.api.commons.enums.DoorAccessControlConfigState.INIT;
import static com.yuchen.saas.park.api.commons.enums.DoorAccessControlConfigType.ALL;
import static java.util.stream.Collectors.*;

@Transactional
@Component
@Slf4j
@RequiredArgsConstructor
public class DoorAccessControlConfigMappingComponentImpl implements DoorAccessControlConfigMappingComponent {
    private final DoorAccessControlConfigMappingRepository doorAccessControlConfigMappingRepository;
    private final DoorAccessControlTimeConfigDetailsRepository doorAccessControlTimeConfigDetailsRepository;
    private final DoorAccessControlConfigTriggerRepository doorAccessControlConfigTriggerRepository;

    private final AdapterComponent adapterComponent;

    @Override
    public void deleteByTypeAndRefIds(DoorAccessControlConfigMappingType type, Set<@Positive Long> userRefIds, Set<@Positive Long> doorAccessControlRefIds, @Positive long timeConfigId) {
        val ids = userRefIds.stream()
          .flatMap(userRefId -> doorAccessControlRefIds.stream().map(doorAccessControlRefId -> doorAccessControlConfigMappingRepository.getByTypeAndRefId(type, userRefId, false, doorAccessControlRefId, timeConfigId)))
          .filter(Optional::isPresent)
          .map(Optional::get)
          .map(SimpleEntity::getId)
          .collect(toSet());

        this.deleteByIds(ids);
    }

    @Override
    public void deleteByDepartmentGroupAndRefIds(DoorAccessControlConfigMappingType type, Set<DepartmentGroup> groups, Set<@Positive Long> doorAccessControlRefIds, @Positive long timeConfigId) {
        val ids = groups.stream()
          .flatMap(o -> doorAccessControlRefIds.stream().map(doorAccessControlRefId -> doorAccessControlConfigMappingRepository.getByTypeAndRefId(type, o.getId(), o.isRecursive(), doorAccessControlRefId, timeConfigId)))
          .filter(Optional::isPresent)
          .map(Optional::get)
          .map(SimpleEntity::getId)
          .collect(toSet());

        this.deleteByIds(ids);
    }

    @Override
    public void deleteById(@Positive long id) {
        this.deleteByIds(Set.of(id));
    }

    @Override
    public void deleteByIds(Set<@Positive Long> ids) {
        if (ids.isEmpty()) {
            return;
        }
        //根据ids批量删除
        doorAccessControlConfigMappingRepository.deleteBatchIds(ids);

        doorAccessControlTimeConfigDetailsRepository.listByParentIds(ids)
          .stream()
          .map(o -> DoorAccessControlConfigTriggerSaveSO.of(o.getTarget(), ALL))
          .forEach(this::saveTrigger);

        doorAccessControlTimeConfigDetailsRepository.deleteByParentIds(ids);
    }

    @Override
    /**
     * 批量绑定部门和门禁控制
     *
     * @param departmentGroups 部门分组集合
     * @param batchDoorAccessControl 批量门禁控制对象
     * @param setting 门禁配置设置对象
     */
    public void batchBindByDepartment(@NotEmpty Set<DepartmentGroup> departmentGroups, BatchDoorAccessControl batchDoorAccessControl, DoorAccessControlConfigSetting setting) {
        // 获取门禁控制组和门禁控制的ID列表
        val doorAccessControlGroupIds = batchDoorAccessControl.getDoorAccessControlGroupIds();
        val doorAccessControlIds = batchDoorAccessControl.getDoorAccessControlIds();

        // 如果门禁控制为空，则直接返回
        if (batchDoorAccessControl.isEmpty()) {
            return;
        }

        // 构建部门ID和部门分组ID的映射关系
        val departmentRecursiveIdMap = this.repairDepartmentGroupIdsMap(departmentGroups);

        // 根据部门id - 用户ids
        val departmentGroupsUserIdsMap = adapterComponent.listUserIdByDepartmentIds(departmentRecursiveIdMap.values().stream().flatMap(Collection::stream).collect(toSet()));

        // 删除已存在的部门和门禁控制的绑定关系
        this.deleteByDepartmentGroupAndRefIds(DEPARTMENT_GROUP, departmentGroups, doorAccessControlGroupIds, setting.getTimeConfigId());
        this.deleteByDepartmentGroupAndRefIds(DEPARTMENT_PERSONAL, departmentGroups, doorAccessControlIds, setting.getTimeConfigId());

        // 遍历部门分组集合
        for (val departmentGroup : departmentGroups) {
            // 获取部门ID
            val departmentId = departmentGroup.getId();

            // 获取该部门分组下的用户ID列表
            val userIds = departmentGroupsUserIdsMap.entrySet()
                .stream()
                .filter(o -> departmentRecursiveIdMap.get(departmentId).contains(o.getKey()))
                .map(Entry::getValue)
                .flatMap(Collection::stream)
                .collect(toSet());

            // 遍历门禁控制组ID列表
            for (val doorAccessControlGroupId : doorAccessControlGroupIds) {
                // 保存部门和门禁控制组的绑定关系
                val doorAccessControlMappingId = this.saveMapping(DoorAccessControlConfigMappingSaveSO.of(DEPARTMENT_GROUP, departmentId, departmentGroup.isRecursive(), doorAccessControlGroupId, setting));

                // 遍历该门禁控制组下的门禁控制ID列表
                for (val doorAccessControlId : batchDoorAccessControl.listByGroupId(doorAccessControlGroupId)) {
                    // 保存部门和门禁控制的绑定关系
                    userIds.forEach(userId -> this.saveDetails(DoorAccessControlConfigMappingDetailsSaveSO.of(doorAccessControlMappingId, userId, doorAccessControlId, setting)));
                }
            }

            // 遍历门禁控制ID列表
            for (val doorAccessControlId : doorAccessControlIds) {
                // 保存部门和个人的绑定关系
                val doorAccessControlMappingId = this.saveMapping(DoorAccessControlConfigMappingSaveSO.of(DEPARTMENT_PERSONAL, departmentId, departmentGroup.isRecursive(), doorAccessControlId, setting));

                // 保存部门和个人的绑定关系
                userIds.forEach(userId -> this.saveDetails(DoorAccessControlConfigMappingDetailsSaveSO.of(doorAccessControlMappingId, userId, doorAccessControlId, setting)));
            }
        }
    }


    @Override
    public void batchBindByUserGroup(@NotEmpty Set<@Positive Long> userGroupIds, BatchDoorAccessControl batchDoorAccessControl, DoorAccessControlConfigSetting setting) {
        // 获取门禁控制组ID列表和门禁控制ID列表
        val doorAccessControlGroupIds = batchDoorAccessControl.getDoorAccessControlGroupIds();
        val doorAccessControlIds = batchDoorAccessControl.getDoorAccessControlIds();

        // 如果批量门禁控制为空，则直接返回
        if (batchDoorAccessControl.isEmpty()) {
            return;
        }

        // 删除指定类型和引用ID的记录
        this.deleteByTypeAndRefIds(GROUP_GROUP, userGroupIds, doorAccessControlGroupIds, setting.getTimeConfigId());
        this.deleteByTypeAndRefIds(GROUP_PERSONAL, userGroupIds, doorAccessControlIds, setting.getTimeConfigId());

        // 获取用户分组ID对应的用户ID列表映射
        val userGroupIdsMap = adapterComponent.listUserIdByUserGroupIds(userGroupIds);

        // 遍历用户组ID列表
        for (val userGroupId : userGroupIds) {
            // 获取用户组ID对应的用户ID列表
            val userIds = userGroupIdsMap.getOrDefault(userGroupId, Set.of());

            // 遍历门禁控制组ID列表
            for (val doorAccessControlGroupId : doorAccessControlGroupIds) {
                // 保存门禁控制组映射ID
                val doorAccessControlMappingId = this.saveMapping(DoorAccessControlConfigMappingSaveSO.of(GROUP_GROUP, userGroupId, doorAccessControlGroupId, setting));

                // 遍历批量门禁控制列表中的门禁控制ID列表
                for (val doorAccessControlId : batchDoorAccessControl.listByGroupId(doorAccessControlGroupId)) {
                    // 遍历用户ID列表，保存门禁控制组映射详情
                    userIds.forEach(userId -> this.saveDetails(DoorAccessControlConfigMappingDetailsSaveSO.of(doorAccessControlMappingId, userId, doorAccessControlId, setting)));
                }
            }

            // 遍历门禁控制ID列表
            for (val doorAccessControlId : doorAccessControlIds) {
                // 保存个人门禁控制映射ID
                val doorAccessControlMappingId = this.saveMapping(DoorAccessControlConfigMappingSaveSO.of(GROUP_PERSONAL, userGroupId, doorAccessControlId, setting));

                // 遍历用户ID列表，保存个人门禁控制映射详情
                userIds.forEach(userId -> this.saveDetails(DoorAccessControlConfigMappingDetailsSaveSO.of(doorAccessControlMappingId, userId, doorAccessControlId, setting)));
            }
        }

    }

    @Override
    public void batchBindByUser(@NotEmpty Set<@Positive Long> userIds, BatchDoorAccessControl batchDoorAccessControl, DoorAccessControlConfigSetting setting) {
        // 获取门禁控制组ID列表和门禁控制ID列表
        val doorAccessControlGroupIds = batchDoorAccessControl.getDoorAccessControlGroupIds();
        val doorAccessControlIds = batchDoorAccessControl.getDoorAccessControlIds();
        // 如果批量门禁控制为空，则直接返回
        if (batchDoorAccessControl.isEmpty()) {
            return;
        }
        // 根据类型和个人组ID、用户ID、门禁控制组ID和时间配置ID进行删除操作
        this.deleteByTypeAndRefIds(PERSONAL_GROUP, userIds, doorAccessControlGroupIds, setting.getTimeConfigId());
        // 遍历用户ID列表
        for (val userId : userIds) {
            // 遍历门禁控制组ID列表
            for (val doorAccessControlGroupId : doorAccessControlGroupIds) {
                // 保存映射关系
                val doorAccessControlMappingId = this.saveMapping(DoorAccessControlConfigMappingSaveSO.of(PERSONAL_GROUP, userId, doorAccessControlGroupId, setting));
                // 遍历门禁控制ID列表
                for (val doorAccessControlId : batchDoorAccessControl.listByGroupId(doorAccessControlGroupId)) {
                    // 保存详细信息
                    this.saveDetails(DoorAccessControlConfigMappingDetailsSaveSO.of(doorAccessControlMappingId, userId, doorAccessControlId, setting));
                }
            }
            // 遍历门禁控制ID列表
            for (val doorAccessControlId : doorAccessControlIds) {
                // 保存ConfigMapping映射关系
                val doorAccessControlMappingId = this.saveMapping(DoorAccessControlConfigMappingSaveSO.of(PERSONAL_PERSONAL, userId, doorAccessControlId, setting));
                // 保存详细信息
                this.saveDetails(DoorAccessControlConfigMappingDetailsSaveSO.of(doorAccessControlMappingId, userId, doorAccessControlId, setting));
            }
        }
    }

    // 同次操作时: 同一部门不能同时 recursive + !recursive
    private Map<@Positive Long, Set<@Positive Long>> repairDepartmentGroupIdsMap(@NotEmpty Set<DepartmentGroup> set) {
        val recursiveRootIds = set.stream().filter(DepartmentGroup::isRecursive).map(DepartmentGroup::getId).collect(toSet());

        val map = adapterComponent.listDepartmentIdsByDepartmentIdsRecursive(recursiveRootIds);

        return set.stream().collect(toMap(DepartmentGroup::getId, o -> o.isRecursive() ? map.getOrDefault(o.getId(), Set.of(o.getId())) : Set.of(o.getId())));
    }

    @Positive
    @Override
    public long saveMapping(DoorAccessControlConfigMappingSaveSO so) {
        val setting = so.getSetting();

        val exists = doorAccessControlConfigMappingRepository.getByTypeAndRefId(so.getType(), so.getUserRefId(), so.isRecursive(), so.getDoorAccessControlRefId(), setting.getTimeConfigId()).orElse(null);

        if (exists != null) {
            exists.setSetting(setting);

            doorAccessControlConfigMappingRepository.updateById(exists);

            return exists.getId();
        }

        val o = new DoorAccessControlConfigMapping()
          .setType(so.getType())
          .setUserRefId(so.getUserRefId())
          .setRecursive(so.isRecursive())
          .setDoorAccessControlRefId(so.getDoorAccessControlRefId())
          .setSetting(setting);

        doorAccessControlConfigMappingRepository.insert(o);
        return o.getId();
    }

    /**
     * 保留最新配置
     * 保存时预期状态均为{@link DoorAccessControlConfigState#INIT}
     * 并通过{@link DoorAccessControlConfigTrigger}触发后续任务
     */
    @Positive
    @Override
    public long saveDetails(DoorAccessControlConfigMappingDetailsSaveSO so) {
        // 获取目标对象
        val target = so.getTarget();

        // 根据目标对象和父级ID获取对应的门禁时间配置详情对象，如果不存在则返回null
        val exists = doorAccessControlTimeConfigDetailsRepository.getByTargetAndParentId(target, so.getDoorAccessControlMappingId()).orElse(null);

        // 如果存在对应的门禁时间配置详情对象
        if (exists != null) {
            // 设置门禁时间配置详情对象的设置值，并重置状态
            exists.setSetting(so.getSetting()).resetState();

            // 更新门禁时间配置详情对象
            doorAccessControlTimeConfigDetailsRepository.updateById(exists);

            // 保存门禁配置触发器
            this.saveTrigger(DoorAccessControlConfigTriggerSaveSO.of(target, ALL));

            // 返回门禁时间配置详情对象的ID
            return exists.getId();
        }

        // 创建新的门禁时间配置详情对象
        val o = new DoorAccessControlTimeConfigDetails()
          .setTarget(target)
          .setDoorAccessControlMappingId(so.getDoorAccessControlMappingId())
          .setSetting(so.getSetting())
          .setState(INIT);

        // 插入门禁时间配置详情对象
        doorAccessControlTimeConfigDetailsRepository.insert(o);

        // 保存门禁配置触发器
        this.saveTrigger(DoorAccessControlConfigTriggerSaveSO.of(target, ALL));

        // 返回门禁时间配置详情对象的ID
        return o.getId();

    }

    @Positive
    @Override
    public long saveTrigger(DoorAccessControlConfigTriggerSaveSO so) {
        // 获取目标对象
        val target = so.getTarget();

        // 根据目标对象和类型获取对应的门禁配置触发器对象，如果不存在则返回null
        val exists = doorAccessControlConfigTriggerRepository.getByTargetAndType(target, so.getType()).orElse(null);

        // 如果存在对应的门禁配置触发器对象
        if (exists != null) {
            // 保留之前可能存在的处理时间
            exists.setProcessed(false);

            // 更新门禁配置触发器对象
            doorAccessControlConfigTriggerRepository.updateById(exists);

            // 返回门禁配置触发器对象的ID
            return exists.getId();
        }

        // 创建新的门禁配置触发器对象
        val o = new DoorAccessControlConfigTrigger().setTarget(target).setType(so.getType());

        // 插入门禁配置触发器对象
        doorAccessControlConfigTriggerRepository.insert(o);

        // 返回门禁配置触发器对象的ID
        return o.getId();

    }

    @Override
    public int queryUseNumByTemplateId(long templateId) {
        return doorAccessControlConfigMappingRepository.selectCount(new LambdaQueryWrapper<DoorAccessControlConfigMapping>().eq(DoorAccessControlConfigMapping::getTimeConfigId, templateId));
    }
}
