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

import java.math.BigDecimal;
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.freight.constant.FreightTemplateConstants;
import com.ruoyi.web.modules.freight.domain.BizShopFreightTemplate;
import com.ruoyi.web.modules.freight.domain.BizShopFreightTemplateRegion;
import com.ruoyi.web.modules.freight.dto.FreightTemplateRegionRuleDTO;
import com.ruoyi.web.modules.freight.dto.FreightTemplateRuleDTO;
import com.ruoyi.web.modules.freight.dto.FreightTemplateSaveDTO;
import com.ruoyi.web.modules.freight.exception.FreightTemplateBatchDeleteException;
import com.ruoyi.web.modules.freight.mapper.BizShopFreightTemplateMapper;
import com.ruoyi.web.modules.freight.mapper.BizShopFreightTemplateRegionMapper;
import com.ruoyi.web.modules.freight.service.IFreightTemplateService;
import com.ruoyi.web.modules.freight.vo.FreightTemplateDetailVO;
import com.ruoyi.web.modules.freight.vo.FreightTemplateListVO;
import com.ruoyi.web.modules.freight.vo.FreightTemplateOptionVO;
import com.ruoyi.web.modules.freight.vo.FreightTemplateRegionRuleVO;
import com.ruoyi.web.modules.region.domain.BizRegion;
import com.ruoyi.web.modules.region.mapper.BizRegionMapper;

/**
 * 运费模版业务实现
 */
@Service
public class FreightTemplateServiceImpl implements IFreightTemplateService
{
    @Autowired
    private BizShopFreightTemplateMapper templateMapper;

    @Autowired
    private BizShopFreightTemplateRegionMapper templateRegionMapper;

    @Autowired
    private BizRegionMapper bizRegionMapper;

    @Autowired
    private IGoodsFacade goodsFacade;

    @Autowired
    private IShopFacade shopFacade;

    @Override
    public List<FreightTemplateListVO> selectTemplateList(String keyword)
    {
        Long shopId = requireShopId();
        assertShopMaintable(shopId);
        BizShopFreightTemplate query = new BizShopFreightTemplate();
        query.setShopId(shopId);
        query.setKeyword(keyword);
        List<BizShopFreightTemplate> templates = templateMapper.selectList(query);
        List<FreightTemplateListVO> rows = new ArrayList<>();
        for (BizShopFreightTemplate template : templates)
        {
            rows.add(toListVO(template));
        }
        return rows;
    }

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

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertTemplate(FreightTemplateSaveDTO dto, String operator)
    {
        Long shopId = requireShopId();
        assertShopMaintable(shopId);
        validateSaveDto(dto, false);
        assertTemplateNameUnique(shopId, dto.getTemplateName(), null);
        String isDefault = normalizeIsDefault(dto.getIsDefault());
        if (FreightTemplateConstants.DEFAULT_YES.equals(isDefault))
        {
            templateMapper.clearDefaultByShopId(shopId, null);
        }
        BizShopFreightTemplate template = buildTemplateEntity(dto, shopId, isDefault, operator, true);
        templateMapper.insert(template);
        persistRegions(template.getTemplateId(), dto);
        return 1;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateTemplate(FreightTemplateSaveDTO dto, String operator)
    {
        Long shopId = requireShopId();
        assertShopMaintable(shopId);
        if (dto.getTemplateId() == null)
        {
            throw new ServiceException(FreightTemplateConstants.MSG_TEMPLATE_NOT_FOUND);
        }
        BizShopFreightTemplate existing = templateMapper.selectById(dto.getTemplateId(), shopId);
        if (existing == null)
        {
            throw new ServiceException(FreightTemplateConstants.MSG_TEMPLATE_NOT_FOUND);
        }
        validateSaveDto(dto, true);
        assertTemplateNameUnique(shopId, dto.getTemplateName(), dto.getTemplateId());
        String isDefault = normalizeIsDefault(dto.getIsDefault());
        if (FreightTemplateConstants.DEFAULT_YES.equals(isDefault))
        {
            templateMapper.clearDefaultByShopId(shopId, dto.getTemplateId());
        }
        BizShopFreightTemplate template = buildTemplateEntity(dto, shopId, isDefault, operator, false);
        template.setTemplateId(dto.getTemplateId());
        templateMapper.update(template);
        replaceRegions(dto.getTemplateId(), dto);
        return 1;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> setDefaultTemplate(Long templateId, String operator)
    {
        Long shopId = requireShopId();
        assertShopMaintable(shopId);
        BizShopFreightTemplate existing = templateMapper.selectById(templateId, shopId);
        if (existing == null)
        {
            throw new ServiceException(FreightTemplateConstants.MSG_TEMPLATE_NOT_FOUND);
        }
        templateMapper.clearDefaultByShopId(shopId, templateId);
        templateMapper.updateIsDefault(templateId, shopId, FreightTemplateConstants.DEFAULT_YES, operator);
        Map<String, Object> result = new HashMap<>();
        result.put("templateId", templateId);
        result.put("isDefault", FreightTemplateConstants.DEFAULT_YES);
        return result;
    }

    @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(FreightTemplateConstants.MSG_DELETE_IDS_REQUIRED);
        }
        List<String> reasons = new ArrayList<>();
        List<Long> validIds = new ArrayList<>();
        for (Long templateId : templateIds)
        {
            BizShopFreightTemplate template = templateMapper.selectById(templateId, shopId);
            if (template == null)
            {
                continue;
            }
            if (goodsFacade.existsByFreightTemplateId(templateId))
            {
                reasons.add("运费模版「" + template.getTemplateName() + "」已被商品使用，无法删除");
            }
            else
            {
                validIds.add(templateId);
            }
        }
        if (!reasons.isEmpty())
        {
            throw new FreightTemplateBatchDeleteException(FreightTemplateConstants.MSG_BATCH_DELETE, reasons);
        }
        if (validIds.isEmpty())
        {
            throw new ServiceException(FreightTemplateConstants.MSG_TEMPLATE_NOT_FOUND);
        }
        for (Long templateId : validIds)
        {
            templateRegionMapper.deleteByTemplateId(templateId);
        }
        templateMapper.logicDeleteByIds(validIds, shopId, operator);
    }

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

