/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.springblade.modules.mall.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springblade.common.constant.CacheBizConstant;
import org.springblade.common.enums.AuditStatusEnum;
import org.springblade.common.enums.YesOrNoEnum;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.modules.mall.entity.*;
import org.springblade.modules.mall.mapper.GoodsInfoMapper;
import org.springblade.modules.mall.service.ICategoryInfoService;
import org.springblade.modules.mall.service.IGoodsDetailService;
import org.springblade.modules.mall.service.IGoodsInfoService;
import org.springblade.modules.mall.service.IStoreService;
import org.springblade.modules.mall.vo.GoodsInfoVO;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.io.InputStream;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 商品表 服务实现类
 *
 * @author BladeX
 * @since 2022-05-07
 */
@Slf4j
@Service
public class GoodsInfoServiceImpl extends ServiceImpl<GoodsInfoMapper, GoodsInfo> implements IGoodsInfoService {

	@Resource
	private GoodsImgServiceImpl goodsImgService;
	@Resource
	private IGoodsDetailService goodsDetailService;
	@Resource
	private IStoreService storeService;
	@Resource
	private ICategoryInfoService categoryInfoService;
	@Resource
	private BladeRedis bladeRedis;

	@Override
	public GoodsInfo get(Serializable id) {
		return this.baseMapper.get(id);
	}

