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

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
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.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.web.modules.account.facade.IMemberFacade;
import com.ruoyi.web.modules.cart.constant.CartConstants;
import com.ruoyi.web.modules.cart.domain.BizMemberCartItem;
import com.ruoyi.web.modules.cart.dto.CartCheckoutPrepareDTO;
import com.ruoyi.web.modules.cart.dto.CartItemAddDTO;
import com.ruoyi.web.modules.cart.dto.CartItemCheckedBatchDTO;
import com.ruoyi.web.modules.cart.dto.CartItemCheckedItemDTO;
import com.ruoyi.web.modules.cart.dto.CartItemQuantityDTO;
import com.ruoyi.web.modules.cart.dto.CartItemRowDTO;
import com.ruoyi.web.modules.cart.mapper.CartAppMapper;
import com.ruoyi.web.modules.cart.service.ICartAppService;
import com.ruoyi.web.modules.cart.support.CartInvalidSupport;
import com.ruoyi.web.modules.cart.vo.CartCheckedSummaryVO;
import com.ruoyi.web.modules.cart.vo.CartCheckoutItemVO;
import com.ruoyi.web.modules.cart.vo.CartCheckoutPrepareVO;
import com.ruoyi.web.modules.cart.vo.CartCleanInvalidResultVO;
import com.ruoyi.web.modules.cart.vo.CartItemAddResultVO;
import com.ruoyi.web.modules.cart.vo.CartItemVO;
import com.ruoyi.web.modules.cart.vo.CartItemVO;
import com.ruoyi.web.modules.cart.vo.CartListVO;
import com.ruoyi.web.modules.category.facade.ICategoryFacade;
import com.ruoyi.web.modules.goods.domain.BizGoods;
import com.ruoyi.web.modules.goods.domain.BizGoodsAttr;
import com.ruoyi.web.modules.goods.facade.IGoodsPurchaseFacade;
import com.ruoyi.web.modules.goods.mapper.BizGoodsAttrMapper;
import com.ruoyi.web.modules.goods.mapper.BizGoodsMapper;
import com.ruoyi.web.modules.goods.vo.GoodsPurchaseVO;
import com.ruoyi.web.modules.template.constant.AttrTemplateConstants;

/**
 * C 端购物车 Service 实现
 */
@Service
public class CartAppServiceImpl implements ICartAppService
{
    @Autowired
    private CartAppMapper cartAppMapper;

    @Autowired
    private BizGoodsMapper goodsMapper;

    @Autowired
    private BizGoodsAttrMapper goodsAttrMapper;

    @Autowired
    private IGoodsPurchaseFacade purchaseFacade;

    @Autowired
    private ICategoryFacade categoryFacade;

    @Autowired
    private IMemberFacade memberFacade;