    @Override
    public List<FreightTemplateOptionVO> selectOptionsByShopId(Long shopId)
    {
        if (shopId == null || shopId <= 0)
        {
            return Collections.emptyList();
        }
        BizShopFreightTemplate query = new BizShopFreightTemplate();
        query.setShopId(shopId);
        List<BizShopFreightTemplate> templates = templateMapper.selectList(query);
        List<FreightTemplateOptionVO> options = new ArrayList<>();
        for (BizShopFreightTemplate template : templates)
        {
            options.add(toOptionVO(template));
        }
        return options;
    }

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

    @Override
    public FreightTemplateOptionVO getDefaultTemplate(Long shopId)
    {
        if (shopId == null || shopId <= 0)
        {
            return null;
        }
        BizShopFreightTemplate template = templateMapper.selectDefaultByShopId(shopId);
        return template == null ? null : toOptionVO(template);
    }

    private FreightTemplateDetailVO loadDetail(Long templateId, Long shopId)
    {
        BizShopFreightTemplate template = templateMapper.selectById(templateId, shopId);
        if (template == null)
        {
            throw new ServiceException(FreightTemplateConstants.MSG_TEMPLATE_NOT_FOUND);
        }
        FreightTemplateDetailVO vo = new FreightTemplateDetailVO();
        vo.setTemplateId(template.getTemplateId());
        vo.setTemplateName(template.getTemplateName());
        vo.setShipTime(template.getShipTime());
        vo.setIsDefault(template.getIsDefault());
        vo.setFreeFlag(template.getFreeFlag());
        if (FreightTemplateConstants.FREE_FLAG_YES.equals(template.getFreeFlag()))
        {
            return vo;
        }
        vo.setPricingType(template.getPricingType());
        vo.setDefaultRule(toRuleDto(template));
        List<BizShopFreightTemplateRegion> regions = templateRegionMapper.selectByTemplateId(templateId);
        vo.setRegionRules(toRegionRuleVoList(regions));
        return vo;
    }

