package com.neusoft.smart.pos.inventory.service;

import com.neusoft.smart.pos.dto.inventory.request.CreateCategoryRequest;
import com.neusoft.smart.pos.dto.inventory.request.GetCategoriesRequest;
import com.neusoft.smart.pos.dto.inventory.request.UpdateCategoryRequest;
import com.neusoft.smart.pos.dto.inventory.response.CategoryResponse;
import com.neusoft.smart.pos.dto.inventory.response.InventoryCategoryTreeResponse;
import com.neusoft.smart.pos.framework.exception.BusinessCommonException;
import com.neusoft.smart.pos.framework.utils.BeanUtil;
import com.neusoft.smart.pos.inventory.constants.GoodsConstant;
import com.neusoft.smart.pos.inventory.constants.GoodsErrorCode;
import com.neusoft.smart.pos.inventory.persistent.domain.InventoryGoodsCategory;
import com.neusoft.smart.pos.inventory.persistent.domain.InventoryGoodsCategoryExample;
import com.neusoft.smart.pos.inventory.persistent.domain.InventoryGoodsInfoExample;
import com.neusoft.smart.pos.inventory.persistent.mapper.InventoryGoodsCategoryMapper;
import com.neusoft.smart.pos.inventory.persistent.mapper.InventoryGoodsInfoMapper;
import com.neusoft.smart.pos.utils.RequestHeaderUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@EnableTransactionManagement
@Service
public class InventoryGoodsCategoryService {
    
    @Resource
    InventoryGoodsCategoryMapper inventorygoodsCategoryMapper;
    
    @Resource
    InventoryGoodsInfoMapper inventoryGoodsInfoMapper;
    
    public List<CategoryResponse> selectCategories(GetCategoriesRequest request, GoodsConstant.Industry industry) {
        InventoryGoodsCategoryExample example = new InventoryGoodsCategoryExample();
        InventoryGoodsCategoryExample.Criteria criteria = example.createCriteria();
        
        criteria.andDeletedEqualTo(0);
        
        if (request.getPreset() != null && request.getPreset() == 1) {
            criteria.andOrgIdEqualTo(0)
                    .andShopIdEqualTo(0);
        } else {
            Integer orgId = RequestHeaderUtil.getOrgId();
            criteria.andOrgIdEqualTo(orgId);
            
            if (request.getShop() != null) {
                List<Integer> storeIds = RequestHeaderUtil.getStoreIds();
                if (storeIds != null && storeIds.contains(request.getShop())) {
                    criteria.andShopIdEqualTo(request.getShop());
                } else {
                    throw new BusinessCommonException(GoodsErrorCode.INVALID_SHOP);
                }
            }
        }
        
        List<CategoryResponse> list = new ArrayList<>();
        for (InventoryGoodsCategory category : inventorygoodsCategoryMapper.selectByExample(example)) {
            CategoryResponse categoryResponse = new CategoryResponse();
            categoryResponse.setId(category.getId());
            categoryResponse.setParent(category.getParentId());
            categoryResponse.setName(category.getName());
            categoryResponse.setOrg(category.getOrgId());
            categoryResponse.setShop(category.getShopId());
            categoryResponse.setStore(category.getShopId());
            categoryResponse.setOrder(category.getPriority());
            list.add(categoryResponse);
        }
        
        return list;
    }
    
    public List<CategoryResponse> selectCategories(GetCategoriesRequest request) {
        Integer orgId = RequestHeaderUtil.getOrgId();
        request.setOrg(orgId);
        if ("admin".equals(RequestHeaderUtil.getRole())) {
            request.setShop(0);
        } else if (request.getStore() != null) {
            List<Integer> storeIds = RequestHeaderUtil.getStoreIds();
            if (storeIds != null && !storeIds.contains(request.getStore())) {
                throw new BusinessCommonException(GoodsErrorCode.INVALID_SHOP);
            }
            
            request.setShop(request.getStore());
        } else {
            throw new BusinessCommonException(GoodsErrorCode.MISSING_STORE_ID);
        }
        
        List<CategoryResponse> list = new ArrayList<>();
        for (InventoryGoodsCategory category : doSelect(request)) {
            CategoryResponse categoryResponse = new CategoryResponse();
            categoryResponse.setId(category.getId());
            categoryResponse.setParent(category.getParentId());
            categoryResponse.setName(category.getName());
            categoryResponse.setShop(category.getShopId());
            categoryResponse.setOrg(category.getOrgId());
            categoryResponse.setStore(category.getShopId());
            categoryResponse.setOrder(category.getPriority());
            list.add(categoryResponse);
        }
        
        return list;
    }
    
