package cn.qihangerp.service.external.impl;

import cn.qihangerp.common.ResultVo;
import cn.qihangerp.model.bo.GoodsAddBo;
import cn.qihangerp.model.entity.OGoods;
import cn.qihangerp.model.entity.OGoodsSku;
import cn.qihangerp.model.request.ExternalGoodsDelistRequest;
import cn.qihangerp.model.request.ExternalGoodsUpsertRequest;
import cn.qihangerp.model.vo.ExternalGoodsUpsertResultVo;
import cn.qihangerp.model.vo.PddPublishLaneResultVo;
import cn.qihangerp.model.vo.SphPublishLaneResultVo;
import cn.qihangerp.module.service.OGoodsService;
import cn.qihangerp.module.service.OGoodsSkuService;
import cn.qihangerp.service.external.ExternalGoodsAppService;
import cn.qihangerp.service.external.pdd.ExternalPddPublishService;
import cn.qihangerp.service.external.sph.ExternalSphPublishService;
import cn.qihangerp.service.external.pdd.OGoodsPddMappingPersistence;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author guochengyu
 */
@Slf4j
@AllArgsConstructor
@Service
public class ExternalGoodsAppServiceImpl implements ExternalGoodsAppService {
    private final OGoodsService goodsService;
    private final OGoodsSkuService skuService;
    private final ExternalPddPublishService externalPddPublishService;
    private final ExternalSphPublishService externalSphPublishService;
    private final OGoodsPddMappingPersistence oGoodsPddMappingPersistence;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ExternalGoodsUpsertResultVo upsertGoods(ExternalGoodsUpsertRequest req) {
        Objects.requireNonNull(req, "req");

        OGoods existing = goodsService.getOne(new LambdaQueryWrapper<OGoods>()
                .eq(OGoods::getShopId, req.getShopId())
                .eq(OGoods::getOuterErpGoodsId, req.getOutGoodsId())
                .last("LIMIT 1"));

        Long goodsId;
        if (existing == null) {
            GoodsAddBo addBo = toGoodsAddBo(req);
            ResultVo<Long> r = goodsService.insertGoods("external", addBo);
            if (r == null || r.getCode() != 0) {
                throw new IllegalArgumentException(r == null ? "新增商品失败" : r.getMsg());
            }
            goodsId = r.getData();
            OGoods inserted = goodsService.getById(goodsId);
            if (inserted != null) {
                applyGoodsCanonicalFields(inserted, req);
                goodsService.updateById(inserted);
            }
        } else {
            applyGoodsCanonicalFields(existing, req);
            goodsService.updateById(existing);
            goodsId = existing.getId();
        }

        if (!CollectionUtils.isEmpty(req.getSkus())) {
            for (var skuReq : req.getSkus()) {
                if (skuReq == null || !StringUtils.hasText(skuReq.getOutSkuId())) {
                    continue;
                }

                OGoodsSku sku = skuService.getOne(new LambdaQueryWrapper<OGoodsSku>()
                        .eq(OGoodsSku::getShopId, req.getShopId())
                        .eq(OGoodsSku::getOuterErpSkuId, skuReq.getOutSkuId())
                        .last("LIMIT 1"));

                if (sku == null) {
                    sku = new OGoodsSku();
                    sku.setShopId(req.getShopId());
                    sku.setGoodsId(goodsId);
                    sku.setOuterErpGoodsId(req.getOutGoodsId());
                    sku.setOuterErpSkuId(skuReq.getOutSkuId());
                    sku.setCreateTime(new Date());
                    applySkuFields(sku, skuReq, req, goodsId);
                    skuService.save(sku);
                } else {
                    applySkuFields(sku, skuReq, req, goodsId);
                    sku.setUpdateTime(new Date());
                    skuService.updateById(sku);
                }
            }
        }

        ExternalGoodsUpsertResultVo.ExternalGoodsUpsertResultVoBuilder out = ExternalGoodsUpsertResultVo.builder()
                .goodsId(goodsId)
                .pddPublishAttempted(false)
                .pddPublishSuccess(null)
                .pddPublishMessage(null)
                .pddResponseSnippet(null)
                .shopCredentialSource(null)
                .pddCatRuleFetched(null)
                .pddCatRuleSnippet(null)
                .pddSpecAutoResolved(null)
                .pddAutoResolveDetail(null)
                .pddPublishLane(null)
                .sphPublishAttempted(false)
                .sphPublishSuccess(null)
                .sphPublishMessage(null)
                .sphResponseSnippet(null)
                .sphProductId(null);

        if ("PDD".equalsIgnoreCase(req.getPlatform())) {
            OGoods g = goodsService.getById(goodsId);
            List<OGoodsSku> skuRows = skuService.list(new LambdaQueryWrapper<OGoodsSku>()
                    .eq(OGoodsSku::getGoodsId, goodsId));
            Map<String, OGoodsSku> byOuter = new LinkedHashMap<>();
            for (OGoodsSku row : skuRows) {
                if (row != null && StringUtils.hasText(row.getOuterErpSkuId())) {
                    byOuter.putIfAbsent(row.getOuterErpSkuId(), row);
                }
            }
            List<OGoodsSku> orderedSkus = new ArrayList<>();
            if (!CollectionUtils.isEmpty(req.getSkus())) {
                for (ExternalGoodsUpsertRequest.Sku sr : req.getSkus()) {
                    if (sr == null || !StringUtils.hasText(sr.getOutSkuId())) {
                        continue;
                    }
                    OGoodsSku row = byOuter.get(sr.getOutSkuId());
                    if (row != null) {
                        orderedSkus.add(row);
                    }
                }
            }
            PddPublishLaneResultVo lane = externalPddPublishService.publish(g, orderedSkus, req);
            out.pddPublishAttempted(Boolean.TRUE.equals(lane.getAttempted()));
            out.pddPublishSuccess(lane.getSuccess());
            out.pddPublishMessage(lane.getMessage());
            out.pddResponseSnippet(lane.getGoodsAddSnippet());
            out.pddGoodsId(lane.getPddGoodsId());
            out.shopCredentialSource(lane.getShopCredentialSource());
            out.pddCatRuleFetched(lane.getCatRuleFetched());
            out.pddCatRuleSnippet(lane.getCatRuleSnippet());
            out.pddSpecAutoResolved(lane.getSpecAutoResolved());
            out.pddAutoResolveDetail(lane.getAutoResolveDetail());
            out.pddPublishLane(lane.getPublishLane());
        }

        if ("WEIXIN".equalsIgnoreCase(req.getPlatform())) {
            OGoods g = goodsService.getById(goodsId);
            List<OGoodsSku> skuRows = skuService.list(new LambdaQueryWrapper<OGoodsSku>()
                    .eq(OGoodsSku::getGoodsId, goodsId));
            Map<String, OGoodsSku> byOuter = new LinkedHashMap<>();
            for (OGoodsSku row : skuRows) {
                if (row != null && StringUtils.hasText(row.getOuterErpSkuId())) {
                    byOuter.putIfAbsent(row.getOuterErpSkuId(), row);
                }
            }
            List<OGoodsSku> orderedSkus = new ArrayList<>();
            if (!CollectionUtils.isEmpty(req.getSkus())) {
                for (ExternalGoodsUpsertRequest.Sku sr : req.getSkus()) {
                    if (sr == null || !StringUtils.hasText(sr.getOutSkuId())) {
                        continue;
                    }
                    OGoodsSku row = byOuter.get(sr.getOutSkuId());
                    if (row != null) {
                        orderedSkus.add(row);
                    }
                }
            }
            SphPublishLaneResultVo lane = externalSphPublishService.publish(g, orderedSkus, req);
            out.sphPublishAttempted(Boolean.TRUE.equals(lane.getAttempted()));
            out.sphPublishSuccess(lane.getSuccess());
            out.sphPublishMessage(lane.getMessage());
            out.sphResponseSnippet(lane.getResponseSnippet());
            out.sphProductId(lane.getSphProductId());
        }

        ExternalGoodsUpsertResultVo vo = out.build();
        if ("PDD".equalsIgnoreCase(req.getPlatform())) {
            oGoodsPddMappingPersistence.persistAfterPddPublishSuccess(goodsId, req, vo);
            log.info("[external/upsert] PDD summary shopId={} outGoodsId={} erpGoodsId={} attempted={} success={} lane={} credentialSource={} message={} goodsAddSnippet={} catRuleFetched={} catRuleSnippet={} specAutoResolved={} autoResolveDetail={}",
                    req.getShopId(),
                    req.getOutGoodsId(),
                    vo.getGoodsId(),
                    vo.getPddPublishAttempted(),
                    vo.getPddPublishSuccess(),
                    vo.getPddPublishLane(),
                    vo.getShopCredentialSource(),
                    truncateForLog(vo.getPddPublishMessage(), 600),
                    truncateForLog(vo.getPddResponseSnippet(), 500),
                    vo.getPddCatRuleFetched(),
                    truncateForLog(vo.getPddCatRuleSnippet(), 300),
                    vo.getPddSpecAutoResolved(),
                    truncateForLog(vo.getPddAutoResolveDetail(), 300));
        }
        if ("WEIXIN".equalsIgnoreCase(req.getPlatform())) {
            log.info("[external/upsert] WEIXIN summary shopId={} outGoodsId={} erpGoodsId={} attempted={} success={} message={} sphProductId={}",
                    req.getShopId(),
                    req.getOutGoodsId(),
                    vo.getGoodsId(),
                    vo.getSphPublishAttempted(),
                    vo.getSphPublishSuccess(),
                    truncateForLog(vo.getSphPublishMessage(), 600),
                    vo.getSphProductId());
        }
        return vo;
    }

