package com.ruoyi.jarvis.service.goofish;

import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.jarvis.domain.ErpGoofishOrder;
import com.ruoyi.jarvis.domain.ErpGoofishOrderEventLog;
import com.ruoyi.jarvis.mapper.ErpGoofishOrderEventLogMapper;
import com.ruoyi.jarvis.wecom.WxSendGoofishNotifyClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 闲管家订单：状态 / 物流 / 发货 变更落库 + SLF4J
 */
@Component
public class GoofishOrderChangeLogger {

    private static final Logger log = LoggerFactory.getLogger(GoofishOrderChangeLogger.class);
    private static final int MESSAGE_MAX = 1000;

    public static final String TYPE_ORDER = "ORDER_SYNC";
    public static final String TYPE_LOGISTICS = "LOGISTICS_SYNC";
    public static final String TYPE_SHIP = "SHIP";

    /**
     * 京东物流扫描：货主通知已由 LogisticsServiceImpl 走闲鱼自建应用或 PDD；此处仅落库，避免再调 wxSend 重复推送。
     */
    public static final String SOURCE_JD_LOGISTICS_PUSH = "JD_LOGISTICS_PUSH";

    /**
     * Redis 写入本地运单：与后续详情运单合并、发货成功通知重复度高，仅落库便于对账，不推企微。
     */
    public static final String SOURCE_REDIS_WAYBILL = "REDIS_WAYBILL";

    @Resource
    private ErpGoofishOrderEventLogMapper erpGoofishOrderEventLogMapper;

    @Resource
    private WxSendGoofishNotifyClient wxSendGoofishNotifyClient;

    public void append(Long orderId, String appKey, String orderNo, String eventType, String source, String message) {
        append(orderId, appKey, orderNo, eventType, source, message, true);
    }

    /**
     * @param notifyWecom 为 false 时仅写事件日志，不推企微（用于过滤纯对齐类状态）。
     */
    public void append(Long orderId, String appKey, String orderNo, String eventType, String source, String message,
        boolean notifyWecom) {
        if (orderId == null || StringUtils.isEmpty(message)) {
            return;
        }
        String msg = message.length() > MESSAGE_MAX ? message.substring(0, MESSAGE_MAX - 1) + "…" : message;
        ErpGoofishOrderEventLog row = new ErpGoofishOrderEventLog();
        row.setOrderId(orderId);
        row.setAppKey(appKey);
        row.setOrderNo(orderNo != null ? orderNo : "");
        row.setEventType(eventType != null ? eventType : "UNKNOWN");
        row.setSource(source != null ? source : "");
        row.setMessage(msg);
        row.setCreateTime(DateUtils.getNowDate());
        try {
            erpGoofishOrderEventLogMapper.insert(row);
        } catch (Exception e) {
            log.warn("闲管家订单事件日志写入失败 orderId={} {}", orderId, e.toString());
            return;
        }
        log.info("[goofish-order-event] orderId={} orderNo={} type={} source={} {}", orderId, orderNo, eventType, source, msg);
        if (SOURCE_JD_LOGISTICS_PUSH.equals(source) || SOURCE_REDIS_WAYBILL.equals(source)) {
            return;
        }
        if (!notifyWecom) {
            return;
        }
        try {
            wxSendGoofishNotifyClient.notifyGoofishEvent(orderNo, eventType, source, msg);
        } catch (Exception ex) {
            log.debug("闲鱼订单事件 wxSend 通知跳过 err={}", ex.toString());
        }
    }