    private List<FreightTemplateRegionRuleVO> toRegionRuleVoList(List<BizShopFreightTemplateRegion> regions)
    {
        if (regions == null || regions.isEmpty())
        {
            return Collections.emptyList();
        }
        List<FreightTemplateRegionRuleVO> list = new ArrayList<>();
        for (BizShopFreightTemplateRegion region : regions)
        {
            FreightTemplateRegionRuleVO vo = new FreightTemplateRegionRuleVO();
            vo.setProvinceIds(parseProvinceIds(region.getProvinceIds()));
            vo.setProvinceNames(region.getProvinceNames());
            vo.setFirstUnit(region.getFirstUnit());
            vo.setFirstFee(region.getFirstFee());
            vo.setAddUnit(region.getAddUnit());
            vo.setAddFee(region.getAddFee());
            vo.setFreeThreshold(region.getFreeThreshold());
            list.add(vo);
        }
        return list;
    }

    private List<Integer> parseProvinceIds(String provinceIds)
    {
        if (StringUtils.isEmpty(provinceIds))
        {
            return Collections.emptyList();
        }
        List<Integer> ids = new ArrayList<>();
        for (String part : provinceIds.split(","))
        {
            if (StringUtils.isNotEmpty(part.trim()))
            {
                ids.add(Integer.valueOf(part.trim()));
            }
        }
        return ids;
    }

    private FreightTemplateRuleDTO toRuleDto(BizShopFreightTemplate template)
    {
        FreightTemplateRuleDTO rule = new FreightTemplateRuleDTO();
        rule.setFirstUnit(template.getFirstUnit());
        rule.setFirstFee(template.getFirstFee());
        rule.setAddUnit(template.getAddUnit());
        rule.setAddFee(template.getAddFee());
        rule.setFreeThreshold(template.getFreeThreshold());
        return rule;
    }

    private FreightTemplateListVO toListVO(BizShopFreightTemplate template)
    {
        FreightTemplateListVO vo = new FreightTemplateListVO();
        vo.setTemplateId(template.getTemplateId());
        vo.setTemplateName(template.getTemplateName());
        vo.setFreeFlag(template.getFreeFlag());
        vo.setFreeFlagLabel(FreightTemplateConstants.FREE_FLAG_YES.equals(template.getFreeFlag()) ? "包邮" : "不包邮");
        vo.setPricingType(template.getPricingType());
        vo.setPricingTypeLabel(resolvePricingTypeLabel(template.getFreeFlag(), template.getPricingType()));
        vo.setShipTime(template.getShipTime());
        vo.setIsDefault(template.getIsDefault());
        return vo;
    }

    private FreightTemplateOptionVO toOptionVO(BizShopFreightTemplate template)
    {
        FreightTemplateOptionVO vo = new FreightTemplateOptionVO();
        vo.setTemplateId(template.getTemplateId());
        vo.setTemplateName(template.getTemplateName());
        vo.setIsDefault(template.getIsDefault());
        vo.setFreeFlag(template.getFreeFlag());
        return vo;
    }

    private String resolvePricingTypeLabel(String freeFlag, String pricingType)
    {
        if (FreightTemplateConstants.FREE_FLAG_YES.equals(freeFlag))
        {
            return "—";
        }
        if (FreightTemplateConstants.PRICING_TYPE_PIECE.equals(pricingType))
        {
            return "按件数";
        }
        if (FreightTemplateConstants.PRICING_TYPE_WEIGHT.equals(pricingType))
        {
            return "按重量";
        }
        return "—";
    }

