package com.cpop.mall.business.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.cpop.common.utils.StringUtils;
import com.cpop.common.utils.bean.BeanUtils;
import com.cpop.core.base.entity.PageDomain;
import com.cpop.core.base.enums.SourceType;
import com.cpop.core.service.RedisService;
import com.cpop.core.utils.SecurityUtils;
import com.cpop.core.utils.SpringUtils;
import com.cpop.core.utils.sql.SqlUtils;
import com.cpop.jambox.business.entity.BrandExtend;
import com.cpop.jambox.business.entity.CardTemplate;
import com.cpop.jambox.business.service.BrandExtendService;
import com.cpop.jambox.business.service.CardTemplateService;
import com.cpop.mall.business.bo.ProductBo;
import com.cpop.mall.business.bo.ProductPageBo;
import com.cpop.mall.business.bo.ProductRecordBo;
import com.cpop.mall.business.entity.Product;
import com.cpop.mall.business.entity.ProductRecord;
import com.cpop.mall.business.entity.ProductSpecification;
import com.cpop.mall.business.mapper.ProductMapper;
import com.cpop.mall.business.service.OrderEvaluateService;
import com.cpop.mall.business.service.ProductRecordService;
import com.cpop.mall.business.service.ProductService;
import com.cpop.mall.business.service.ProductSpecificationService;
import com.cpop.mall.framework.task.ShoppingCartAsyncTask;
import com.cpop.mall.business.vo.*;
import com.cpop.mall.framework.constant.MallRedisConstant;
import com.cpop.system.business.service.StoreService;
import com.mybatisflex.core.datasource.DataSourceKey;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.Db;
import com.mybatisflex.core.row.Row;
import com.mybatisflex.core.row.RowUtil;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import static com.cpop.jambox.business.entity.table.BrandExtendTableDef.BRAND_EXTEND;
import static com.cpop.jambox.business.entity.table.StoreExtendTableDef.STORE_EXTEND;
import static com.cpop.mall.business.entity.table.OrderDetailTableDef.ORDER_DETAIL;
import static com.cpop.mall.business.entity.table.OrderEvaluateTableDef.ORDER_EVALUATE;
import static com.cpop.mall.business.entity.table.OrderTableDef.ORDER;
import static com.cpop.mall.business.entity.table.ProductRecordTableDef.PRODUCT_RECORD;
import static com.cpop.mall.business.entity.table.ProductSpecificationTableDef.PRODUCT_SPECIFICATION;
import static com.cpop.mall.business.entity.table.ProductTableDef.PRODUCT;
import static com.cpop.system.business.entity.table.BrandTableDef.BRAND;
import static com.cpop.system.business.entity.table.StoreTableDef.STORE;
import static com.mybatisflex.core.query.QueryMethods.*;

/**
 * 商城-商品表 服务层实现。
 *
 * @author DB
 * @since 2023-10-23
 */