    public List<InventoryGoodsCategory> selectCategories(Integer org, Integer store, GoodsConstant.Industry industry) {
        GetCategoriesRequest request = new GetCategoriesRequest();
        request.setOrg(org);
        request.setShop(store);
//        request.setIndustry(industry.getCode());
        
        return doSelect(request);
    }
    
    private List<InventoryGoodsCategory> doSelect(GetCategoriesRequest request) {
        InventoryGoodsCategoryExample example = new InventoryGoodsCategoryExample();
        InventoryGoodsCategoryExample.Criteria criteria = example.createCriteria();
        
        example.setOrderByClause("`priority` desc");

        criteria.andDeletedEqualTo(0);
        if (request.getOrg() != null) criteria.andOrgIdEqualTo(request.getOrg());
        if (request.getShop() != null) criteria.andShopIdEqualTo(request.getShop());
        if (request.getStore() != null) criteria.andShopIdEqualTo(request.getStore());
        if (request.getParent() != null) criteria.andParentIdEqualTo(request.getParent());
        if (request.getDeleted() != null) criteria.andDeletedEqualTo(request.getDeleted());
        
        return inventorygoodsCategoryMapper.selectByExample(example);
    }
    
    public CategoryResponse insertCategoryWithRoleCheck(CreateCategoryRequest request) {
        Integer orgId = RequestHeaderUtil.getOrgId();
        request.setOrg(orgId);
        if ("admin".equals(RequestHeaderUtil.getRole())) {
            request.setStore(0);
        } else if (request.getStore() != null) {
            List<Integer> storeIds = RequestHeaderUtil.getStoreIds();
            if (storeIds == null || !storeIds.contains(request.getStore())) {
                throw new BusinessCommonException(GoodsErrorCode.INVALID_SHOP);
            }
            
            request.setStore(request.getStore());
        } else {
            throw new BusinessCommonException(GoodsErrorCode.MISSING_STORE_ID);
        }
        InventoryGoodsCategory inventoryGoodsCategory = insertCategory(request);
        CategoryResponse categoryResponse = new CategoryResponse();
        categoryResponse.setId(inventoryGoodsCategory.getId());
        categoryResponse.setParent(inventoryGoodsCategory.getParentId());
        categoryResponse.setOrg(inventoryGoodsCategory.getOrgId());
        categoryResponse.setShop(inventoryGoodsCategory.getShopId());
        categoryResponse.setName(inventoryGoodsCategory.getName());
        categoryResponse.setOrder(inventoryGoodsCategory.getPriority());
        categoryResponse.setStore(inventoryGoodsCategory.getShopId());
        return categoryResponse;
    }
    
    @Transactional
    public InventoryGoodsCategory insertCategory(CreateCategoryRequest request) {
        checkoutDuplicateName(request.getName(), request.getOrg(), request.getStore(), request.getIndustry(), null);
        
        Integer lastOrderVal = inventorygoodsCategoryMapper.findMaxCategoryPriority(request.getOrg(), request.getStore());
        
        InventoryGoodsCategory category = new InventoryGoodsCategory();
        
        category.setName(validateName(request.getName()));
        category.setParentId(request.getParent() != null ? request.getParent() : 0);
        if (request.getOrg() != null) category.setOrgId(request.getOrg());
        if (request.getStore() != null) category.setShopId(request.getStore());
        
        category.setPriority((lastOrderVal != null ? lastOrderVal + 1 : 0));
        
        Date now = new Date();
        category.setCreatedTime(now);
        category.setUpdatedTime(now);
        
        inventorygoodsCategoryMapper.insertSelective(category);
        
        return category;
    }
    
    public void updateCategory(Integer id, UpdateCategoryRequest request) {
        Integer orgId = RequestHeaderUtil.getOrgId();
        Integer storeId;
        if ("admin".equals(RequestHeaderUtil.getRole())) {
            storeId = 0;
        } else if (request.getStore() != null) {
            List<Integer> storeIds = RequestHeaderUtil.getStoreIds();
            if (storeIds == null || !storeIds.contains(request.getStore())) {
                throw new BusinessCommonException(GoodsErrorCode.INVALID_SHOP);
            }
            
            storeId = request.getStore();
        } else {
            throw new BusinessCommonException(GoodsErrorCode.MISSING_STORE_ID);
        }
        
        checkoutDuplicateName(request.getName(), orgId, storeId, request.getIndustry(), id);
        
        InventoryGoodsCategory category = new InventoryGoodsCategory();
        
        category.setId(id);
        
        category.setName(request.getName());
        if (request.getParent() != null) category.setParentId(request.getParent());
        category.setUpdatedTime(new Date());
        
        inventorygoodsCategoryMapper.updateByPrimaryKeySelective(category);
    }
    
    public String validateName(String name) {
        if (StringUtils.isEmpty(name)) {
            throw new BusinessCommonException(GoodsErrorCode.EMPTY_CATEGORY_NAME);
        }
        return name;
    }
    
