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

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
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.goods.domain.BizGoods;
import com.ruoyi.web.modules.goods.mapper.BizGoodsMapper;
import com.ruoyi.web.modules.stock.constant.StockConstants;
import com.ruoyi.web.modules.stock.domain.BizStockInbound;
import com.ruoyi.web.modules.stock.domain.BizStockInboundItem;
import com.ruoyi.web.modules.stock.domain.BizStockLog;
import com.ruoyi.web.modules.stock.dto.StockInboundCreateDTO;
import com.ruoyi.web.modules.stock.dto.StockInboundCreateItemDTO;
import com.ruoyi.web.modules.stock.dto.StockInboundQuery;
import com.ruoyi.web.modules.stock.mapper.BizStockInboundItemMapper;
import com.ruoyi.web.modules.stock.mapper.BizStockInboundMapper;
import com.ruoyi.web.modules.stock.service.IStockInboundService;
import com.ruoyi.web.modules.stock.service.IStockLogService;
import com.ruoyi.web.modules.stock.support.StockSupport;
import com.ruoyi.web.modules.stock.vo.StockGoodsPickVO;
import com.ruoyi.web.modules.stock.vo.StockInboundDetailVO;
import com.ruoyi.web.modules.stock.vo.StockInboundItemVO;
import com.ruoyi.web.modules.stock.vo.StockInboundListRowVO;
import com.ruoyi.web.modules.stock.vo.StockOperatorVO;

@Service
public class StockInboundServiceImpl implements IStockInboundService
{
    @Autowired
    private BizStockInboundMapper inboundMapper;

    @Autowired
    private BizStockInboundItemMapper inboundItemMapper;

    @Autowired
    private BizGoodsMapper goodsMapper;

    @Autowired
    private IStockLogService stockLogService;

    @Autowired
    private StockSupport stockSupport;

    @Override
    public List<StockInboundListRowVO> selectList(Long shopId, StockInboundQuery query)
    {
        List<StockInboundListRowVO> rows = inboundMapper.selectList(shopId, query);
        for (StockInboundListRowVO row : rows)
        {
            row.setInboundTypeText(stockSupport.inboundTypeText(row.getInboundType()));
        }
        return rows;
    }