    @Override
    public CartListVO list(Long memberId)
    {
        assertMemberEnabled(memberId);
        List<CartItemRowDTO> rows = cartAppMapper.selectItemRowsByMemberId(memberId);
        if (rows == null || rows.isEmpty())
        {
            CartListVO empty = new CartListVO();
            empty.setItems(Collections.emptyList());
            empty.setCheckedSummary(emptySummary());
            return empty;
        }
        List<CartItemVO> items = new ArrayList<>();
        for (CartItemRowDTO row : rows)
        {
            items.add(toCartItemVO(row));
        }
        CartListVO vo = new CartListVO();
        vo.setItems(items);
        vo.setCheckedSummary(buildCheckedSummary(items));
        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CartItemAddResultVO addItem(Long memberId, CartItemAddDTO dto)
    {
        assertMemberEnabled(memberId);
        if (dto == null || dto.getGoodsId() == null)
        {
            throw new ServiceException(CartConstants.MSG_GOODS_NOT_FOUND);
        }
        int quantity = dto.getQuantity() != null ? dto.getQuantity() : 1;
        if (quantity < 1)
        {
            throw new ServiceException(CartConstants.MSG_QUANTITY_MIN);
        }
        GoodsPurchaseVO purchase = purchaseFacade.canPurchase(dto.getGoodsId());
        if (!purchase.isAllowed())
        {
            throw new ServiceException(purchase.getReason());
        }
        BizGoods goods = goodsMapper.selectById(dto.getGoodsId());
        if (goods == null)
        {
            throw new ServiceException(CartConstants.MSG_GOODS_NOT_FOUND);
        }
        String specKey = normalizeSpecKey(dto.getSpecKey());
        BizMemberCartItem existing = cartAppMapper.selectByMemberGoodsSpec(memberId, dto.getGoodsId(), specKey);
        if (existing != null)
        {
            int newQty = existing.getQuantity() + quantity;
            if (newQty > goods.getStock())
            {
                throw new ServiceException(CartConstants.MSG_STOCK_INSUFFICIENT);
            }
            cartAppMapper.updateQuantity(existing.getCartItemId(), memberId, newQty);
            return new CartItemAddResultVO(existing.getCartItemId(), newQty);
        }
        if (quantity > goods.getStock())
        {
            throw new ServiceException(CartConstants.MSG_STOCK_INSUFFICIENT);
        }
        BizMemberCartItem item = new BizMemberCartItem();
        item.setMemberId(memberId);
        item.setShopId(goods.getShopId());
        item.setGoodsId(dto.getGoodsId());
        item.setSpecKey(specKey);
        item.setSpecText(resolveSpecText(dto.getSpecText(), dto.getGoodsId()));
        item.setQuantity(quantity);
        item.setChecked(CartConstants.CHECKED_YES);
        Date now = DateUtils.getNowDate();
        item.setCreateTime(now);
        item.setUpdateTime(now);
        cartAppMapper.insert(item);
        return new CartItemAddResultVO(item.getCartItemId(), quantity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CartItemAddResultVO updateQuantity(Long memberId, Long cartItemId, CartItemQuantityDTO dto)
    {
        assertMemberEnabled(memberId);
        if (dto == null || dto.getQuantity() == null || dto.getQuantity() < 1)
        {
            throw new ServiceException(CartConstants.MSG_QUANTITY_MIN);
        }
        BizMemberCartItem item = requireCartItem(memberId, cartItemId);
        BizGoods goods = goodsMapper.selectById(item.getGoodsId());
        if (goods == null)
        {
            throw new ServiceException(CartConstants.MSG_GOODS_NOT_FOUND);
        }
        if (dto.getQuantity() > goods.getStock())
        {
            throw new ServiceException(CartConstants.MSG_STOCK_INSUFFICIENT);
        }
        cartAppMapper.updateQuantity(cartItemId, memberId, dto.getQuantity());
        return new CartItemAddResultVO(cartItemId, dto.getQuantity());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateChecked(Long memberId, CartItemCheckedBatchDTO dto)
    {
        assertMemberEnabled(memberId);
        if (dto == null || dto.getItems() == null || dto.getItems().isEmpty())
        {
            return;
        }
        for (CartItemCheckedItemDTO entry : dto.getItems())
        {
            if (entry.getCartItemId() == null || entry.getChecked() == null)
            {
                continue;
            }
            requireCartItem(memberId, entry.getCartItemId());
            String checked = Boolean.TRUE.equals(entry.getChecked()) ? CartConstants.CHECKED_YES
                    : CartConstants.CHECKED_NO;
            cartAppMapper.updateChecked(entry.getCartItemId(), memberId, checked);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeItem(Long memberId, Long cartItemId)
    {
        assertMemberEnabled(memberId);
        requireCartItem(memberId, cartItemId);
        cartAppMapper.deleteByIdAndMember(cartItemId, memberId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeItems(Long memberId, List<Long> cartItemIds)
    {
        assertMemberEnabled(memberId);
        if (cartItemIds == null || cartItemIds.isEmpty())
        {
            return;
        }
        cartAppMapper.deleteByIdsAndMember(memberId, cartItemIds);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CartCleanInvalidResultVO cleanInvalid(Long memberId)
    {
        assertMemberEnabled(memberId);
        List<CartItemRowDTO> rows = cartAppMapper.selectItemRowsByMemberId(memberId);
        if (rows == null || rows.isEmpty())
        {
            return new CartCleanInvalidResultVO(0);
        }
        List<Long> invalidIds = new ArrayList<>();
        for (CartItemRowDTO row : rows)
        {
            String invalidType = CartInvalidSupport.resolveInvalidType(row, categoryFacade);
            if (!CartInvalidSupport.isPurchasable(invalidType))
            {
                invalidIds.add(row.getCartItemId());
            }
        }
        if (invalidIds.isEmpty())
        {
            return new CartCleanInvalidResultVO(0);
        }
        cartAppMapper.deleteByIdsAndMember(memberId, invalidIds);
        return new CartCleanInvalidResultVO(invalidIds.size());
    }

    @Override
    public CartCheckoutPrepareVO prepareCheckout(Long memberId, CartCheckoutPrepareDTO dto)
    {
        assertMemberEnabled(memberId);
        if (dto == null || dto.getCartItemIds() == null || dto.getCartItemIds().isEmpty())
        {
            throw new ServiceException(CartConstants.MSG_CHECKOUT_EMPTY);
        }
        List<CartItemRowDTO> rows = cartAppMapper.selectItemRowsByIdsAndMember(memberId, dto.getCartItemIds());
        if (rows == null || rows.size() != dto.getCartItemIds().size())
        {
            throw new ServiceException(CartConstants.MSG_ITEM_NOT_FOUND);
        }
        rows.sort(Comparator.comparingInt(row -> dto.getCartItemIds().indexOf(row.getCartItemId())));
        for (CartItemRowDTO row : rows)
        {
            if (!CartConstants.CHECKED_YES.equals(row.getChecked()))
            {
                throw new ServiceException(CartConstants.MSG_CHECKOUT_NOT_CHECKED);
            }
        }
        Long shopId = rows.get(0).getShopId();
        String shopName = rows.get(0).getShopName();
        for (CartItemRowDTO row : rows)
        {
            if (!shopId.equals(row.getShopId()))
            {
                throw new ServiceException(CartConstants.MSG_CROSS_SHOP);
            }
        }
        BigDecimal goodsAmount = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
        List<CartCheckoutItemVO> items = new ArrayList<>();
        for (CartItemRowDTO row : rows)
        {
            String invalidType = CartInvalidSupport.resolveInvalidType(row, categoryFacade);
            if (!CartInvalidSupport.isPurchasable(invalidType))
            {
                String msg = CartInvalidSupport.resolveInvalidMsg(invalidType, null);
                throw new ServiceException(msg);
            }
            GoodsPurchaseVO purchase = purchaseFacade.canPurchase(row.getGoodsId());
            if (purchase == null || !purchase.isAllowed())
            {
                throw new ServiceException(purchase != null ? purchase.getReason() : CartConstants.MSG_INVALID_OFF_SHELF);
            }
            CartCheckoutItemVO item = new CartCheckoutItemVO();
            item.setCartItemId(row.getCartItemId());
            item.setGoodsId(row.getGoodsId());
            item.setGoodsName(row.getGoodsName());
            item.setMainPic(row.getMainPic());
            item.setSpecText(row.getSpecText());
            item.setSalePrice(row.getSalePrice());
            item.setQuantity(row.getQuantity());
            BigDecimal subtotal = calcSubtotal(row.getSalePrice(), row.getQuantity());
            item.setSubtotal(subtotal);
            goodsAmount = goodsAmount.add(subtotal);
            items.add(item);
        }
        CartCheckoutPrepareVO vo = new CartCheckoutPrepareVO();
        vo.setShopId(shopId);
        vo.setShopName(shopName);
        vo.setItems(items);
        vo.setGoodsAmount(goodsAmount);
        return vo;
    }

    private CartItemVO toCartItemVO(CartItemRowDTO row)
    {
        String invalidType = CartInvalidSupport.resolveInvalidType(row, categoryFacade);
        boolean purchasable = CartInvalidSupport.isPurchasable(invalidType);
        CartItemVO vo = new CartItemVO();
        vo.setCartItemId(row.getCartItemId());
        vo.setGoodsId(row.getGoodsId());
        vo.setGoodsName(row.getGoodsName());
        vo.setMainPic(row.getMainPic());
        vo.setSpecText(row.getSpecText());
        vo.setSalePrice(row.getSalePrice());
        vo.setQuantity(row.getQuantity());
        vo.setSubtotal(calcSubtotal(row.getSalePrice(), row.getQuantity()));
        vo.setChecked(CartConstants.CHECKED_YES.equals(row.getChecked()));
        vo.setPurchasable(purchasable);
        vo.setInvalidType(invalidType);
        vo.setInvalidMsg(CartInvalidSupport.resolveInvalidMsg(invalidType, null));
        vo.setShopId(row.getShopId());
        vo.setShopName(StringUtils.isNotEmpty(row.getShopName()) ? row.getShopName() : "");
        vo.setShopAvatar(row.getShopAvatar());
        vo.setShopStatus(row.getShopStatus());
        return vo;
    }

    private CartCheckedSummaryVO buildCheckedSummary(List<CartItemVO> items)
    {
        CartCheckedSummaryVO summary = emptySummary();
        int count = 0;
        int qty = 0;
        BigDecimal amount = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
        for (CartItemVO item : items)
        {
            if (Boolean.TRUE.equals(item.getChecked()) && Boolean.TRUE.equals(item.getPurchasable()))
            {
                count++;
                qty += item.getQuantity() != null ? item.getQuantity() : 0;
                if (item.getSubtotal() != null)
                {
                    amount = amount.add(item.getSubtotal());
                }
            }
        }
        summary.setCheckedCount(count);
        summary.setCheckedQuantity(qty);
        summary.setCheckedAmount(amount);
        return summary;
    }

    private CartCheckedSummaryVO emptySummary()
    {
        CartCheckedSummaryVO summary = new CartCheckedSummaryVO();
        summary.setCheckedCount(0);
        summary.setCheckedQuantity(0);
        summary.setCheckedAmount(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
        return summary;
    }

    private BigDecimal calcSubtotal(BigDecimal price, Integer quantity)
    {
        if (price == null || quantity == null)
        {
            return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
        }
        return price.multiply(BigDecimal.valueOf(quantity.longValue())).setScale(2, RoundingMode.HALF_UP);
    }

    private BizMemberCartItem requireCartItem(Long memberId, Long cartItemId)
    {
        BizMemberCartItem item = cartAppMapper.selectByIdAndMember(cartItemId, memberId);
        if (item == null)
        {
            throw new ServiceException(CartConstants.MSG_ITEM_NOT_FOUND);
        }
        return item;
    }

    private void assertMemberEnabled(Long memberId)
    {
        if (!memberFacade.isMemberEnabled(memberId))
        {
            throw new ServiceException(CartConstants.MSG_MEMBER_DISABLED, 403);
        }
    }

    private String normalizeSpecKey(String specKey)
    {
        return specKey != null ? specKey : CartConstants.DEFAULT_SPEC_KEY;
    }

    private String resolveSpecText(String specText, Long goodsId)
    {
        if (StringUtils.isNotEmpty(specText))
        {
            return specText;
        }
        List<BizGoodsAttr> attrs = goodsAttrMapper.selectByGoodsId(goodsId);
        if (attrs == null || attrs.isEmpty())
        {
            return CartConstants.DEFAULT_SPEC_TEXT;
        }
        StringBuilder sb = new StringBuilder();
        for (BizGoodsAttr attr : attrs)
        {
            if (!AttrTemplateConstants.ITEM_TYPE_SPEC.equals(attr.getAttrType()))
            {
                continue;
            }
            if (sb.length() > 0)
            {
                sb.append("；");
            }
            sb.append(attr.getItemName()).append(":").append(attr.getValueText());
        }
        return sb.length() > 0 ? sb.toString() : CartConstants.DEFAULT_SPEC_TEXT;
    }
}
