package com.ruoyi.web.modules.template.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.web.modules.category.facade.IGoodsFacade;
import com.ruoyi.web.modules.category.facade.IShopFacade;
import com.ruoyi.web.modules.category.support.SellerShopContext;
import com.ruoyi.web.modules.template.constant.AttrTemplateConstants;
import com.ruoyi.web.modules.template.domain.BizGoodsAttrTemplate;
import com.ruoyi.web.modules.template.domain.BizGoodsAttrTemplateItem;
import com.ruoyi.web.modules.template.domain.BizGoodsAttrTemplateValue;
import com.ruoyi.web.modules.template.dto.AttrTemplateItemDTO;
import com.ruoyi.web.modules.template.dto.AttrTemplateSaveDTO;
import com.ruoyi.web.modules.template.exception.AttrTemplateBatchDeleteException;
import com.ruoyi.web.modules.template.mapper.BizGoodsAttrTemplateItemMapper;
import com.ruoyi.web.modules.template.mapper.BizGoodsAttrTemplateMapper;
import com.ruoyi.web.modules.template.mapper.BizGoodsAttrTemplateValueMapper;
import com.ruoyi.web.modules.template.service.IAttrTemplateService;
import com.ruoyi.web.modules.template.vo.AttrTemplateDetailVO;
import com.ruoyi.web.modules.template.vo.AttrTemplateListVO;
import com.ruoyi.web.modules.template.vo.AttrTemplateOptionVO;

/**
 * 属性模版业务实现
 */
@Service
public class AttrTemplateServiceImpl implements IAttrTemplateService
{
    @Autowired
    private BizGoodsAttrTemplateMapper templateMapper;

    @Autowired
    private BizGoodsAttrTemplateItemMapper itemMapper;

    @Autowired
    private BizGoodsAttrTemplateValueMapper valueMapper;

    @Autowired
    private IGoodsFacade goodsFacade;

    @Autowired
    private IShopFacade shopFacade;

    @Override
    public List<AttrTemplateListVO> selectTemplateList(String keyword)
    {
        Long shopId = requireShopId();
        assertShopMaintable(shopId);
        BizGoodsAttrTemplate query = new BizGoodsAttrTemplate();
        query.setShopId(shopId);
        query.setKeyword(keyword);
        List<BizGoodsAttrTemplate> templates = templateMapper.selectList(query);
        if (templates.isEmpty())
        {
            return Collections.emptyList();
        }
        Map<Long, String> nameListMap = buildNameListMap(
                templates.stream().map(BizGoodsAttrTemplate::getTemplateId).collect(Collectors.toList()));
        List<AttrTemplateListVO> rows = new ArrayList<>();
        for (BizGoodsAttrTemplate template : templates)
        {
            AttrTemplateListVO vo = new AttrTemplateListVO();
            vo.setTemplateId(template.getTemplateId());
            vo.setTemplateName(template.getTemplateName());
            vo.setNameList(nameListMap.getOrDefault(template.getTemplateId(), StringUtils.EMPTY));
            rows.add(vo);
        }
        return rows;
    }