    @Override
    public StockInboundDetailVO getDetail(Long shopId, Long inboundId)
    {
        StockInboundDetailVO detail = inboundMapper.selectDetail(shopId, inboundId);
        if (detail == null)
        {
            throw new ServiceException(StockConstants.MSG_INBOUND_NOT_FOUND);
        }
        detail.setInboundTypeText(stockSupport.inboundTypeText(detail.getInboundType()));
        List<StockInboundItemVO> items = inboundItemMapper.selectByInboundId(inboundId);
        detail.setItems(items != null ? items : Collections.emptyList());
        return detail;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> createManual(Long shopId, StockInboundCreateDTO dto, Long operatorId,
            String operatorName)
    {
        validateCreateDto(dto);
        Map<Long, Integer> merged = mergeItems(dto.getItems());
        List<Long> goodsIds = new ArrayList<>(merged.keySet());
        Collections.sort(goodsIds);

        Date now = new Date();
        String inboundType = dto.getInboundType();
        String changeReason = stockSupport.changeReasonForInboundType(inboundType);
        BizStockInbound inbound = new BizStockInbound();
        inbound.setInboundNo(stockSupport.nextInboundNo());
        inbound.setShopId(shopId);
        inbound.setInboundType(inboundType);
        inbound.setRemark(StringUtils.trim(dto.getRemark()));
        inbound.setOperatorId(operatorId);
        inbound.setOperatorName(StringUtils.nvl(operatorName, ""));
        inbound.setInboundTime(now);
        inbound.setCreateBy(operatorName);
        inbound.setCreateTime(now);
        inboundMapper.insert(inbound);

        List<BizStockInboundItem> itemRows = new ArrayList<>();
        for (Long goodsId : goodsIds)
        {
            Integer qty = merged.get(goodsId);
            BizGoods goods = goodsMapper.selectByIdAndShopForUpdate(goodsId, shopId);
            if (goods == null)
            {
                throw new ServiceException(StockConstants.MSG_INBOUND_GOODS_INVALID);
            }
            int stockBefore = goods.getStock() != null ? goods.getStock() : 0;
            int rows = goodsMapper.incrementStock(goodsId, shopId, qty);
            if (rows <= 0)
            {
                throw new ServiceException(StockConstants.MSG_INBOUND_GOODS_INVALID);
            }
            int stockAfter = stockBefore + qty;
            String specText = stockSupport.buildSpecText(goodsId);

            BizStockInboundItem item = new BizStockInboundItem();
            item.setInboundId(inbound.getInboundId());
            item.setGoodsId(goodsId);
            item.setGoodsName(goods.getGoodsName());
            item.setMainPic(StringUtils.nvl(goods.getMainPic(), ""));
            item.setSpecText(stockSupport.normalizeSpecText(specText));
            item.setQuantity(qty);
            item.setStockBefore(stockBefore);
            item.setStockAfter(stockAfter);
            itemRows.add(item);

            BizStockLog log = new BizStockLog();
            log.setShopId(shopId);
            log.setGoodsId(goodsId);
            log.setGoodsName(goods.getGoodsName());
            log.setSpecText(stockSupport.normalizeSpecText(specText));
            log.setDirection(StockConstants.DIRECTION_IN);
            log.setChangeReason(changeReason);
            log.setChangeQty(qty);
            log.setStockBefore(stockBefore);
            log.setStockAfter(stockAfter);
            log.setRefType(StockConstants.REF_TYPE_INBOUND);
            log.setRefId(inbound.getInboundId());
            log.setRefNo(inbound.getInboundNo());
            log.setOperatorId(operatorId);
            log.setOperatorName(StringUtils.nvl(operatorName, ""));
            log.setCreateTime(now);
            stockLogService.insertLog(log);
        }
        inboundItemMapper.batchInsert(itemRows);

        Map<String, Object> result = new HashMap<>();
        result.put("inboundId", inbound.getInboundId());
        result.put("inboundNo", inbound.getInboundNo());
        return result;
    }

    @Override
    public List<StockOperatorVO> listOperators(Long shopId)
    {
        List<StockOperatorVO> rows = inboundMapper.selectDistinctOperators(shopId);
        return rows != null ? rows : Collections.emptyList();
    }

    @Override
    public List<StockGoodsPickVO> listGoodsOptions(Long shopId, String keyword)
    {
        List<BizGoods> goodsList = goodsMapper.selectStockPickList(shopId, keyword);
        return toPickVoList(goodsList);
    }

    @Override
    public List<StockGoodsPickVO> listGoodsByBarcode(Long shopId, String barcode)
    {
        if (StringUtils.isEmpty(barcode))
        {
            throw new ServiceException(StockConstants.MSG_BARCODE_REQUIRED);
        }
        List<BizGoods> goodsList = goodsMapper.selectByGoodsSnAndShop(shopId, barcode.trim());
        if (goodsList == null || goodsList.isEmpty())
        {
            throw new ServiceException(StockConstants.MSG_BARCODE_NOT_FOUND);
        }
        return toPickVoList(goodsList);
    }

    private List<StockGoodsPickVO> toPickVoList(List<BizGoods> goodsList)
    {
        if (goodsList == null || goodsList.isEmpty())
        {
            return Collections.emptyList();
        }
        List<StockGoodsPickVO> result = new ArrayList<>();
        for (BizGoods goods : goodsList)
        {
            StockGoodsPickVO vo = new StockGoodsPickVO();
            vo.setGoodsId(goods.getGoodsId());
            vo.setGoodsName(goods.getGoodsName());
            vo.setMainPic(goods.getMainPic());
            vo.setStock(goods.getStock());
            vo.setGoodsSn(goods.getGoodsSn());
            vo.setSpecText(stockSupport.normalizeSpecText(stockSupport.buildSpecText(goods.getGoodsId())));
            result.add(vo);
        }
        return result;
    }

    private void validateCreateDto(StockInboundCreateDTO dto)
    {
        if (dto == null || StringUtils.isEmpty(dto.getInboundType()))
        {
            throw new ServiceException(StockConstants.MSG_INBOUND_TYPE_REQUIRED);
        }
        if (!StockConstants.INBOUND_TYPE_PURCHASE.equals(dto.getInboundType())
                && !StockConstants.INBOUND_TYPE_RETURN.equals(dto.getInboundType())
                && !StockConstants.INBOUND_TYPE_OTHER.equals(dto.getInboundType()))
        {
            throw new ServiceException(StockConstants.MSG_INBOUND_TYPE_INVALID);
        }
        if (dto.getItems() == null || dto.getItems().isEmpty())
        {
            throw new ServiceException(StockConstants.MSG_INBOUND_ITEMS_REQUIRED);
        }
        String remark = dto.getRemark();
        if (StringUtils.isNotEmpty(remark) && remark.length() > StockConstants.MAX_REMARK_LENGTH)
        {
            throw new ServiceException(StockConstants.MSG_REMARK_TOO_LONG);
        }
        for (StockInboundCreateItemDTO item : dto.getItems())
        {
            if (item == null || item.getGoodsId() == null)
            {
                throw new ServiceException(StockConstants.MSG_INBOUND_GOODS_INVALID);
            }
            if (item.getQuantity() == null || item.getQuantity() < 1)
            {
                throw new ServiceException(StockConstants.MSG_INBOUND_QTY_INVALID);
            }
        }
    }

    private Map<Long, Integer> mergeItems(List<StockInboundCreateItemDTO> items)
    {
        Map<Long, Integer> merged = new LinkedHashMap<>();
        for (StockInboundCreateItemDTO item : items)
        {
            merged.merge(item.getGoodsId(), item.getQuantity(), Integer::sum);
        }
        return merged;
    }
}