    /**
     * 合并摘要前后对比：订单状态/退款状态、平台运单与快递、本地运单。
     *
     * @param beforeSnap 合并前从 row 拷贝的跟踪字段快照（可不含 id）
     * @param afterRow   合并并 apply 后的 row
     */
    public void logSummaryMergeDiff(ErpGoofishOrder beforeSnap, ErpGoofishOrder afterRow, String source) {
        if (afterRow == null || afterRow.getId() == null) {
            return;
        }
        RowSnap b = RowSnap.from(beforeSnap);
        RowSnap a = RowSnap.from(afterRow);

        boolean refundDiff = !Objects.equals(b.refundStatus, a.refundStatus);
        boolean orderDiff = !Objects.equals(b.orderStatus, a.orderStatus);

        List<String> orderParts = new ArrayList<>();
        if (orderDiff) {
            orderParts.add("订单状态 " + GoofishStatusLabels.orderStatusChangeForNotify(b.orderStatus, a.orderStatus));
        }
        if (refundDiff) {
            orderParts.add("退款状态 " + GoofishStatusLabels.refundStatusChange(b.refundStatus, a.refundStatus));
        }
        if (!orderParts.isEmpty()) {
            boolean notifyWecom = refundDiff || (orderDiff
                && GoofishStatusLabels.isWxNotifiableOrderStatusChange(b.orderStatus, a.orderStatus));
            append(afterRow.getId(), afterRow.getAppKey(), afterRow.getOrderNo(), TYPE_ORDER, source,
                String.join("；", orderParts), notifyWecom);
        }

        List<String> logParts = new ArrayList<>();
        if (!eqStr(b.detailWaybillNo, a.detailWaybillNo)) {
            logParts.add("平台运单 " + str(b.detailWaybillNo) + "→" + str(a.detailWaybillNo));
        }
        if (!eqStr(b.detailExpressCode, a.detailExpressCode)) {
            logParts.add("快递编码 " + str(b.detailExpressCode) + "→" + str(a.detailExpressCode));
        }
        if (!eqStr(b.detailExpressName, a.detailExpressName)) {
            logParts.add("快递名称 " + str(b.detailExpressName) + "→" + str(a.detailExpressName));
        }
        if (!eqStr(b.localWaybillNo, a.localWaybillNo)) {
            logParts.add("本地运单 " + str(b.localWaybillNo) + "→" + str(a.localWaybillNo));
        }
        if (!logParts.isEmpty()) {
            boolean notifyWecomLogistics =
                !isLogisticsWecomMirroringShipWaybillFill(beforeSnap, afterRow, logParts);
            append(afterRow.getId(), afterRow.getAppKey(), afterRow.getOrderNo(), TYPE_LOGISTICS, source,
                String.join("；", logParts), notifyWecomLogistics);
        }
    }

    /**
     * 详情/列表反拉：平台「运单号为 null→671…」并与本地 Redis 写入的运单一致、本地运单字段未在同一笔合并里改动时，
     * 内容与刚发的「发货」通知重复 → 不写企微，仅保留事件日志。
     */
    private static boolean isLogisticsWecomMirroringShipWaybillFill(ErpGoofishOrder b, ErpGoofishOrder a,
        List<String> logParts) {
        if (a == null) {
            return false;
        }
        String lwAfter = trimEmpty(a.getLocalWaybillNo());
        if (lwAfter.isEmpty()) {
            return false;
        }
        if (!eqStr(b == null ? null : b.getLocalWaybillNo(), a == null ? null : a.getLocalWaybillNo())) {
            return false;
        }
        for (String p : logParts) {
            if (p.startsWith("本地运单 ")) {
                return false;
            }
            if (!(p.startsWith("平台运单 ") || p.startsWith("快递编码 ") || p.startsWith("快递名称 "))) {
                return false;
            }
        }
        String adb = trimEmpty(a.getDetailWaybillNo());
        String bdb = trimEmpty(b == null ? null : b.getDetailWaybillNo());
        if (!adb.equals(lwAfter)) {
            return false;
        }
        // 平台侧从空补上运单号，与同单本地发货所用单号一致
        boolean detailWaybillJustFilled =
            !eqStr(b == null ? null : b.getDetailWaybillNo(), a.getDetailWaybillNo()) && bdb.isEmpty();
        // 或运单早前已对齐，只差快递字段刷新，仍视作发货侧已知信息延伸
        boolean detailStableMatchesLocal =
            eqStr(b == null ? null : b.getDetailWaybillNo(), a.getDetailWaybillNo()) && !adb.isEmpty();
        return detailWaybillJustFilled || detailStableMatchesLocal;
    }

    private static String trimEmpty(String x) {
        return x == null ? "" : x.trim();
    }

    private static String str(Object o) {
        return o == null ? "null" : String.valueOf(o);
    }

    private static boolean eqStr(String x, String y) {
        String a = x == null ? "" : x.trim();
        String b = y == null ? "" : y.trim();
        return Objects.equals(a, b);
    }

    private static final class RowSnap {
        Integer orderStatus;
        Integer refundStatus;
        String detailWaybillNo;
        String detailExpressCode;
        String detailExpressName;
        String localWaybillNo;

        static RowSnap from(ErpGoofishOrder r) {
            RowSnap s = new RowSnap();
            if (r == null) {
                return s;
            }
            s.orderStatus = r.getOrderStatus();
            s.refundStatus = r.getRefundStatus();
            s.detailWaybillNo = r.getDetailWaybillNo();
            s.detailExpressCode = r.getDetailExpressCode();
            s.detailExpressName = r.getDetailExpressName();
            s.localWaybillNo = r.getLocalWaybillNo();
            return s;
        }
    }
}