    @Override
    public AttrTemplateDetailVO selectTemplateDetail(Long templateId)
    {
        Long shopId = requireShopId();
        assertShopMaintable(shopId);
        return loadDetail(templateId, shopId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertTemplate(AttrTemplateSaveDTO dto, String operator)
    {
        Long shopId = requireShopId();
        assertShopMaintable(shopId);
        validateSaveDto(dto);
        assertTemplateNameUnique(shopId, dto.getTemplateName(), null);
        BizGoodsAttrTemplate template = new BizGoodsAttrTemplate();
        template.setShopId(shopId);
        template.setTemplateName(dto.getTemplateName().trim());
        template.setDelFlag(AttrTemplateConstants.DEL_FLAG_NORMAL);
        template.setCreateBy(operator);
        templateMapper.insert(template);
        persistItems(template.getTemplateId(), dto.getAttributes(), dto.getSpecs());
        return 1;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateTemplate(AttrTemplateSaveDTO dto, String operator)
    {
        Long shopId = requireShopId();
        assertShopMaintable(shopId);
        if (dto.getTemplateId() == null)
        {
            throw new ServiceException(AttrTemplateConstants.MSG_TEMPLATE_NOT_FOUND);
        }
        BizGoodsAttrTemplate existing = templateMapper.selectById(dto.getTemplateId(), shopId);
        if (existing == null)
        {
            throw new ServiceException(AttrTemplateConstants.MSG_TEMPLATE_NOT_FOUND);
        }
        validateSaveDto(dto);
        assertTemplateNameUnique(shopId, dto.getTemplateName(), dto.getTemplateId());
        BizGoodsAttrTemplate template = new BizGoodsAttrTemplate();
        template.setTemplateId(dto.getTemplateId());
        template.setShopId(shopId);
        template.setTemplateName(dto.getTemplateName().trim());
        template.setUpdateBy(operator);
        templateMapper.update(template);
        replaceItems(dto.getTemplateId(), dto.getAttributes(), dto.getSpecs());
        return 1;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteTemplateByIds(Long[] templateIds, String operator)
    {
        Long shopId = requireShopId();
        assertShopMaintable(shopId);
        if (templateIds == null || templateIds.length == 0)
        {
            throw new ServiceException(AttrTemplateConstants.MSG_DELETE_IDS_REQUIRED);
        }
        List<String> reasons = new ArrayList<>();
        List<Long> validIds = new ArrayList<>();
        for (Long templateId : templateIds)
        {
            BizGoodsAttrTemplate template = templateMapper.selectById(templateId, shopId);
            if (template == null)
            {
                continue;
            }
            if (goodsFacade.existsByAttrTemplateId(templateId))
            {
                reasons.add("属性模版「" + template.getTemplateName() + "」已被商品使用，无法删除");
            }
            else
            {
                validIds.add(templateId);
            }
        }
        if (!reasons.isEmpty())
        {
            throw new AttrTemplateBatchDeleteException(AttrTemplateConstants.MSG_BATCH_DELETE, reasons);
        }
        if (validIds.isEmpty())
        {
            throw new ServiceException(AttrTemplateConstants.MSG_TEMPLATE_NOT_FOUND);
        }
        for (Long templateId : validIds)
        {
            valueMapper.deleteByTemplateId(templateId);
            itemMapper.deleteByTemplateId(templateId);
        }
        templateMapper.logicDeleteByIds(validIds, shopId, operator);
    }

    @Override
    public List<AttrTemplateOptionVO> selectOptions()
    {
        return selectOptionsByShopId(requireShopId());
    }

    @Override
    public List<AttrTemplateOptionVO> selectOptionsByShopId(Long shopId)
    {
        if (shopId == null || shopId <= 0)
        {
            return Collections.emptyList();
        }
        BizGoodsAttrTemplate query = new BizGoodsAttrTemplate();
        query.setShopId(shopId);
        List<BizGoodsAttrTemplate> templates = templateMapper.selectList(query);
        if (templates.isEmpty())
        {
            return Collections.emptyList();
        }
        Map<Long, String> nameListMap = buildNameListMap(
                templates.stream().map(BizGoodsAttrTemplate::getTemplateId).collect(Collectors.toList()));
        List<AttrTemplateOptionVO> options = new ArrayList<>();
        for (BizGoodsAttrTemplate template : templates)
        {
            AttrTemplateOptionVO vo = new AttrTemplateOptionVO();
            vo.setTemplateId(template.getTemplateId());
            vo.setTemplateName(template.getTemplateName());
            vo.setNameList(nameListMap.getOrDefault(template.getTemplateId(), StringUtils.EMPTY));
            options.add(vo);
        }
        return options;
    }

    @Override
    public AttrTemplateDetailVO selectDetailForGoods(Long templateId, Long shopId)
    {
        if (shopId == null || shopId <= 0 || templateId == null)
        {
            throw new ServiceException(AttrTemplateConstants.MSG_TEMPLATE_NOT_FOUND);
        }
        return loadDetail(templateId, shopId);
    }

    private AttrTemplateDetailVO loadDetail(Long templateId, Long shopId)
    {
        BizGoodsAttrTemplate template = templateMapper.selectById(templateId, shopId);
        if (template == null)
        {
            throw new ServiceException(AttrTemplateConstants.MSG_TEMPLATE_NOT_FOUND);
        }
        List<BizGoodsAttrTemplateItem> items = itemMapper.selectByTemplateId(templateId);
        Map<Long, List<String>> valueMap = loadValueMap(items);
        AttrTemplateDetailVO vo = new AttrTemplateDetailVO();
        vo.setTemplateId(template.getTemplateId());
        vo.setTemplateName(template.getTemplateName());
        vo.setAttributes(toItemDtoList(items, AttrTemplateConstants.ITEM_TYPE_ATTR, valueMap));
        vo.setSpecs(toItemDtoList(items, AttrTemplateConstants.ITEM_TYPE_SPEC, valueMap));
        return vo;
    }

    private List<AttrTemplateItemDTO> toItemDtoList(List<BizGoodsAttrTemplateItem> items, String itemType,
            Map<Long, List<String>> valueMap)
    {
        List<AttrTemplateItemDTO> list = new ArrayList<>();
        for (BizGoodsAttrTemplateItem item : items)
        {
            if (!itemType.equals(item.getItemType()))
            {
                continue;
            }
            AttrTemplateItemDTO dto = new AttrTemplateItemDTO();
            dto.setItemName(item.getItemName());
            dto.setValues(new ArrayList<>(valueMap.getOrDefault(item.getItemId(), Collections.emptyList())));
            list.add(dto);
        }
        return list;
    }

    private Map<Long, List<String>> loadValueMap(List<BizGoodsAttrTemplateItem> items)
    {
        if (items.isEmpty())
        {
            return Collections.emptyMap();
        }
        List<Long> itemIds = items.stream().map(BizGoodsAttrTemplateItem::getItemId).collect(Collectors.toList());
        List<BizGoodsAttrTemplateValue> values = valueMapper.selectByItemIds(itemIds);
        Map<Long, List<String>> map = new LinkedHashMap<>();
        for (BizGoodsAttrTemplateValue value : values)
        {
            map.computeIfAbsent(value.getItemId(), k -> new ArrayList<>()).add(value.getValueText());
        }
        return map;
    }

    private Map<Long, String> buildNameListMap(List<Long> templateIds)
    {
        if (templateIds == null || templateIds.isEmpty())
        {
            return Collections.emptyMap();
        }
        List<BizGoodsAttrTemplateItem> items = itemMapper.selectByTemplateIds(templateIds);
        Map<Long, StringBuilder> builders = new LinkedHashMap<>();
        for (BizGoodsAttrTemplateItem item : items)
        {
            builders.computeIfAbsent(item.getTemplateId(), k -> new StringBuilder());
            StringBuilder sb = builders.get(item.getTemplateId());
            if (sb.length() > 0)
            {
                sb.append(',');
            }
            sb.append(item.getItemName());
        }
        Map<Long, String> result = new HashMap<>();
        builders.forEach((id, sb) -> result.put(id, sb.toString()));
        return result;
    }

    private void replaceItems(Long templateId, List<AttrTemplateItemDTO> attributes, List<AttrTemplateItemDTO> specs)
    {
        valueMapper.deleteByTemplateId(templateId);
        itemMapper.deleteByTemplateId(templateId);
        persistItems(templateId, attributes, specs);
    }

    private void persistItems(Long templateId, List<AttrTemplateItemDTO> attributes, List<AttrTemplateItemDTO> specs)
    {
        persistItemGroup(templateId, AttrTemplateConstants.ITEM_TYPE_ATTR, attributes);
        persistItemGroup(templateId, AttrTemplateConstants.ITEM_TYPE_SPEC, specs);
    }

    private void persistItemGroup(Long templateId, String itemType, List<AttrTemplateItemDTO> items)
    {
        if (items == null || items.isEmpty())
        {
            return;
        }
        int sortNo = 0;
        for (AttrTemplateItemDTO itemDto : items)
        {
            BizGoodsAttrTemplateItem item = new BizGoodsAttrTemplateItem();
            item.setTemplateId(templateId);
            item.setItemType(itemType);
            item.setItemName(itemDto.getItemName().trim());
            item.setSortNo(sortNo++);
            itemMapper.insert(item);
            persistValues(item.getItemId(), itemDto.getValues());
        }
    }

    private void persistValues(Long itemId, List<String> values)
    {
        int sortNo = 0;
        for (String value : values)
        {
            BizGoodsAttrTemplateValue row = new BizGoodsAttrTemplateValue();
            row.setItemId(itemId);
            row.setValueText(value.trim());
            row.setSortNo(sortNo++);
            valueMapper.insert(row);
        }
    }

    private void validateSaveDto(AttrTemplateSaveDTO dto)
    {
        if (dto == null)
        {
            throw new ServiceException(AttrTemplateConstants.MSG_TEMPLATE_NAME_REQUIRED);
        }
        if (StringUtils.isEmpty(dto.getTemplateName()))
        {
            throw new ServiceException(AttrTemplateConstants.MSG_TEMPLATE_NAME_REQUIRED);
        }
        List<AttrTemplateItemDTO> attributes = dto.getAttributes() == null ? Collections.emptyList() : dto.getAttributes();
        List<AttrTemplateItemDTO> specs = dto.getSpecs() == null ? Collections.emptyList() : dto.getSpecs();
        if (attributes.isEmpty() && specs.isEmpty())
        {
            throw new ServiceException(AttrTemplateConstants.MSG_NO_ITEM);
        }
        validateItemGroup(attributes);
        validateItemGroup(specs);
        Set<String> attrNames = collectItemNames(attributes);
        Set<String> specNames = collectItemNames(specs);
        for (String name : attrNames)
        {
            if (specNames.contains(name))
            {
                throw new ServiceException(AttrTemplateConstants.MSG_CROSS_ITEM_NAME);
            }
        }
    }

    private void validateItemGroup(List<AttrTemplateItemDTO> items)
    {
        Set<String> names = new HashSet<>();
        for (AttrTemplateItemDTO item : items)
        {
            if (item == null || StringUtils.isEmpty(item.getItemName()))
            {
                throw new ServiceException(AttrTemplateConstants.MSG_ITEM_NAME_REQUIRED);
            }
            String name = item.getItemName().trim();
            if (!names.add(name))
            {
                throw new ServiceException(AttrTemplateConstants.MSG_ITEM_NAME_DUPLICATE);
            }
            List<String> values = item.getValues();
            if (values == null || values.isEmpty())
            {
                throw new ServiceException(AttrTemplateConstants.MSG_VALUE_REQUIRED);
            }
            Set<String> valueSet = new HashSet<>();
            for (String value : values)
            {
                if (StringUtils.isEmpty(value))
                {
                    throw new ServiceException(AttrTemplateConstants.MSG_VALUE_REQUIRED);
                }
                String trimmed = value.trim();
                if (!valueSet.add(trimmed))
                {
                    throw new ServiceException(AttrTemplateConstants.MSG_VALUE_DUPLICATE);
                }
            }
        }
    }

    private Set<String> collectItemNames(List<AttrTemplateItemDTO> items)
    {
        Set<String> names = new HashSet<>();
        for (AttrTemplateItemDTO item : items)
        {
            if (item != null && StringUtils.isNotEmpty(item.getItemName()))
            {
                names.add(item.getItemName().trim());
            }
        }
        return names;
    }

    private void assertTemplateNameUnique(Long shopId, String templateName, Long excludeTemplateId)
    {
        if (templateMapper.countByName(shopId, templateName.trim(), excludeTemplateId) > 0)
        {
            throw new ServiceException(AttrTemplateConstants.MSG_TEMPLATE_NAME_DUPLICATE);
        }
    }

    private Long requireShopId()
    {
        Long shopId = SellerShopContext.getShopId();
        if (shopId == null || shopId <= 0)
        {
            throw new ServiceException(AttrTemplateConstants.MSG_SHOP_REQUIRED);
        }
        return shopId;
    }

    private void assertShopMaintable(Long shopId)
    {
        shopFacade.assertShopMaintable(shopId);
    }
}