    private void checkoutDuplicateName(String name, Integer org, Integer store, Byte industry, Integer id) {
        InventoryGoodsCategoryExample example = new InventoryGoodsCategoryExample();
        InventoryGoodsCategoryExample.Criteria criteria = example.createCriteria();
        criteria.andNameEqualTo(validateName(name))
                .andOrgIdEqualTo(org)
                .andShopIdEqualTo(store)
                .andDeletedEqualTo(0);
        
        if (id != null) {
            criteria.andIdNotEqualTo(id);
        }
        
        if (inventorygoodsCategoryMapper.countByExample(example) > 0) {
            throw new BusinessCommonException(GoodsErrorCode.DUPLICATE_CATEGORY_NAME);
        }
    }
    
    @Transactional
    public void reorder(Map<Integer, Integer> orderMap) {
        InventoryGoodsCategoryExample example = new InventoryGoodsCategoryExample();
        example.createCriteria().andIdIn(new ArrayList<>(orderMap.keySet()));
        for (InventoryGoodsCategory category : inventorygoodsCategoryMapper.selectByExample(example)) {
            category.setPriority(orderMap.get(category.getId()));
            category.setUpdatedTime(new Date());
            inventorygoodsCategoryMapper.updateByPrimaryKey(category);
        }
    }
    
    @Transactional
    public void deleteCateringCategory(Integer id) {
        InventoryGoodsInfoExample example = new InventoryGoodsInfoExample();
        example.createCriteria().andDeletedEqualTo((0))
                .andCateIdEqualTo(id);
        if (inventoryGoodsInfoMapper.countByExample(example) > 0) {
            throw new BusinessCommonException(GoodsErrorCode.CONTAINS_GOODS_WITH_CATEGORY);
        }
        
        InventoryGoodsCategory category = new InventoryGoodsCategory();
        category.setId(id);
        category.setUpdatedTime(new Date());
        category.setDeleted(1);
        
        inventorygoodsCategoryMapper.updateByPrimaryKeySelective(category);
    }
    
    public List<CategoryResponse> selectCategoriesByShopId(Integer shopId) {
        InventoryGoodsCategoryExample example = new InventoryGoodsCategoryExample();
        InventoryGoodsCategoryExample.Criteria criteria = example.createCriteria();
        
        criteria.andDeletedEqualTo(0);
        criteria.andShopIdEqualTo(shopId);
        return inventorygoodsCategoryMapper.selectByExample(example).parallelStream()
                .map(item -> {
                    CategoryResponse categoryResponse = new CategoryResponse();
                    categoryResponse.setId(item.getId());
                    categoryResponse.setParent(item.getParentId());
                    categoryResponse.setOrg(item.getOrgId());
                    categoryResponse.setName(item.getName());
                    categoryResponse.setShop(item.getShopId());
                    categoryResponse.setStore(item.getShopId());
                    categoryResponse.setOrder(item.getPriority());
                    return categoryResponse;
                })
                .collect(Collectors.toList());
    }
    
    /**
     * 树形分类列表
     *
     * @param request
     * @return
     */
    public List<InventoryCategoryTreeResponse> getCategoryTree(GetCategoriesRequest request) {
        List<InventoryGoodsCategory> inventoryGoodsCategories = this.doSelect(request);
        return this.buildTree(BeanUtil.parseList(inventoryGoodsCategories, InventoryCategoryTreeResponse.class));
    }
    
    
    /**
     * 使用递归方法建树
     *
     * @param treeNodes
     * @return
     */
    private List<InventoryCategoryTreeResponse> buildTree(List<InventoryCategoryTreeResponse> treeNodes) {
        List<InventoryCategoryTreeResponse> trees = new ArrayList<>();
        for (InventoryCategoryTreeResponse treeNode : treeNodes) {
            if (treeNode.getParentId().equals(0)) {
                trees.add(findChildren(treeNode, treeNodes));
            }
        }
        return trees;
    }
    
    /**
     * 递归查找子节点
     *
     * @param treeNodes
     * @return
     */
    private InventoryCategoryTreeResponse findChildren(InventoryCategoryTreeResponse treeNode, List<InventoryCategoryTreeResponse> treeNodes) {
        for (InventoryCategoryTreeResponse it : treeNodes) {
            if (treeNode.getId().equals(it.getParentId())) {
                treeNode.getChildren().add(findChildren(it, treeNodes));
            }
        }
        //兼容前端级联选择框叶子节点如果children是空集合也会认为是父节点的问题
        if (treeNode.getChildren().isEmpty()) {
            treeNode.setChildren(null);
        } else {
            treeNode.getChildren().sort(Comparator.comparing(InventoryCategoryTreeResponse::getPriority));
        }
        return treeNode;
    }
}