	@Override
	public IPage<GoodsInfoVO> selectGoodsInfoPage(IPage<GoodsInfoVO> page, GoodsInfoVO goodsInfo) {
		return page.setRecords(baseMapper.selectGoodsInfoPage(page, goodsInfo));
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void update(@Valid GoodsInfo goodsInfo) {
		GoodsInfo record = this.getById(goodsInfo.getId());
		Assert.notNull(record, "商品信息不存在");
//		goodsInfo.setDiscountPrice(this.calDiscountPrice(goodsInfo.getDiscount(), goodsInfo.getSalePrice()));
		this.updateById(goodsInfo);
		// 删除老的图片数据
		LambdaQueryWrapper<GoodsImg> wrapper = Wrappers.lambdaQuery(GoodsImg.class).eq(GoodsImg::getGoodsId, goodsInfo.getId());
		List<GoodsImg> list = goodsImgService.list(wrapper);
		if (CollUtil.isNotEmpty(list)) {
			goodsImgService.remove(wrapper);
		}

		// 保存商品图片数据
		List<GoodsImg> goodsImgList = new ArrayList<>();
		List<String> slideshowList = goodsInfo.getSlideshowList();
		if (StrUtil.isNotBlank(goodsInfo.getVideoUrl())) {
			goodsImgList.add(new GoodsImg(goodsInfo.getId(), goodsInfo.getVideoUrl(), 1, 2, 1));
		}
		if (CollUtil.isNotEmpty(slideshowList)) {
			for (int i = 0; i < slideshowList.size(); i++) {
				String url = slideshowList.get(i);
				goodsImgList.add(new GoodsImg(goodsInfo.getId(), url, 1, 1, i + 2));
			}
		}
		if (CollUtil.isNotEmpty(goodsImgList)) {
			goodsImgService.saveBatch(goodsImgList);
		}

		GoodsDetail goodsDetail = goodsDetailService.lambdaQuery().eq(GoodsDetail::getGoodsId, goodsInfo.getId()).one();
		if (goodsDetail != null) {
			goodsDetail.setContent(goodsInfo.getDetailContent());
			goodsDetailService.updateById(goodsDetail);
		} else {
			goodsDetail = new GoodsDetail();
			goodsDetail.setContent(goodsInfo.getDetailContent());
			goodsDetail.setGoodsId(record.getId());
			goodsDetailService.save(goodsDetail);
		}
		// 更新库存
		this.cacheGoodsStore(goodsInfo.getId(), goodsInfo.getTotalStock());
	}

	/**
	 * 计算折扣价格
	 * @param discount
	 * @param goodsPrice
	 * @return
	 */
	private BigDecimal calDiscountPrice(Double discount, BigDecimal goodsPrice) {
		return goodsPrice.multiply(BigDecimal.valueOf(discount)).divide(BigDecimal.valueOf(10), 2, BigDecimal.ROUND_HALF_UP);
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void submit(@Valid GoodsInfo goodsInfo) {
		if (StrUtil.isBlank(goodsInfo.getDetailContent())) {
			throw new ServiceException("商品详情不能为空");
		}
		if (StrUtil.isBlank(goodsInfo.getGoodsNo())) {
			throw new ServiceException("商品编号不能为空");
		}
		Integer count = lambdaQuery().eq(GoodsInfo::getGoodsNo, goodsInfo.getGoodsNo()).count();
		if (count > 0) {
			throw new ServiceException("商品编号已存在");
		}
		goodsInfo.setId(null);
//		goodsInfo.setDiscountPrice(this.calDiscountPrice(goodsInfo.getDiscount(), goodsInfo.getSalePrice()));
		this.save(goodsInfo);
		// 保存商品轮播图片数据
		List<GoodsImg> goodsImgList = new ArrayList<>();
		List<String> slideshowList = goodsInfo.getSlideshowList();
		if (StrUtil.isNotBlank(goodsInfo.getVideoUrl())) {
			goodsImgList.add(new GoodsImg(goodsInfo.getId(), goodsInfo.getVideoUrl(), 1, 2, 1));
		}
		if (CollUtil.isNotEmpty(slideshowList)) {
			for (int i = 0; i < slideshowList.size(); i++) {
				String url = slideshowList.get(i);
				goodsImgList.add(new GoodsImg(goodsInfo.getId(), url, 1, 1, i + 2));
			}
		}
		if (CollUtil.isNotEmpty(goodsImgList)) {
			goodsImgService.saveBatch(goodsImgList);
		}

		// 保存商品详情
		GoodsDetail goodsDetail = new GoodsDetail();
		goodsDetail.setContent(goodsInfo.getDetailContent());
		goodsDetail.setGoodsId(goodsInfo.getId());
		goodsDetailService.save(goodsDetail);

		// 更新库存
		this.cacheGoodsStore(goodsInfo.getId(), goodsInfo.getTotalStock());
	}

	@Override
	public GoodsInfo detail(Long id) {
		GoodsInfo detail = this.getById(id);
		Assert.notNull(detail, "商品详情不存在");
		List<GoodsImg> list = goodsImgService.list(Wrappers.lambdaQuery(GoodsImg.class).eq(GoodsImg::getGoodsId, detail.getId()));
		if (CollUtil.isNotEmpty(list)) {
			List<String> slideshowList = new ArrayList<>();
			List<String> detailImgUrlList = new ArrayList<>();
			for (GoodsImg goodsImg : list) {
				if (goodsImg.getFileType() == 2) {
					detail.setVideoUrl(goodsImg.getUrl());
					continue;
				}
				if (goodsImg.getType() == 1) {
					slideshowList.add(goodsImg.getUrl());
				} else {
					detailImgUrlList.add(goodsImg.getUrl());
				}
			}
			detail.setDetailImgUrlList(detailImgUrlList);
			detail.setSlideshowList(slideshowList);
		}
		GoodsDetail goodsDetail = goodsDetailService.lambdaQuery().eq(GoodsDetail::getGoodsId, id).one();
		if (goodsDetail != null) {
			detail.setDetailContent(goodsDetail.getContent());
		}
		detail.setTotalStock(this.getGodsStock(detail.getId()));
		return detail;
	}

	@Override
	public GoodsInfo getByGoodsNo(String goodsNo) {
		return this.getOne(Wrappers.lambdaQuery(GoodsInfo.class).eq(GoodsInfo::getGoodsNo, goodsNo));
	}

	@Override
	public void modifyState(List<Long> ids, Integer state) {
		List<GoodsInfo> goodsInfos = this.getBaseMapper().selectList(Wrappers.lambdaQuery(GoodsInfo.class).in(GoodsInfo::getId, ids));
		if (CollUtil.isNotEmpty(goodsInfos)) {
			List<GoodsInfo> updateList = new ArrayList<>();
			for (GoodsInfo goodsInfo : goodsInfos) {
				GoodsInfo update = new GoodsInfo();
				update.setId(goodsInfo.getId());
				update.setIsSale(state);
				updateList.add(update);
			}
			this.updateBatchById(updateList);
		}
	}

	@Override
	public void modifyCategory(List<Long> ids, String categoryIds) {
		List<GoodsInfo> goodsInfos = this.getBaseMapper().selectList(Wrappers.lambdaQuery(GoodsInfo.class).in(GoodsInfo::getId, ids));
		if (CollUtil.isNotEmpty(goodsInfos)) {
			List<GoodsInfo> updateList = new ArrayList<>();
			for (GoodsInfo goodsInfo : goodsInfos) {
				GoodsInfo update = new GoodsInfo();
				update.setId(goodsInfo.getId());
				update.setGoodsCategoryId(categoryIds + "-");
				updateList.add(update);
			}
			this.updateBatchById(updateList);
		}
	}



	@Transactional(rollbackFor = Exception.class)
	@Override
	public void modifyZoneType(List<Long> ids, Integer zoneType, Date activityDate) {
		List<GoodsInfo> goodsInfos = this.getBaseMapper().selectList(Wrappers.lambdaQuery(GoodsInfo.class).in(GoodsInfo::getId, ids));
		if (CollUtil.isNotEmpty(goodsInfos)) {
			goodsInfos.forEach(e -> e.setZoneType(zoneType));
			this.updateBatchById(goodsInfos);
		}
	}


	@Override
	public List<GoodsInfo> activityGoods(IPage<Object> page, String activityDate) {
		return this.baseMapper.activityGoods(page, activityDate);
	}

	@Async
	@Override
	public void addTotalView(Long goodsInfoId) {
		try {
			GoodsInfo goodsInfo = this.getById(goodsInfoId);
			goodsInfo.setTotalViews(goodsInfo.getTotalViews());
			this.updateById(goodsInfo);
		} catch (Exception e) {}
	}

	@Override
	public void audit(String id, Integer auditStatus, String remark) {
		GoodsInfo goodsInfo = getById(id);
		Assert.notNull(goodsInfo, "数据不存在");
		goodsInfo.setAuditStatus(auditStatus);
		goodsInfo.setAuditMessage(remark);
		updateById(goodsInfo);
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void copy(Long goodsId) {
		GoodsInfo goodsInfo = getById(goodsId);
		Assert.notNull(goodsInfo, "商品信息不存在");
		GoodsDetail goodsDetail = goodsDetailService.lambdaQuery().eq(GoodsDetail::getGoodsId, goodsId).one();
		List<GoodsImg> goodsImgs = goodsImgService.lambdaQuery().eq(GoodsImg::getGoodsId, goodsId).list();

		goodsInfo.setId(null);
		goodsInfo.setCreateTime(new Date());
		goodsInfo.setGoodsNo("temp_" + RandomUtil.randomStringUpper(12));
		save(goodsInfo);

		if (goodsDetail != null) {
			goodsDetail.setId(null);
			goodsDetail.setGoodsId(goodsInfo.getId());
			goodsDetailService.save(goodsDetail);
		}

		if (CollUtil.isNotEmpty(goodsImgs)) {
			for (GoodsImg goodsImg : goodsImgs) {
				goodsImg.setId(null);
				goodsImg.setGoodsId(goodsInfo.getId());
			}
			goodsImgService.saveBatch(goodsImgs);
		}
	}

	@Override
	public void batchImport(InputStream inputStream, Long storeId) {
		Store store = storeService.getById(storeId);
		Assert.notNull(store, "供应商信息不存在");
		ExcelReader reader = ExcelUtil.getReader(inputStream);
		reader.setHeaderAlias(getHeaderAlias());

		List<GoodsImport> imports = reader.readAll(GoodsImport.class);
		Assert.notEmpty(imports, "表格数据为空");
		// 校验表格里是否有重复的商品编号
		List<String> collect = imports.stream().map(GoodsImport::getGoodsNo).collect(Collectors.toList());
		Set<String> set = new HashSet<>(collect);
		if (collect.size() > set.size()) {
			throw new ServiceException("有重复的商品编号，请检查");
		}

		List<GoodsInfo> list = lambdaQuery().in(GoodsInfo::getGoodsNo, set).list();
		if (CollUtil.isNotEmpty(list)) {
			List<String> repeat = list.stream().map(GoodsInfo::getGoodsNo).collect(Collectors.toList());
			String join = CollUtil.join(repeat, ",");
			throw new ServiceException("以下商品编号被占用[" + join + "]");
		}

		// 查询名称为'其他'的分类，将导入的数据纳入到此分类下
		Long otherCategoryId = this.getOtherCategoryId();
		List<GoodsInfo> goodsInfoList = new ArrayList<>();
		for (GoodsImport g : imports) {
			GoodsInfo goodsInfo = BeanUtil.copyProperties(g, GoodsInfo.class);
			goodsInfo.setAuditStatus(AuditStatusEnum.WAIT.getValue());
			goodsInfo.setIsSale(YesOrNoEnum.NO.getValue());
			goodsInfo.setGoodsCategoryId(otherCategoryId.toString());
			goodsInfo.setSupplierId(store.getId());
			goodsInfoList.add(goodsInfo);
		}
		saveBatch(goodsInfoList);

	}

	private Map<String, String> getHeaderAlias() {
		Map<String, String> map = new HashMap<>();
		map.put("商品名称", "goodsName");
		map.put("商品编号", "goodsNo");
		map.put("商品规格", "goodsSpec");
		map.put("划线价", "salePrice");
		map.put("供货价", "costPrice");
		map.put("库存", "totalStock");
		return map;
	}

	private Long getOtherCategoryId() {
		CategoryInfo one = categoryInfoService.lambdaQuery().eq(CategoryInfo::getTitle, "其他").one();
		Assert.notNull(one, "请联系管理员添加'其他'分类数据");
		return one.getId();
	}

	/**
	 * 缓存商品库存
	 */
	public void cacheGoodsStore(Long goodsId, Integer goodsStock) {
		String key = CacheBizConstant.GOODS_STORE + goodsId;
		Long size = bladeRedis.lLen(key);
		if (size <= 0 && goodsStock > 0) {
			Object[] stockList = getStockList(goodsStock);
			bladeRedis.getListOps().leftPushAll(key, stockList);
		} else if (size > 0 && goodsStock == 0) {
			bladeRedis.del(key);
		} else if (size > 0 && goodsStock > 0 && size > goodsStock) {
			// 减少库存
			bladeRedis.lTrim(key, 0, goodsStock - 1);
		} else if (size > 0 && goodsStock > 0 && size < goodsStock) {
			// 增加库存
			long num = goodsStock - size;
			Object[] stockList = getStockList(Math.toIntExact(num));
			bladeRedis.getListOps().leftPushAll(key, stockList);
		}
	}

	@Override
	public int getGodsStock(Long goodsId) {
		String key = CacheBizConstant.GOODS_STORE + goodsId;
		Boolean exists = bladeRedis.exists(key);
		if (!exists) {
			GoodsInfo goodsInfo = getById(goodsId);
			this.cacheGoodsStore(goodsId, goodsInfo.getTotalStock());
			return goodsInfo.getTotalStock();
		} else {
			return bladeRedis.lLen(key).intValue();
		}
	}

	@Override
	public Object[] getStockList(Integer goodsStock) {
		List<Object> list = new ArrayList<>();
		for (int i = 1; i <= goodsStock; i++) {
			list.add(1);
		}
		return list.toArray();
	}
}