    private static String truncateForLog(String s, int max) {
        if (s == null) {
            return null;
        }
        return s.length() <= max ? s : s.substring(0, max) + "...";
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delistGoods(ExternalGoodsDelistRequest req) {
        Objects.requireNonNull(req, "req");
        if (req.getShopId() == null || req.getShopId() <= 0) {
            throw new IllegalArgumentException("shopId不能为空");
        }
        if (!StringUtils.hasText(req.getOutGoodsId())) {
            throw new IllegalArgumentException("outGoodsId不能为空");
        }
        OGoods existing = goodsService.getOne(new LambdaQueryWrapper<OGoods>()
                .eq(OGoods::getShopId, req.getShopId())
                .eq(OGoods::getOuterErpGoodsId, req.getOutGoodsId().trim())
                .last("LIMIT 1"));
        if (existing == null) {
            throw new IllegalArgumentException("商品不存在：shopId=" + req.getShopId() + ", outGoodsId=" + req.getOutGoodsId());
        }
        PddPublishLaneResultVo popDelist = externalPddPublishService.pddSaleStatusSetForDelist(req);
        if (Boolean.TRUE.equals(popDelist.getAttempted()) && !Boolean.TRUE.equals(popDelist.getSuccess())) {
            throw new IllegalArgumentException(StringUtils.hasText(popDelist.getMessage())
                    ? popDelist.getMessage()
                    : "拼多多下架在售状态失败");
        }
        // 1 销售中 2 已下架（与 o_goods.status 注释一致）
        existing.setStatus(2);
        existing.setUpdateBy("external");
        existing.setUpdateTime(new Date());
        goodsService.updateById(existing);
    }

    private static void applyGoodsCanonicalFields(OGoods g, ExternalGoodsUpsertRequest req) {
        g.setShopId(req.getShopId());
        g.setName(req.getTitle());
        g.setImage(resolveMainImage(req));
        g.setGoodsNum(resolveGoodsNumber(req));
        g.setBarCode(req.getBarCode());
        g.setProvince(req.getProvince());
        g.setCity(req.getCity());
        g.setTown(req.getTown());
        g.setPurPrice(req.getPurPrice());
        g.setWholePrice(req.getWholePrice());
        g.setRetailPrice(resolveSpuRetailPrice(req));
        g.setUnitCost(req.getUnitCost());
        g.setCategoryCode(req.getCategoryCode());
        g.setBrandCode(req.getBrandCode());
        g.setLinkUrl(req.getLinkUrl());
        g.setCanonicalExt(buildGoodsCanonicalExtJson(req));
        g.setUpdateBy("external");
        g.setUpdateTime(new Date());
    }

    private static void applySkuFields(OGoodsSku sku, ExternalGoodsUpsertRequest.Sku skuReq, ExternalGoodsUpsertRequest req, Long goodsId) {
        sku.setShopId(req.getShopId());
        sku.setGoodsId(goodsId);
        sku.setOuterErpGoodsId(req.getOutGoodsId());
        sku.setGoodsName(req.getTitle());
        sku.setGoodsNum(resolveGoodsNumber(req));
        sku.setSkuCode(skuReq.getSkuCode());
        sku.setSkuName(skuReq.getSkuName());
        sku.setBarCode(skuReq.getBarCode());
        sku.setPurPrice(skuReq.getPurPrice());
        sku.setRetailPrice(resolveSkuSalePrice(skuReq));
        sku.setUnitCost(skuReq.getUnitCost());
        sku.setGroupPrice(skuReq.getGroupPrice());
        sku.setStockQty(skuReq.getStockQty());
        sku.setWeightGram(skuReq.getWeightGram());
        sku.setSkuImageUrl(StringUtils.hasText(skuReq.getImageUrl()) ? skuReq.getImageUrl() : null);
        sku.setCanonicalExt(buildSkuCanonicalExtJson(skuReq));
    }

    private static GoodsAddBo toGoodsAddBo(ExternalGoodsUpsertRequest req) {
        GoodsAddBo bo = new GoodsAddBo();
        bo.setName(req.getTitle());
        bo.setImage(resolveMainImage(req));
        // insertGoods 要求 number 非空；对外可省略 goodsNum，用 outGoodsId 作为内部商品编码
        bo.setNumber(resolveGoodsNumber(req));
        bo.setShopId(req.getShopId());
        bo.setOuterErpGoodsId(req.getOutGoodsId());
        bo.setProvince(req.getProvince());
        bo.setCity(req.getCity());
        bo.setTown(req.getTown());
        bo.setCategoryId(null);
        bo.setBarCode(req.getBarCode());
        bo.setPurPrice(req.getPurPrice());
        bo.setWholePrice(req.getWholePrice());
        bo.setRetailPrice(resolveSpuRetailPrice(req));
        bo.setUnitCost(req.getUnitCost());
        // insertGoods 内 supplierId.toString()，对外新建默认 0，避免 NPE
        bo.setSupplierId(0L);
        bo.setBrandId(null);
        bo.setAttr5(null);
        bo.setLinkUrl(req.getLinkUrl());
        // 不在 insertGoods 中落 SKU：SKU 由本类后续循环写入且带 shopId，避免无 shopId 的重复行
        bo.setSpecList(new ArrayList<>());
        return bo;
    }

    /** 商品编码：优先 goodsNum，否则用外部商品 ID，满足 insertGoods 与列表展示 */
    private static String resolveGoodsNumber(ExternalGoodsUpsertRequest req) {
        if (req == null) {
            return null;
        }
        if (StringUtils.hasText(req.getGoodsNum())) {
            return req.getGoodsNum().trim();
        }
        if (StringUtils.hasText(req.getOutGoodsId())) {
            return req.getOutGoodsId().trim();
        }
        return null;
    }

    private static String resolveMainImage(ExternalGoodsUpsertRequest req) {
        if (StringUtils.hasText(req.getMainImage())) {
            return req.getMainImage().trim();
        }
        List<String> imgs = req.getImages();
        if (!CollectionUtils.isEmpty(imgs)) {
            for (String u : imgs) {
                if (StringUtils.hasText(u)) {
                    return u.trim();
                }
            }
        }
        return null;
    }

    private static BigDecimal resolveSpuRetailPrice(ExternalGoodsUpsertRequest req) {
        if (req.getRetailPrice() != null) {
            return req.getRetailPrice();
        }
        if (CollectionUtils.isEmpty(req.getSkus())) {
            return null;
        }
        BigDecimal max = null;
        for (ExternalGoodsUpsertRequest.Sku s : req.getSkus()) {
            if (s == null) {
                continue;
            }
            BigDecimal p = resolveSkuSalePrice(s);
            if (p != null && (max == null || p.compareTo(max) > 0)) {
                max = p;
            }
        }
        return max;
    }

    private static BigDecimal resolveSkuSalePrice(ExternalGoodsUpsertRequest.Sku skuReq) {
        if (skuReq.getSalePrice() != null) {
            return skuReq.getSalePrice();
        }
        return skuReq.getRetailPrice();
    }

    private static String buildGoodsCanonicalExtJson(ExternalGoodsUpsertRequest req) {
        JSONObject o = new JSONObject();
        if (!CollectionUtils.isEmpty(req.getImages())) {
            o.put("images", req.getImages());
        }
        if (!CollectionUtils.isEmpty(req.getDetailImages())) {
            o.put("detailImages", req.getDetailImages());
        }
        if (StringUtils.hasText(req.getVideoUrl())) {
            o.put("videoUrl", req.getVideoUrl());
        }
        if (!CollectionUtils.isEmpty(req.getAttributes())) {
            o.put("attributes", req.getAttributes());
        }
        if (!CollectionUtils.isEmpty(req.getSalesAttributes())) {
            o.put("salesAttributes", req.getSalesAttributes());
        }
        if (req.getMarketPrice() != null) {
            o.put("marketPrice", req.getMarketPrice());
        }
        if (req.getShipWithinHours() != null) {
            o.put("shipWithinHours", req.getShipWithinHours());
        }
        if (req.getIsRefundable() != null) {
            o.put("isRefundable", req.getIsRefundable());
        }
        if (req.getIsPreSale() != null) {
            o.put("isPreSale", req.getIsPreSale());
        }
        if (req.getIsSecondHand() != null) {
            o.put("isSecondHand", req.getIsSecondHand());
        }
        if (StringUtils.hasText(req.getLogisticsTemplateCode())) {
            o.put("logisticsTemplateCode", req.getLogisticsTemplateCode());
        }
        if (!CollectionUtils.isEmpty(req.getExt())) {
            o.put("ext", req.getExt());
        }
        return o.isEmpty() ? null : JSON.toJSONString(o);
    }

    private static String buildSkuCanonicalExtJson(ExternalGoodsUpsertRequest.Sku skuReq) {
        if (CollectionUtils.isEmpty(skuReq.getAttributeValues())) {
            return null;
        }
        JSONObject o = new JSONObject();
        o.put("attributeValues", skuReq.getAttributeValues());
        return JSON.toJSONString(o);
    }
}
