package tech.easyflow.system.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tech.easyflow.system.entity.SysRoleCategoryScope;
import tech.easyflow.system.entity.SysRoleCategoryScopeItem;
import tech.easyflow.system.entity.vo.SysRoleCategoryScopeDetailVo;
import tech.easyflow.system.entity.vo.SysRoleCategoryScopeItemVo;
import tech.easyflow.system.enums.CategoryResourceType;
import tech.easyflow.system.enums.CategoryScopeMode;
import tech.easyflow.system.mapper.SysRoleCategoryScopeItemMapper;
import tech.easyflow.system.mapper.SysRoleCategoryScopeMapper;
import tech.easyflow.system.service.SysRoleCategoryScopeService;

import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class SysRoleCategoryScopeServiceImpl extends ServiceImpl<SysRoleCategoryScopeMapper, SysRoleCategoryScope>
        implements SysRoleCategoryScopeService {

    @Resource
    private SysRoleCategoryScopeMapper sysRoleCategoryScopeMapper;

    @Resource
    private SysRoleCategoryScopeItemMapper sysRoleCategoryScopeItemMapper;

    @Override
    public SysRoleCategoryScopeDetailVo getRoleScopeDetail(BigInteger roleId) {
        SysRoleCategoryScopeDetailVo detailVo = new SysRoleCategoryScopeDetailVo();
        detailVo.setRoleId(roleId);
        if (roleId == null) {
            detailVo.setScopes(defaultScopes());
            return detailVo;
        }

        QueryWrapper scopeWrapper = QueryWrapper.create().eq(SysRoleCategoryScope::getRoleId, roleId);
        List<SysRoleCategoryScope> scopes = list(scopeWrapper);
        if (CollectionUtil.isEmpty(scopes)) {
            detailVo.setScopes(defaultScopes());
            return detailVo;
        }

        List<BigInteger> scopeIds = scopes.stream().map(SysRoleCategoryScope::getId).collect(Collectors.toList());
        Map<BigInteger, List<BigInteger>> scopeItemMap = new LinkedHashMap<>();
        if (CollectionUtil.isNotEmpty(scopeIds)) {
            QueryWrapper itemWrapper = QueryWrapper.create().in(SysRoleCategoryScopeItem::getScopeId, scopeIds);
            List<SysRoleCategoryScopeItem> items = sysRoleCategoryScopeItemMapper.selectListByQuery(itemWrapper);
            scopeItemMap = items.stream().collect(Collectors.groupingBy(
                    SysRoleCategoryScopeItem::getScopeId,
                    LinkedHashMap::new,
                    Collectors.mapping(SysRoleCategoryScopeItem::getCategoryId, Collectors.toList())
            ));
        }

        Map<String, SysRoleCategoryScope> scopeMap = scopes.stream().collect(Collectors.toMap(
                SysRoleCategoryScope::getResourceType,
                item -> item,
                (left, right) -> left,
                LinkedHashMap::new
        ));

        List<SysRoleCategoryScopeItemVo> result = new ArrayList<>();
        for (String resourceType : CategoryResourceType.allCodes()) {
            SysRoleCategoryScope existing = scopeMap.get(resourceType);
            SysRoleCategoryScopeItemVo itemVo = new SysRoleCategoryScopeItemVo();
            itemVo.setResourceType(resourceType);
            if (existing == null) {
                itemVo.setScopeMode(CategoryScopeMode.CUSTOM.getCode());
                itemVo.setCategoryIds(new ArrayList<>());
            } else {
                itemVo.setScopeMode(CategoryScopeMode.from(existing.getScopeMode()).getCode());
                itemVo.setCategoryIds(new ArrayList<>(scopeItemMap.getOrDefault(existing.getId(), new ArrayList<>())));
            }
            result.add(itemVo);
        }
        detailVo.setScopes(result);
        return detailVo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveRoleScopes(BigInteger roleId, List<SysRoleCategoryScopeItemVo> scopes, BigInteger operatorId) {
        QueryWrapper scopeWrapper = QueryWrapper.create().eq(SysRoleCategoryScope::getRoleId, roleId);
        List<SysRoleCategoryScope> existingScopes = sysRoleCategoryScopeMapper.selectListByQuery(scopeWrapper);
        if (CollectionUtil.isNotEmpty(existingScopes)) {
            List<BigInteger> existingScopeIds = existingScopes.stream()
                    .map(SysRoleCategoryScope::getId)
                    .collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(existingScopeIds)) {
                QueryWrapper itemDeleteWrapper = QueryWrapper.create().in(SysRoleCategoryScopeItem::getScopeId, existingScopeIds);
                sysRoleCategoryScopeItemMapper.deleteByQuery(itemDeleteWrapper);
            }
            sysRoleCategoryScopeMapper.deleteByQuery(scopeWrapper);
        }

        Date now = new Date();
        for (SysRoleCategoryScopeItemVo itemVo : normalizeScopes(scopes)) {
            SysRoleCategoryScope scope = new SysRoleCategoryScope();
            scope.setRoleId(roleId);
            scope.setResourceType(itemVo.getResourceType());
            scope.setScopeMode(itemVo.getScopeMode());
            scope.setCreated(now);
            scope.setCreatedBy(operatorId);
            scope.setModified(now);
            scope.setModifiedBy(operatorId);
            sysRoleCategoryScopeMapper.insert(scope);

            if (!CategoryScopeMode.CUSTOM.getCode().equals(itemVo.getScopeMode())
                    || CollectionUtil.isEmpty(itemVo.getCategoryIds())) {
                continue;
            }
            for (BigInteger categoryId : itemVo.getCategoryIds()) {
                SysRoleCategoryScopeItem scopeItem = new SysRoleCategoryScopeItem();
                scopeItem.setScopeId(scope.getId());
                scopeItem.setCategoryId(categoryId);
                sysRoleCategoryScopeItemMapper.insert(scopeItem);
            }
        }
    }

    private List<SysRoleCategoryScopeItemVo> defaultScopes() {
        return normalizeScopes(null);
    }

    private List<SysRoleCategoryScopeItemVo> normalizeScopes(List<SysRoleCategoryScopeItemVo> scopes) {
        Map<String, SysRoleCategoryScopeItemVo> scopeMap = new LinkedHashMap<>();
        if (CollectionUtil.isNotEmpty(scopes)) {
            for (SysRoleCategoryScopeItemVo itemVo : scopes) {
                if (itemVo == null) {
                    continue;
                }
                String resourceType = CategoryResourceType.from(itemVo.getResourceType()).getCode();
                CategoryScopeMode scopeMode = CategoryScopeMode.from(itemVo.getScopeMode());
                SysRoleCategoryScopeItemVo normalized = new SysRoleCategoryScopeItemVo();
                normalized.setResourceType(resourceType);
                normalized.setScopeMode(scopeMode.getCode());
                Set<BigInteger> categoryIds = itemVo.getCategoryIds() == null
                        ? new LinkedHashSet<>()
                        : itemVo.getCategoryIds().stream()
                        .filter(item -> item != null)
                        .collect(Collectors.toCollection(LinkedHashSet::new));
                normalized.setCategoryIds(new ArrayList<>(categoryIds));
                scopeMap.put(resourceType, normalized);
            }
        }

        List<SysRoleCategoryScopeItemVo> result = new ArrayList<>();
        for (String resourceType : CategoryResourceType.allCodes()) {
            SysRoleCategoryScopeItemVo itemVo = scopeMap.get(resourceType);
            if (itemVo == null) {
                itemVo = new SysRoleCategoryScopeItemVo();
                itemVo.setResourceType(resourceType);
                itemVo.setScopeMode(CategoryScopeMode.CUSTOM.getCode());
                itemVo.setCategoryIds(new ArrayList<>());
            }
            result.add(itemVo);
        }
        return result;
    }
}