    private BizShopFreightTemplate buildTemplateEntity(FreightTemplateSaveDTO dto, Long shopId, String isDefault,
            String operator, boolean insert)
    {
        BizShopFreightTemplate template = new BizShopFreightTemplate();
        template.setShopId(shopId);
        template.setTemplateName(dto.getTemplateName().trim());
        template.setShipTime(dto.getShipTime().trim());
        template.setFreeFlag(dto.getFreeFlag());
        template.setIsDefault(isDefault);
        if (FreightTemplateConstants.FREE_FLAG_YES.equals(dto.getFreeFlag()))
        {
            template.setPricingType(null);
            template.setFirstUnit(null);
            template.setFirstFee(null);
            template.setAddUnit(null);
            template.setAddFee(null);
            template.setFreeThreshold(null);
        }
        else
        {
            template.setPricingType(dto.getPricingType());
            applyRuleToTemplate(template, dto.getDefaultRule());
        }
        if (insert)
        {
            template.setDelFlag(FreightTemplateConstants.DEL_FLAG_NORMAL);
            template.setCreateBy(operator);
        }
        else
        {
            template.setUpdateBy(operator);
        }
        return template;
    }

    private void applyRuleToTemplate(BizShopFreightTemplate template, FreightTemplateRuleDTO rule)
    {
        if (rule == null)
        {
            return;
        }
        template.setFirstUnit(rule.getFirstUnit());
        template.setFirstFee(rule.getFirstFee());
        template.setAddUnit(rule.getAddUnit());
        template.setAddFee(rule.getAddFee());
        template.setFreeThreshold(rule.getFreeThreshold());
    }

    private void replaceRegions(Long templateId, FreightTemplateSaveDTO dto)
    {
        templateRegionMapper.deleteByTemplateId(templateId);
        persistRegions(templateId, dto);
    }

    private void persistRegions(Long templateId, FreightTemplateSaveDTO dto)
    {
        if (FreightTemplateConstants.FREE_FLAG_YES.equals(dto.getFreeFlag()))
        {
            return;
        }
        List<FreightTemplateRegionRuleDTO> regionRules = dto.getRegionRules();
        if (regionRules == null || regionRules.isEmpty())
        {
            return;
        }
        int sortNo = 0;
        for (FreightTemplateRegionRuleDTO ruleDto : regionRules)
        {
            BizShopFreightTemplateRegion region = new BizShopFreightTemplateRegion();
            region.setTemplateId(templateId);
            region.setProvinceIds(joinProvinceIds(ruleDto.getProvinceIds()));
            region.setProvinceNames(buildProvinceNames(ruleDto.getProvinceIds()));
            region.setFirstUnit(ruleDto.getFirstUnit());
            region.setFirstFee(ruleDto.getFirstFee());
            region.setAddUnit(ruleDto.getAddUnit());
            region.setAddFee(ruleDto.getAddFee());
            region.setFreeThreshold(ruleDto.getFreeThreshold());
            region.setSortNo(sortNo++);
            templateRegionMapper.insert(region);
        }
    }

    private String joinProvinceIds(List<Integer> provinceIds)
    {
        return provinceIds.stream().map(String::valueOf).collect(Collectors.joining(","));
    }

    private String buildProvinceNames(List<Integer> provinceIds)
    {
        Map<Integer, String> nameMap = loadProvinceNameMap(provinceIds);
        List<String> names = new ArrayList<>();
        for (Integer id : provinceIds)
        {
            names.add(nameMap.get(id));
        }
        return String.join(",", names);
    }

    private Map<Integer, String> loadProvinceNameMap(List<Integer> provinceIds)
    {
        List<BizRegion> regions = bizRegionMapper.selectByIds(provinceIds);
        Map<Integer, String> map = new LinkedHashMap<>();
        for (BizRegion region : regions)
        {
            map.put(region.getId(), region.getName());
        }
        return map;
    }

