package com.erp.waybill.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.erp.waybill.dto.*;
import com.erp.waybill.entity.*;
import com.erp.waybill.exception.WaybillException;
import com.erp.waybill.express.ExpressAdapter;
import com.erp.waybill.express.ExpressAdapterFactory;
import com.erp.waybill.repository.*;
import com.erp.waybill.service.WaybillService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * 运单服务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class WaybillServiceImpl implements WaybillService {

    private final WaybillRepository waybillRepository;
    private final WaybillStatusRepository waybillStatusRepository;
    private final LogisticsTraceRepository logisticsTraceRepository;
    private final PrintLogRepository printLogRepository;
    private final ExpressAdapterFactory expressAdapterFactory;

    // ==================== 运单管理 ====================

    @Override
    @Transactional
    public WaybillResponse applyWaybill(WaybillApplyRequest request, Long operatorId) {
        log.info("申请电子面单，订单ID: {}, 快递: {}", request.getOrderId(), request.getExpressCode());

        // 获取适配器
        ExpressAdapter adapter = expressAdapterFactory.getAdapter(request.getExpressCode());

        // 构建申请参数
        ExpressAdapter.ApplyParams params = buildApplyParams(request);

        // 调用快递API申请面单
        ExpressAdapter.ApplyResult result = adapter.applyWaybill(params);

        // 保存运单
        Waybill waybill = new Waybill();
        waybill.setOrderId(request.getOrderId());
        waybill.setExpressCode(result.getExpressCode());
        waybill.setWaybillNo(result.getWaybillNo());
        waybill.setPrintContent(result.getPrintContent());
        waybill.setStatus("unused");
        waybill.setCreatedAt(LocalDateTime.now());
        waybill.setUpdatedAt(LocalDateTime.now());
        waybillRepository.insert(waybill);

        // 记录打印日志
        logPrint(waybill.getOrderId(), "print", operatorId, null, waybill.getWaybillNo(), null);

        log.info("运单申请成功，运单号: {}", waybill.getWaybillNo());
        return toResponse(waybill);
    }

    @Override
    @Transactional
    public BatchApplyResult batchApplyWaybill(BatchApplyRequest request, Long operatorId) {
        log.info("批量申请电子面单，订单数: {}, 快递: {}", request.getOrderIds().size(), request.getExpressCode());

        List<WaybillResponse> success = new ArrayList<>();
        List<BatchApplyResult.BatchError> errors = new ArrayList<>();

        for (Long orderId : request.getOrderIds()) {
            try {
                WaybillApplyRequest applyRequest = WaybillApplyRequest.builder()
                        .orderId(orderId)
                        .expressCode(request.getExpressCode())
                        .build();
                WaybillResponse response = applyWaybill(applyRequest, operatorId);
                success.add(response);
            } catch (Exception e) {
                log.error("订单 {} 申请运单失败: {}", orderId, e.getMessage());
                errors.add(new BatchApplyResult.BatchError(orderId, e.getMessage()));
            }
        }

        return new BatchApplyResult(success, errors);
    }

    @Override
    public WaybillResponse getWaybill(Long waybillId) {
        Waybill waybill = waybillRepository.selectById(waybillId);
        if (waybill == null) {
            throw new WaybillException("运单不存在");
        }
        return toResponse(waybill);
    }

    @Override
    public WaybillResponse getWaybillByNo(String waybillNo) {
        Waybill waybill = waybillRepository.findByWaybillNo(waybillNo)
                .orElseThrow(() -> new WaybillException("运单不存在"));
        return toResponse(waybill);
    }

    @Override
    public List<WaybillResponse> getWaybillsByOrderId(Long orderId) {
        List<Waybill> waybills = waybillRepository.findByOrderId(orderId);
        return waybills.stream().map(this::toResponse).toList();
    }

    @Override
    @Transactional
    public WaybillResponse voidWaybill(WaybillVoidRequest request) {
        Waybill waybill = waybillRepository.selectById(request.getWaybillId());
        if (waybill == null) {
            throw new WaybillException("运单不存在");
        }

        waybill.markAsVoided(request.getReason());
        waybillRepository.updateById(waybill);

        log.info("运单 {} 已作废，原因: {}", waybill.getWaybillNo(), request.getReason());
        return toResponse(waybill);
    }

    // ==================== 打印管理 ====================

    @Override
    @Transactional
    public PrintResult printWaybill(Long waybillId, Long operatorId) {
        Waybill waybill = waybillRepository.selectById(waybillId);
        if (waybill == null) {
            throw new WaybillException("运单不存在");
        }

        if (!waybill.isUnused()) {
            throw new WaybillException("只有未使用的运单才能打印，当前状态: " + waybill.getStatus());
        }

        // 记录打印日志
        logPrint(waybill.getOrderId(), "print", operatorId, null, waybill.getWaybillNo(), null);

        return new PrintResult(waybill.getWaybillNo(), waybill.getPrintContent());
    }

    @Override
    @Transactional
    public PrintResult reprintWaybill(Long waybillId, Long operatorId) {
        Waybill waybill = waybillRepository.selectById(waybillId);
        if (waybill == null) {
            throw new WaybillException("运单不存在");
        }

        // 记录补打日志
        logPrint(waybill.getOrderId(), "reprint", operatorId, waybill.getWaybillNo(), waybill.getWaybillNo(), null);

        return new PrintResult(waybill.getWaybillNo(), waybill.getPrintContent());
    }

    @Override
    @Transactional
    public ChangeWaybillNoResult changeWaybillNo(Long waybillId, String reason, Long operatorId) {
        Waybill waybill = waybillRepository.selectById(waybillId);
        if (waybill == null) {
            throw new WaybillException("运单不存在");
        }

        // 获取适配器申请新单号
        ExpressAdapter adapter = expressAdapterFactory.getAdapter(waybill.getExpressCode());

        ExpressAdapter.ApplyParams params = ExpressAdapter.ApplyParams.builder()
                .orderId(waybill.getOrderId())
                .build();

        ExpressAdapter.ApplyResult result = adapter.applyWaybill(params);

        String oldWaybillNo = waybill.getWaybillNo();

        // 更新运单
        waybill.setWaybillNo(result.getWaybillNo());
        waybill.setPrintContent(result.getPrintContent());
        waybill.setStatus("unused");
        waybill.setUpdatedAt(LocalDateTime.now());
        waybillRepository.updateById(waybill);

        // 记录换单日志
        logPrint(waybill.getOrderId(), "change", operatorId, oldWaybillNo, result.getWaybillNo(), reason);

        log.info("运单换号成功，旧单号: {}, 新单号: {}", oldWaybillNo, result.getWaybillNo());
        return new ChangeWaybillNoResult(oldWaybillNo, result.getWaybillNo());
    }

    // ==================== 取消管理 ====================

    @Override
    @Transactional
    public boolean cancelWaybill(WaybillCancelRequest request) {
        Waybill waybill = waybillRepository.findByWaybillNo(request.getWaybillNo())
                .orElseThrow(() -> new WaybillException("运单不存在"));

        if (waybill.isUsed()) {
            throw new WaybillException("运单已使用，无法取消");
        }

        if (waybill.isCancelled() || waybill.isVoided()) {
            throw new WaybillException("运单已取消或作废，无法重复取消");
        }

        try {
            // 调用快递API取消
            ExpressAdapter adapter = expressAdapterFactory.getAdapter(waybill.getExpressCode());
            adapter.cancelWaybill(waybill.getWaybillNo());
        } catch (Exception e) {
            log.warn("快递API取消失败，标记为本系统取消: {}", e.getMessage());
        }

        waybill.markAsCancelled(request.getReason());
        waybillRepository.updateById(waybill);

        log.info("运单 {} 已取消，原因: {}", waybill.getWaybillNo(), request.getReason());
        return true;
    }

    // ==================== 状态跟踪 ====================

    @Override
    public WaybillStatus getTrackingStatus(String waybillNo) {
        return waybillStatusRepository.findByWaybillNo(waybillNo)
                .orElse(null);
    }

    @Override
    public List<LogisticsTrace> getLogisticsTraces(String waybillNo) {
        return logisticsTraceRepository.findByWaybillNo(waybillNo);
    }

    @Override
    @Transactional
    public WaybillStatus syncTrackingStatus(String waybillNo) {
        Waybill waybill = waybillRepository.findByWaybillNo(waybillNo)
                .orElseThrow(() -> new WaybillException("运单不存在"));

        ExpressAdapter adapter = expressAdapterFactory.getAdapter(waybill.getExpressCode());
        ExpressAdapter.TrackResult trackResult = adapter.queryStatus(waybillNo);

        WaybillStatus status = waybillStatusRepository.findByWaybillNo(waybillNo)
                .orElse(new WaybillStatus());

        status.setWaybillNo(waybillNo);
        status.setCarrier(waybill.getExpressCode());
        status.setStatus(trackResult.getStatus());
        status.setLocation(trackResult.getLocation());
        status.setDescription(trackResult.getDescription());
        status.setLastTraceTime(LocalDateTime.now());

        if (trackResult.getSignedTime() != null) {
            status.setSignedTime(LocalDateTime.parse(trackResult.getSignedTime()));
        }
        if (trackResult.getReceiverName() != null) {
            status.setReceiverName(trackResult.getReceiverName());
        }
        if (trackResult.getReceiverPhone() != null) {
            status.setReceiverPhone(trackResult.getReceiverPhone());
        }

        if (status.getId() == null) {
            waybillStatusRepository.insert(status);
        } else {
            waybillStatusRepository.updateById(status);
        }

        return status;
    }

    @Override
    @Transactional
    public int batchSyncTrackingStatus(int limit) {
        LocalDateTime threshold = LocalDateTime.now().minusMinutes(30);
        List<WaybillStatus> pending = waybillStatusRepository.findNeedSync(threshold);

        int synced = 0;
        for (WaybillStatus status : pending) {
            if (synced >= limit) break;
            try {
                syncTrackingStatus(status.getWaybillNo());
                synced++;
            } catch (Exception e) {
                log.error("同步运单 {} 状态失败: {}", status.getWaybillNo(), e.getMessage());
            }
        }

        return synced;
    }

    // ==================== 打印日志 ====================

    @Override
    public List<PrintLog> getPrintLogs(Long orderId) {
        return printLogRepository.findByOrderId(orderId);
    }

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

    private ExpressAdapter.ApplyParams buildApplyParams(WaybillApplyRequest request) {
        return ExpressAdapter.ApplyParams.builder()
                .orderId(request.getOrderId())
                .receiverName(request.getReceiver() != null ? request.getReceiver().getName() : null)
                .receiverPhone(request.getReceiver() != null ? request.getReceiver().getPhone() : null)
                .receiverAddress(request.getReceiver() != null ? request.getReceiver().getAddress() : null)
                .senderName(request.getSender() != null ? request.getSender().getName() : null)
                .senderPhone(request.getSender() != null ? request.getSender().getPhone() : null)
                .senderAddress(request.getSender() != null ? request.getSender().getAddress() : null)
                .build();
    }

    private void logPrint(Long orderId, String action, Long operatorId, String oldWaybillNo, String newWaybillNo, String reason) {
        PrintLog printLog = new PrintLog();
        printLog.setOrderId(orderId);
        printLog.setAction(action);
        printLog.setOperatorId(operatorId);
        printLog.setOldWaybillNo(oldWaybillNo);
        printLog.setNewWaybillNo(newWaybillNo);
        printLog.setReason(reason);
        printLog.setCreatedAt(LocalDateTime.now());
        printLogRepository.insert(printLog);
    }

    private WaybillResponse toResponse(Waybill waybill) {
        WaybillResponse.WaybillStatusResponse trackingResponse = null;
        Optional<WaybillStatus> statusOpt = waybillStatusRepository.findByWaybillNo(waybill.getWaybillNo());
        if (statusOpt.isPresent()) {
            WaybillStatus status = statusOpt.get();
            trackingResponse = WaybillResponse.WaybillStatusResponse.builder()
                    .status(status.getStatus())
                    .statusLabel(status.getStatusLabel())
                    .location(status.getLocation())
                    .description(status.getDescription())
                    .lastTraceTime(status.getLastTraceTime())
                    .signedTime(status.getSignedTime())
                    .receiverName(status.getReceiverName())
                    .receiverPhone(status.getReceiverPhone())
                    .build();
        }

        return WaybillResponse.builder()
                .id(waybill.getId())
                .orderId(waybill.getOrderId())
                .expressCode(waybill.getExpressCode())
                .waybillNo(waybill.getWaybillNo())
                .printContent(waybill.getPrintContent())
                .status(waybill.getStatus())
                .statusLabel(getStatusLabel(waybill.getStatus()))
                .usedAt(waybill.getUsedAt())
                .cancelledAt(waybill.getCancelledAt())
                .cancelReason(waybill.getCancelReason())
                .voidedAt(waybill.getVoidedAt())
                .voidReason(waybill.getVoidReason())
                .tenantId(waybill.getTenantId())
                .createdAt(waybill.getCreatedAt())
                .updatedAt(waybill.getUpdatedAt())
                .tracking(trackingResponse)
                .build();
    }

    private String getStatusLabel(String status) {
        return switch (status) {
            case "unused" -> "未使用";
            case "used" -> "已使用";
            case "cancelled" -> "已取消";
            case "void" -> "已作废";
            default -> status;
        };
    }
}
