package com.erp.logistics.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.erp.logistics.entity.LogisticsTrace;
import com.erp.logistics.entity.WaybillStatus;
import com.erp.logistics.mapper.LogisticsTraceMapper;
import com.erp.logistics.mapper.WaybillStatusMapper;
import com.erp.logistics.service.adapter.CarrierAdapter;
import com.erp.logistics.service.adapter.CarrierAdapterFactory;
import com.erp.logistics.service.impl.LogisticsIdempotencyService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

/**
 * 物流轨迹同步服务
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TraceSyncService {

    private final WaybillStatusMapper waybillStatusMapper;
    private final LogisticsTraceMapper logisticsTraceMapper;
    private final CarrierAdapterFactory carrierAdapterFactory;
    private final LogisticsIdempotencyService idempotencyService;

    @Value("${logistics.sync.batch-size:100}")
    private Integer batchSize;

    @Value("${logistics.sync.interval-minutes:30}")
    private Integer intervalMinutes;

    @Value("${logistics.sync.max-retry:3}")
    private Integer maxRetry;

    // ==================== 轨迹同步 ====================

    /**
     * 同步单条运单轨迹
     */
    @Transactional
    @Retryable(maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 2))
    public boolean syncSingle(String waybillNo, String carrierCode, boolean forceSync) {
        log.info("[轨迹同步] 开始同步, 运单号: {}, 强制: {}", waybillNo, forceSync);

        try {
            // 1. 确定物流商
            if (carrierCode == null || carrierCode.isEmpty()) {
                CarrierAdapter adapter = carrierAdapterFactory.match(waybillNo);
                if (adapter == null) {
                    log.warn("[轨迹同步] 无法识别物流商: {}", waybillNo);
                    return false;
                }
                carrierCode = adapter.getCarrierCode();
            }

            // 2. 查询数据库中的运单状态
            WaybillStatus waybillStatus = waybillStatusMapper.selectByWaybillNo(waybillNo);
            if (waybillStatus == null) {
                // 创建新运单记录
                waybillStatus = new WaybillStatus()
                        .setWaybillNo(waybillNo)
                        .setCarrier(carrierCode)
                        .setStatus("IN_TRANSIT")
                        .setStatusLabel("运输中")
                        .setNeedSync(1)
                        .setSyncStatus(0)
                        .setSyncRetryCount(0)
                        .setCreatedAt(LocalDateTime.now())
                        .setUpdatedAt(LocalDateTime.now());
                waybillStatusMapper.insert(waybillStatus);
            }

            // 3. 更新同步状态为同步中
            waybillStatusMapper.updateSyncStatus(waybillStatus.getId(), 1, waybillStatus.getSyncRetryCount(), null);

            // 4. 调用适配器查询轨迹
            CarrierAdapter adapter = carrierAdapterFactory.getAdapter(carrierCode);
            List<LogisticsTrace> traces = adapter.queryTraces(waybillNo);

            if (traces.isEmpty()) {
                log.info("[轨迹同步] 无新轨迹, 运单号: {}", waybillNo);
                waybillStatusMapper.updateSyncStatus(waybillStatus.getId(), 2, waybillStatus.getSyncRetryCount(), null);
                return true;
            }

            // 5. 保存轨迹
            int savedCount = saveTraces(traces, waybillNo, carrierCode);

            // 6. 更新运单状态
            LogisticsTrace latestTrace = traces.get(traces.size() - 1);
            WaybillStatus update = new WaybillStatus()
                    .setId(waybillStatus.getId())
                    .setStatus(latestTrace.getStatus())
                    .setStatusLabel(latestTrace.getStatusLabel())
                    .setLocation(latestTrace.getLocation())
                    .setDescription(latestTrace.getDescription())
                    .setLastTraceTime(latestTrace.getTraceTime())
                    .setSyncStatus(2)
                    .setSyncRetryCount(0)
                    .setNeedSync(0)
                    .setUpdatedAt(LocalDateTime.now());

            if ("SIGNED".equals(latestTrace.getStatus())) {
                update.setSignedTime(latestTrace.getTraceTime());
            }

            waybillStatusMapper.updateById(update);

            log.info("[轨迹同步] 同步完成, 运单号: {}, 新增轨迹: {} 条", waybillNo, savedCount);
            return true;

        } catch (Exception e) {
            log.error("[轨迹同步] 同步异常, 运单号: {}", waybillNo, e);
            handleSyncFailure(waybillNo, e.getMessage());
            return false;
        }
    }

    /**
     * 批量同步待处理运单
     */
    @Scheduled(fixedDelayString = "${logistics.sync.interval-minutes:30}000")
    @Transactional
    public void syncPending() {
        log.info("[轨迹同步] 开始批量同步待处理运单");

        LocalDateTime threshold = LocalDateTime.now().minusMinutes(intervalMinutes);
        List<WaybillStatus> pendingList = waybillStatusMapper.selectPendingSync(maxRetry, threshold, batchSize);

        if (pendingList.isEmpty()) {
            log.info("[轨迹同步] 无待同步运单");
            return;
        }

        log.info("[轨迹同步] 发现 {} 条待同步运单", pendingList.size());

        int successCount = 0;
        int failCount = 0;

        for (WaybillStatus waybill : pendingList) {
            boolean success = syncSingle(waybill.getWaybillNo(), waybill.getCarrier(), false);
            if (success) {
                successCount++;
            } else {
                failCount++;
            }
        }

        log.info("[轨迹同步] 批量同步完成, 成功: {}, 失败: {}", successCount, failCount);
    }

    /**
     * 异步批量同步（可手动触发）
     */
    @Async
    public void syncPendingAsync() {
        syncPending();
    }

    // ==================== 回调处理 ====================

    /**
     * 处理物流回调
     * <p>
     * 幂等性保证：使用 carrierCode + waybillNo + status + latestTraceTime 作为幂等键
     * 同一运单同状态的回调在24小时内不重复处理
     */
    @Transactional
    public boolean processCallback(String carrierCode, java.util.Map<String, Object> callbackData) {
        log.info("[回调处理] 收到回调, 物流商: {}, 数据: {}", carrierCode, callbackData);

        try {
            // 1. 验证签名
            CarrierAdapter adapter = carrierAdapterFactory.getAdapter(carrierCode);
            String sign = callbackData.get("sign") != null ? callbackData.get("sign").toString() : null;

            if (!adapter.verifySign(callbackData, sign)) {
                log.warn("[回调处理] 签名验证失败, 物流商: {}", carrierCode);
                // 签名验证失败，但为了兼容性仍然处理
            }

            // 2. 解析回调数据
            CarrierAdapter.CallbackResult result = adapter.parseCallback(callbackData);

            if (result == null || result.waybillNo() == null) {
                log.warn("[回调处理] 解析结果为空或运单号为空");
                return false;
            }

            // ========== 幂等性检查 ==========
            // 以最新轨迹时间和状态构建幂等键
            String latestTraceTime = result.traces().isEmpty() ? "now" :
                    (result.traces().get(result.traces().size() - 1).getTraceTime() != null ?
                            result.traces().get(result.traces().size() - 1).getTraceTime().toString() : "now");
            String idempotencyKey = idempotencyService.buildCallbackKey(
                    carrierCode, result.waybillNo(),
                    result.status() != null ? result.status().getStatus() : "unknown",
                    latestTraceTime);

            if (idempotencyService.isDuplicate(idempotencyKey)) {
                log.warn("[回调处理] 重复回调，已跳过: key={}", idempotencyKey);
                return true; // 重复回调返回成功
            }

            // 3. 保存或更新运单
            WaybillStatus waybill = result.status();
            WaybillStatus exist = waybillStatusMapper.selectByWaybillNo(result.waybillNo());

            if (exist != null) {
                waybill.setId(exist.getId());
                waybill.setCreatedAt(exist.getCreatedAt());
                waybill.setUpdatedAt(LocalDateTime.now());
                waybillStatusMapper.updateById(waybill);
            } else {
                waybill.setNeedSync(0);
                waybill.setSyncStatus(2);
                waybill.setCreatedAt(LocalDateTime.now());
                waybill.setUpdatedAt(LocalDateTime.now());
                waybillStatusMapper.insert(waybill);
            }

            // 4. 保存轨迹（数据库层去重）
            for (LogisticsTrace trace : result.traces()) {
                int existCount = logisticsTraceMapper.countExist(
                        trace.getWaybillNo(),
                        trace.getTraceTime(),
                        trace.getLocation()
                );
                if (existCount == 0) {
                    logisticsTraceMapper.insert(trace);
                }
            }

            // 标记幂等
            idempotencyService.markProcessed(idempotencyKey, result.waybillNo());

            log.info("[回调处理] 处理成功, 运单号: {}", result.waybillNo());
            return true;

        } catch (Exception e) {
            log.error("[回调处理] 处理异常, 物流商: {}", carrierCode, e);
            return false;
        }
    }

    // ==================== 私有方法 ====================

    /**
     * 保存轨迹（去重）
     */
    private int saveTraces(List<LogisticsTrace> traces, String waybillNo, String carrierCode) {
        int count = 0;
        for (LogisticsTrace trace : traces) {
            trace.setWaybillNo(waybillNo);
            trace.setCarrier(carrierCode);

            int existCount = logisticsTraceMapper.countExist(
                    waybillNo,
                    trace.getTraceTime(),
                    trace.getLocation()
            );

            if (existCount == 0) {
                logisticsTraceMapper.insert(trace);
                count++;
            }
        }
        return count;
    }

    /**
     * 处理同步失败
     */
    private void handleSyncFailure(String waybillNo, String reason) {
        try {
            WaybillStatus waybill = waybillStatusMapper.selectByWaybillNo(waybillNo);
            if (waybill != null) {
                int newRetryCount = (waybill.getSyncRetryCount() == null ? 0 : waybill.getSyncRetryCount()) + 1;
                waybillStatusMapper.updateSyncStatus(
                        waybill.getId(),
                        3,  // 同步失败
                        newRetryCount,
                        reason
                );
            }
        } catch (Exception e) {
            log.error("[轨迹同步] 更新失败状态异常: {}", waybillNo, e);
        }
    }
}