@Service("productService")
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {

    /**
     * @descriptions 分页查询商城-商品
     * @author DB
     * @date 2023/10/23 11:59
     * @param bo 请求参数
     * @return: com.mybatisflex.core.paginate.Page<com.cpop.mall.business.entity.Product>
     */
    @Override
    public Page<ProductPageVo> getProductPage(ProductPageBo bo) {
        //获取当前用户品牌
        JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
        PageDomain pageDomain = SqlUtils.getInstance().getPageDomain();
        QueryWrapper queryWrapper = QueryWrapper.create();
        //价格排序
        if (null != bo.getPriceOrder()){
            if (bo.getPriceOrder()) {
                queryWrapper.orderBy(PRODUCT.MAX_CONSUME.desc());
            } else {
                queryWrapper.orderBy(PRODUCT.MIN_CONSUME.asc());
            }
        }
        return this.mapper.paginateWithRelationsAs(Page.of(pageDomain.getPageNum(), pageDomain.getPageSize()),
                queryWrapper.select(PRODUCT.ALL_COLUMNS)
                        .from(PRODUCT)
                        //如果没有品牌会查询所有数据
                        .where(PRODUCT.BRAND_ID.eq(loginUserInfo.getString("brandId")))
                        .and(PRODUCT.PRODUCT_NAME.like(bo.getProductName()))
                        .and(PRODUCT.PRODUCT_TYPE.eq(bo.getProductType()))
                        .and(PRODUCT.BUY_RESTRICT.eq(bo.getBuyRestrict()))
                        .and(PRODUCT.IS_UP.eq(bo.getIsUp()))
                        .and(PRODUCT.STORE_IDS.like(bo.getAuthorizedStoreId()))
                        //置顶
                        .orderBy(PRODUCT.IS_TOP.desc(),PRODUCT.UPDATE_TIME.desc()),
                ProductPageVo.class,
                //子查询
                item -> item.field(ProductPageVo::getTradeInfo)
                        .queryWrapper(tradeInfo -> queryChain()
                                //交易金额;交易数量
                                .select(sum(ORDER_DETAIL.NUMBER).as(ProductPageVo.ProductTradeInfo::getTradeNum), sum(ORDER_DETAIL.AMOUNT).as(ProductPageVo.ProductTradeInfo::getTradePrice))
                                .from(ORDER_DETAIL)
                                .leftJoin(PRODUCT_RECORD).on(PRODUCT_RECORD.ID.eq(ORDER_DETAIL.PRODUCT_RECORD_ID))
                                .leftJoin(ORDER).on(ORDER.ID.eq(ORDER_DETAIL.ORDER_ID))
                                .where(PRODUCT_RECORD.PRODUCT_ID.eq(tradeInfo.getId()))
                                //只能统计已完成
                                .and(ORDER.ORDER_STATUS.eq(3))),
                //授权校区查询
                item -> item.field(ProductPageVo::getAuthorizedStoreNames)
                        .queryWrapper(storeNames -> queryChain()
                                .select(groupConcat(STORE.STORE_NAME).as(ProductPageVo::getAuthorizedStoreNames))
                                .from(STORE)
                                .where(STORE.ID.in(StringUtils.isNotBlank(storeNames.getStoreIds()) ? storeNames.getStoreIds().split(",") : "-1"))
                                .groupBy(STORE.BRAND_ID))
        );
    }

    /**
     * @descriptions 分页查询商城-商品
     * @author DB
     * @date 2023/10/27 13:41
     * @param bo 请求参数
     * @return: com.mybatisflex.core.paginate.Page<com.cpop.mall.business.vo.MiniProductPageVo>
     */
    @Override
    public Page<MiniProductPageVo> getMiniProductPage(ProductPageBo bo) {
        //获取当前用户品牌
        JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
        PageDomain pageDomain = SqlUtils.getInstance().getPageDomain();
        QueryWrapper queryWrapper = QueryWrapper.create();
        //价格排序
        if (null != bo.getPriceOrder()){
            if (bo.getPriceOrder()) {
                queryWrapper.orderBy(PRODUCT.MAX_CONSUME.desc());
            } else {
                queryWrapper.orderBy(PRODUCT.MIN_CONSUME.asc());
            }
        }
        if (null != bo.getProductType()){
            queryWrapper.and(PRODUCT.PRODUCT_TYPE.eq(bo.getProductType()));
            queryWrapper.and(PRODUCT.ID.ne(bo.getProductId()));
        }
        return this.mapper.paginateWithRelationsAs(pageDomain.getPageNum(), pageDomain.getPageSize(),
                queryWrapper
                        .where(PRODUCT.BRAND_ID.eq(loginUserInfo.getString("brandId")))
                        .and(PRODUCT.IS_UP.eq(true))
                        .and(PRODUCT.PRODUCT_NAME.like(bo.getProductName()))
                        .and(PRODUCT.BUY_RESTRICT.eq(bo.getBuyRestrict()))
                        .and(PRODUCT.PAY_TYPE.eq(bo.getPayType()))
                        .orderBy(PRODUCT.IS_TOP.desc(), PRODUCT.UPDATE_TIME.desc()),
                MiniProductPageVo.class);
    }

    /**
     * @descriptions 创建规格
     * @author DB
     * @date 2023/10/23 12:43
     * @param specificationGroups 请求参数
     * @return: java.util.List<com.cpop.mall.business.vo.ProductSpecificationCreateVo>
     */
    @Override
    public List<String> createSpecification(List<List<String>> specificationGroups) {
        return recursionCreateSpecification(specificationGroups, 0, "", new ArrayList<>());
    }

    /**
     * @descriptions 递归创建
     * @author DB
     * @date 2023/10/23 16:20
     * @param list 属性值列表
     * @param index 下标
     * @param str 初始化数据
     * @param newObjs 新obj
     * @return: java.util.List<java.lang.String>
     */
    private List<String> recursionCreateSpecification(List<List<String>> list, int index, String str, List<String> newObjs) {
        if (index == list.size()) {
            newObjs.add(str);
            return newObjs;
        }
        for (String dataStr : list.get(index)) {
            if (StringUtils.isBlank(str)) {
                recursionCreateSpecification(list, index + 1, str + dataStr, newObjs);
            } else {
                recursionCreateSpecification(list, index + 1, str + "," + dataStr, newObjs);
            }
        }
        return newObjs;
    }

    /**
     * @descriptions 保存商城-商品
     * @author DB
     * @date 2023/10/23 12:15
     * @param bo 请求参数
     * @return: void
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertProduct(ProductBo bo) {
        Product product = BeanUtils.mapToClass(bo, Product.class);
        if (StringUtils.isBlank(bo.getBrandId())){
            JSONObject loginStaffInfo = SecurityUtils.getInstance().getLoginUserInfo();
            product.setBrandId(loginStaffInfo.getString("brandId"));
        }
        //规格
        ProductSpecificationService productSpecificationService = SpringUtils.getBean(ProductSpecificationService.class);
        List<ProductSpecification> productSpecifications;
        //记录
        ProductRecordService specificationRecordService = SpringUtils.getBean(ProductRecordService.class);
        //保存商品记录
        List<ProductRecord> recordList;
        //判断当前传入的是否是课卡
        if (bo.getProductType() == 0) {
            //获取模板相关信息
            Row oldTemplateInfo = SpringUtils.getBean(CardTemplateService.class).getOldTemplateInfo(bo.getCardTemplateId(), product.getBrandId());
            product.setMaxConsume(oldTemplateInfo.getBigDecimal("price")).setMinConsume(oldTemplateInfo.getBigDecimal("price"));
            this.save(product);
            //课卡模板获取固定规格
            ProductSpecification productSpecification = new ProductSpecification();
            productSpecification.setSpecificationNames(oldTemplateInfo.getString("name"));
            productSpecifications = new ArrayList<ProductSpecification>();
            productSpecifications.add(productSpecification);
            //记录
            recordList = new ArrayList<ProductRecord>();
            ProductRecord productRecord = new ProductRecord();
            productRecord.setRecordNum(2147483647).setRecordNames(productSpecification.getSpecificationNames()).setRecordPrice(oldTemplateInfo.getBigDecimal("price")).setRecordPoints(0);
            recordList.add(productRecord);
        } else {
            //积分支付
            if (bo.getPayType() == 1) {
                //计算最高最低价
                Integer maxPoint = bo.getRecordList().stream().map(ProductRecordBo::getRecordPoints).max(Comparator.comparing(x -> x)).orElse(0);
                Integer minPoint = bo.getRecordList().stream().map(ProductRecordBo::getRecordPoints).min(Comparator.comparing(x -> x)).orElse(0);
                product.setMaxConsume(BigDecimal.valueOf(maxPoint)).setMinConsume(BigDecimal.valueOf(minPoint));
            } else {
                //计算最高最低价
                BigDecimal maxPrice = bo.getRecordList().stream().map(ProductRecordBo::getRecordPrice).max(Comparator.comparing(x -> x)).orElse(null);
                BigDecimal minPrice = bo.getRecordList().stream().map(ProductRecordBo::getRecordPrice).min(Comparator.comparing(x -> x)).orElse(null);
                product.setMaxConsume(maxPrice).setMinConsume(minPrice);
            }
            this.save(product);
            productSpecifications = BeanUtils.mapToList(bo.getSpecificationList(), ProductSpecification.class);
            //保存商品记录
            recordList = BeanUtils.mapToList(bo.getRecordList(), ProductRecord.class);
        }
        productSpecifications.forEach(item -> {
            item.setProductId(product.getId());
        });
        //保存规格
        productSpecificationService.saveBatch(productSpecifications);
        recordList.forEach(item -> {
            item.setProductId(product.getId());
        });
        //保存记录
        specificationRecordService.saveBatch(recordList);
    }

    /**
     * @descriptions 根据主键更新商城-商品
     * @author DB
     * @date 2023/10/23 16:51
     * @param bo 请求参数
     * @return: void
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateProduct(ProductBo bo) {
        Product product = BeanUtils.mapToClass(bo, Product.class);
        //积分支付
        if (bo.getPayType() == 1) {
            //计算最高最低价
            Integer maxPoint = bo.getRecordList().stream().map(ProductRecordBo::getRecordPoints).max(Comparator.comparing(x -> x)).orElse(0);
            Integer minPoint = bo.getRecordList().stream().map(ProductRecordBo::getRecordPoints).min(Comparator.comparing(x -> x)).orElse(0);
            product.setMaxConsume(BigDecimal.valueOf(maxPoint)).setMinConsume(BigDecimal.valueOf(minPoint));
        } else {
            //计算最高最低价
            BigDecimal maxPrice = bo.getRecordList().stream().map(ProductRecordBo::getRecordPrice).max(Comparator.comparing(x -> x)).orElse(null);
            BigDecimal minPrice = bo.getRecordList().stream().map(ProductRecordBo::getRecordPrice).min(Comparator.comparing(x -> x)).orElse(null);
            product.setMaxConsume(maxPrice).setMinConsume(minPrice);
        }
        this.updateById(product);
        //保存规格
        ProductSpecificationService productSpecificationService = SpringUtils.getBean(ProductSpecificationService.class);
        List<ProductSpecification> productSpecifications = BeanUtils.mapToList(bo.getSpecificationList(), ProductSpecification.class);
        productSpecifications.forEach(item -> {
            item.setProductId(product.getId());
        });
        productSpecificationService.updateBatch(productSpecifications);
        //存商品记录详情
        List<ProductRecord> recordList = BeanUtils.mapToList(bo.getRecordList(), ProductRecord.class);
        ProductRecordService specificationRecordService = SpringUtils.getBean(ProductRecordService.class);
        RedisService redisService = SpringUtils.getBean(RedisService.class);
        recordList.forEach(item -> {
            item.setProductId(product.getId());
            //更新库存
            redisService.setCacheObject(MallRedisConstant.STOCK_RECORD_NUM + item.getId(), item.getRecordNum());
        });
        specificationRecordService.updateBatch(recordList);
        //异步更新购物车价格
        SpringUtils.getBean(ShoppingCartAsyncTask.class).asyncUpdateShoppingCartAmount(product);
    }

    /**
     * @descriptions 根据主键重置商城商品
     * @author DB
     * @date 2023/10/23 17:21
     * @param id 主键
     * @return: void
     */
    @Override
    public void resetProduct(String id) {
        //删规格
        ProductSpecificationService productSpecificationService = SpringUtils.getBean(ProductSpecificationService.class);
        productSpecificationService.updateChain().where(PRODUCT_SPECIFICATION.PRODUCT_ID.eq(id)).remove();
        //删商品记录详情
        ProductRecordService specificationRecordService = SpringUtils.getBean(ProductRecordService.class);
        specificationRecordService.updateChain().where(PRODUCT_RECORD.PRODUCT_ID.eq(id)).remove();
    }

    /**
     * @descriptions 获取果酱课卡模板
     * @author DB
     * @date 2023/10/23 18:11
     * @return: java.util.List<com.cpop.jambox.business.entity.CardTemplate>
     */
    @Override
    public List<JamboxCardTemplateListVo> getJamboxCardTemplate() {
        JSONObject loginStaffInfo = SecurityUtils.getInstance().getLoginUserInfo();
        //获取果酱品牌拓展信息
        BrandExtendService brandExtendService = SpringUtils.getBean(BrandExtendService.class);
        BrandExtend brandExtend = brandExtendService.queryChain()
                .leftJoin(BRAND).on(BRAND.ID.eq(BRAND_EXTEND.BRAND_ID))
                .where(BRAND.ID.eq(loginStaffInfo.getString("brandId"))).one();
        //多数据源
        List<Row> rowList;
        List<JamboxCardTemplateListVo> cardTemplateListVos;
        try {
            DataSourceKey.use("jambox");
            rowList = Db.selectListByQuery("t_card_template", QueryWrapper.create()
                    .select("tct.template_id AS id", "tct.brand_id", "tct.name")
                    .from("t_card_template").as("tct")
                    .leftJoin("t_brand_info").as("tbi").on("tbi.id = tct.brand_id")
                    .where("tbi.brand_id = ?", brandExtend.getBrandCloudId())
                    .or("tct.brand_id = ?", brandExtend.getBrandCloudId())
                    .like("tct.pay_type", 0)
                    //未被删除的模板
                    .and("tct.deleted = 1")
                    .orderBy("tct.creation_time", false));
            cardTemplateListVos = RowUtil.toEntityList(rowList, JamboxCardTemplateListVo.class);
        } finally {
            DataSourceKey.clear();
        }
        return cardTemplateListVos;
    }

    /**
     * @descriptions 下单需要选择店铺/校区
     * @author DB
     * @date 2023/10/26 15:34
     * @return: java.util.List<com.cpop.mall.business.vo.StoreListVo>
     */
    @Override
    public List<StoreListVo> getStoreList() {
        //获取当前用户信息
        JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
        SourceType sourceType = SourceType.valueOf(loginUserInfo.getString("sourceType"));
        switch (sourceType) {
            //果酱
            case JAMBOX:
                return getJamboxStore(loginUserInfo);
            case COMMON:
                return getCommonStore(loginUserInfo);
            default:
        }
        return null;
    }

    /**
     * @descriptions 获取通用校区信息
     * @author DB
     * @date 2023/10/26 15:54
     * @param loginUserInfo 登录用户信息
     * @return: java.util.List<com.cpop.mall.business.vo.StoreListVo>
     */
    private List<StoreListVo> getCommonStore(JSONObject loginUserInfo) {
        return SpringUtils.getBean(StoreService.class).queryChain()
                .select(STORE.STORE_NAME, STORE.ID)
                .from(STORE)
                .leftJoin(BRAND).on(BRAND.ID.eq(STORE.BRAND_ID))
                .where(BRAND.ID.eq(loginUserInfo.getString("brandId")))
                .listAs(StoreListVo.class);
    }

    /**
     * @descriptions 获取果酱校区信息
     * @author DB
     * @date 2023/10/26 15:54
     * @param loginUserInfo 登录用户信息
     * @return: java.util.List<com.cpop.mall.business.vo.StoreListVo>
     */
    private List<StoreListVo> getJamboxStore(JSONObject loginUserInfo) {
        return SpringUtils.getBean(StoreService.class).queryChain()
                .select(STORE.STORE_NAME, STORE.ID)
                .select(STORE_EXTEND.STORE_CLOUD_ID.as(StoreListVo::getOutId))
                .from(STORE)
                .leftJoin(BRAND).on(BRAND.ID.eq(STORE.BRAND_ID))
                .leftJoin(STORE_EXTEND).on(STORE_EXTEND.STORE_ID.eq(STORE.ID))
                .where(BRAND.ID.eq(loginUserInfo.getString("brandId")))
                .listAs(StoreListVo.class);
    }

    /**
     * @descriptions 查询商品评价分页列表
     * @author DB
     * @date 2023/10/26 17:02
     * @param productId 商品id
     * @return: com.mybatisflex.core.paginate.Page<com.cpop.mall.business.vo.ProductEvaluateVo>
     */
    @Override
    public Page<ProductEvaluateVo> getOrderEvaluatePage(String productId) {
        PageDomain pageDomain = SqlUtils.getInstance().getPageDomain();
        OrderEvaluateService evaluateService = SpringUtils.getBean(OrderEvaluateService.class);
        return evaluateService.getMapper().paginateAs(pageDomain.getPageNum(),pageDomain.getPageSize(),
                QueryWrapper.create()
                        .select(ORDER_EVALUATE.ALL_COLUMNS)
                        .from(ORDER_EVALUATE)
                        .leftJoin(ORDER).on(ORDER.ID.eq(ORDER_EVALUATE.ORDER_ID))
                        .leftJoin(PRODUCT).on(PRODUCT.ID.eq(ORDER_EVALUATE.PRODUCT_ID))
                        .where(PRODUCT.ID.eq(productId)),
                ProductEvaluateVo.class);
    }

    /**
     * @descriptions 获取商品详情
     * @author DB
     * @date 2023/11/06 11:56
     * @param id 商品id
     * @return: com.cpop.mall.business.vo.ProductInfoVo
     */
    @Override
    public ProductInfoVo getProductInfo(String id) {
        JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
        ProductInfoVo productInfoVo = this.mapper.selectOneWithRelationsByIdAs(id, ProductInfoVo.class);
        if (productInfoVo.getProductType() == 0){
            try {
                DataSourceKey.use("jambox");
                Row cardTemplate = SpringUtils.getBean(CardTemplateService.class).getOldTemplateInfo(productInfoVo.getCardTemplateId(), loginUserInfo.getString("brandId"));
                productInfoVo.setCardTemplateName(cardTemplate.getString("name"));
            } finally {
                DataSourceKey.clear();
            }
        }
        return productInfoVo;
    }

    /**
     * @descriptions 查询商城同类型随机商品
     * @author DB
     * @date 2023/11/13 16:24
     * @param id 主键
     * @param productType 商品类型
     * @return: com.mybatisflex.core.paginate.Page<com.cpop.mall.business.vo.MiniProductPageVo>
     */
    @Override
    public Page<MiniProductPageVo> getSameTypeRandomProduct(String id, Integer productType) {
        //获取当前用户品牌
        JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
        PageDomain pageDomain = SqlUtils.getInstance().getPageDomain();
        return this.mapper.paginateWithRelationsAs(pageDomain.getPageNum(), pageDomain.getPageSize(),
                QueryWrapper.create().select()
                        .where(PRODUCT.BRAND_ID.eq(loginUserInfo.getString("brandId")))
                        .and(PRODUCT.PRODUCT_TYPE.eq(productType))
                        .and("id >= (((SELECT MAX(id) FROM cp_mall_product)-(SELECT MIN(id) FROM cp_mall_product)) * RAND() + (SELECT MIN(id) FROM cp_mall_product))")
                        .and(PRODUCT.IS_UP.eq(true)),
                MiniProductPageVo.class);
    }
}