    private void validateSaveDto(FreightTemplateSaveDTO dto, boolean update)
    {
        if (dto == null)
        {
            throw new ServiceException(FreightTemplateConstants.MSG_TEMPLATE_NAME_REQUIRED);
        }
        if (update && dto.getTemplateId() == null)
        {
            throw new ServiceException(FreightTemplateConstants.MSG_TEMPLATE_NOT_FOUND);
        }
        if (StringUtils.isEmpty(dto.getTemplateName()))
        {
            throw new ServiceException(FreightTemplateConstants.MSG_TEMPLATE_NAME_REQUIRED);
        }
        if (StringUtils.isEmpty(dto.getShipTime()))
        {
            throw new ServiceException(FreightTemplateConstants.MSG_SHIP_TIME_REQUIRED);
        }
        if (StringUtils.isEmpty(dto.getFreeFlag()))
        {
            throw new ServiceException(FreightTemplateConstants.MSG_FREE_FLAG_REQUIRED);
        }
        if (FreightTemplateConstants.FREE_FLAG_YES.equals(dto.getFreeFlag()))
        {
            return;
        }
        if (StringUtils.isEmpty(dto.getPricingType()))
        {
            throw new ServiceException(FreightTemplateConstants.MSG_PRICING_TYPE_REQUIRED);
        }
        validateRule(dto.getDefaultRule(), dto.getPricingType(), true);
        validateRegionRules(dto.getRegionRules(), dto.getPricingType());
    }

    private void validateRule(FreightTemplateRuleDTO rule, String pricingType, boolean required)
    {
        if (rule == null)
        {
            if (required)
            {
                throw new ServiceException(FreightTemplateConstants.MSG_RULE_REQUIRED);
            }
            return;
        }
        String unitMsg = FreightTemplateConstants.PRICING_TYPE_WEIGHT.equals(pricingType)
                ? FreightTemplateConstants.MSG_WEIGHT_INVALID
                : FreightTemplateConstants.MSG_UNIT_INVALID;
        if (rule.getFirstUnit() == null || rule.getFirstUnit().compareTo(BigDecimal.ZERO) <= 0
                || rule.getAddUnit() == null || rule.getAddUnit().compareTo(BigDecimal.ZERO) <= 0)
        {
            throw new ServiceException(unitMsg);
        }
        if (rule.getFirstFee() == null || rule.getFirstFee().compareTo(BigDecimal.ZERO) < 0
                || rule.getAddFee() == null || rule.getAddFee().compareTo(BigDecimal.ZERO) < 0)
        {
            throw new ServiceException(FreightTemplateConstants.MSG_FEE_NEGATIVE);
        }
        if (rule.getFreeThreshold() != null && rule.getFreeThreshold().compareTo(BigDecimal.ZERO) <= 0)
        {
            throw new ServiceException(FreightTemplateConstants.MSG_THRESHOLD_INVALID);
        }
    }

    private void validateRegionRules(List<FreightTemplateRegionRuleDTO> regionRules, String pricingType)
    {
        if (regionRules == null || regionRules.isEmpty())
        {
            return;
        }
        Set<Integer> usedProvinceIds = new HashSet<>();
        for (FreightTemplateRegionRuleDTO rule : regionRules)
        {
            if (rule == null || rule.getProvinceIds() == null || rule.getProvinceIds().isEmpty())
            {
                throw new ServiceException(FreightTemplateConstants.MSG_REGION_PROVINCE_REQUIRED);
            }
            validateProvinces(rule.getProvinceIds());
            for (Integer provinceId : rule.getProvinceIds())
            {
                if (!usedProvinceIds.add(provinceId))
                {
                    throw new ServiceException(FreightTemplateConstants.MSG_REGION_OVERLAP);
                }
            }
            validateRule(rule, pricingType, true);
        }
    }

    private void validateProvinces(List<Integer> provinceIds)
    {
        List<BizRegion> regions = bizRegionMapper.selectByIds(provinceIds);
        if (regions.size() != provinceIds.size())
        {
            throw new ServiceException(FreightTemplateConstants.MSG_REGION_NOT_PROVINCE);
        }
        for (BizRegion region : regions)
        {
            if (region.getType() == null
                    || region.getType().intValue() != FreightTemplateConstants.REGION_TYPE_PROVINCE)
            {
                throw new ServiceException(FreightTemplateConstants.MSG_REGION_NOT_PROVINCE);
            }
        }
    }

    private String normalizeIsDefault(String isDefault)
    {
        return FreightTemplateConstants.DEFAULT_YES.equals(isDefault)
                ? FreightTemplateConstants.DEFAULT_YES
                : FreightTemplateConstants.DEFAULT_NO;
    }

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

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

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