package com.erp.platformsync.service.orderpull.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.erp.platformsync.adapter.PlatformAdapter;
import com.erp.platformsync.adapter.PlatformAdapterFactory;
import com.erp.platformsync.dto.PullOrdersResponse;
import com.erp.platformsync.dto.orderpull.OrderPullConfigDTO;
import com.erp.platformsync.dto.orderpull.OrderPullConfigRequest;
import com.erp.platformsync.dto.orderpull.OrderPullLogDTO;
import com.erp.platformsync.dto.orderpull.OrderPullLogQueryRequest;
import com.erp.platformsync.entity.OrderPullConfig;
import com.erp.platformsync.entity.OrderPullLog;
import com.erp.platformsync.mapper.OrderPullConfigMapper;
import com.erp.platformsync.mapper.OrderPullLogMapper;
import com.erp.platformsync.service.orderpull.OrderPullService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 订单拉取服务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderPullServiceImpl implements OrderPullService {

    private final OrderPullConfigMapper configMapper;
    private final OrderPullLogMapper logMapper;
    private final PlatformAdapterFactory adapterFactory;

    private static final DateTimeFormatter DT_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME;

    // 平台名称映射
    private static final Map<String, String> PLATFORM_NAMES = Map.of(
            "taobao", "淘宝",
            "jd", "京东",
            "pinduoduo", "拼多多",
            "douyin", "抖音"
    );

    // 状态名称映射
    private static final Map<String, String> STATUS_NAMES = Map.of(
            "RUNNING", "运行中",
            "SUCCESS", "成功",
            "FAILED", "失败",
            "PARTIAL_SUCCESS", "部分成功",
            "STOPPED", "已停止",
            "ERROR", "异常"
    );

    // 触发方式名称映射
    private static final Map<String, String> TRIGGER_TYPE_NAMES = Map.of(
            "MANUAL", "手动",
            "TRIGGER", "触发",
            "SCHEDULED", "定时"
    );

    @Override
    public List<OrderPullConfigDTO> getConfigList() {
        LambdaQueryWrapper<OrderPullConfig> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByDesc(OrderPullConfig::getCreatedAt);
        List<OrderPullConfig> configs = configMapper.selectList(wrapper);
        return configs.stream().map(this::toConfigDTO).collect(Collectors.toList());
    }

    @Override
    public OrderPullConfigDTO getConfig(Long id) {
        OrderPullConfig config = configMapper.selectById(id);
        return config != null ? toConfigDTO(config) : null;
    }

    @Override
    @Transactional
    public OrderPullConfigDTO saveConfig(OrderPullConfigRequest request) {
        OrderPullConfig config;

        if (request.getId() != null) {
            // 更新
            config = configMapper.selectById(request.getId());
            if (config == null) {
                throw new RuntimeException("配置不存在");
            }
        } else {
            // 新增
            config = new OrderPullConfig();
            config.setStatus("STOPPED");
        }

        config.setName(request.getName());
        config.setPlatform(request.getPlatform());
        config.setShopId(request.getShopId());
        config.setShopName(request.getShopName());
        config.setPullType(request.getPullType());
        config.setPullInterval(request.getPullInterval());
        config.setCronExpression(request.getCronExpression());
        config.setEnabled(request.getEnabled());
        config.setRetryEnabled(request.getRetryEnabled());
        config.setMaxRetries(request.getMaxRetries());
        config.setRetryInterval(request.getRetryInterval());
        config.setRemark(request.getRemark());

        if (request.getId() != null) {
            configMapper.updateById(config);
        } else {
            configMapper.insert(config);
        }

        return toConfigDTO(config);
    }

    @Override
    @Transactional
    public boolean deleteConfig(Long id) {
        return configMapper.deleteById(id) > 0;
    }

    @Override
    @Transactional
    public OrderPullLogDTO triggerPull(Long id) {
        OrderPullConfig config = configMapper.selectById(id);
        if (config == null) {
            throw new RuntimeException("配置不存在");
        }
        return executePull(config, "TRIGGER");
    }

    @Override
    @Transactional
    public OrderPullLogDTO pullNow(OrderPullConfigRequest request) {
        OrderPullConfig config = new OrderPullConfig();
        config.setPlatform(request.getPlatform());
        config.setShopId(request.getShopId());
        config.setShopName(request.getShopName());
        config.setPullType(request.getPullType());
        config.setRetryEnabled(request.getRetryEnabled());
        config.setMaxRetries(request.getMaxRetries());
        return executePull(config, "MANUAL");
    }

    @Override
    public List<OrderPullLogDTO> getLogs(OrderPullLogQueryRequest queryRequest) {
        LambdaQueryWrapper<OrderPullLog> wrapper = new LambdaQueryWrapper<>();

        if (queryRequest.getConfigId() != null) {
            wrapper.eq(OrderPullLog::getConfigId, queryRequest.getConfigId());
        }
        if (queryRequest.getPlatform() != null && !queryRequest.getPlatform().isEmpty()) {
            wrapper.eq(OrderPullLog::getPlatform, queryRequest.getPlatform());
        }
        if (queryRequest.getShopId() != null) {
            wrapper.eq(OrderPullLog::getShopId, queryRequest.getShopId());
        }
        if (queryRequest.getStatus() != null && !queryRequest.getStatus().isEmpty()) {
            wrapper.eq(OrderPullLog::getStatus, queryRequest.getStatus());
        }
        if (queryRequest.getTriggerType() != null && !queryRequest.getTriggerType().isEmpty()) {
            wrapper.eq(OrderPullLog::getTriggerType, queryRequest.getTriggerType());
        }
        if (queryRequest.getStartTime() != null) {
            wrapper.ge(OrderPullLog::getCreatedAt, queryRequest.getStartTime());
        }
        if (queryRequest.getEndTime() != null) {
            wrapper.le(OrderPullLog::getCreatedAt, queryRequest.getEndTime());
        }

        wrapper.orderByDesc(OrderPullLog::getCreatedAt);

        int offset = (queryRequest.getPage() - 1) * queryRequest.getPageSize();
        wrapper.last("LIMIT " + offset + ", " + queryRequest.getPageSize());

        List<OrderPullLog> logs = logMapper.selectList(wrapper);
        return logs.stream().map(this::toLogDTO).collect(Collectors.toList());
    }

    @Override
    public List<OrderPullLogDTO> getLatestLogs(Long configId, int limit) {
        List<OrderPullLog> logs = logMapper.findLatestByConfigId(configId, limit);
        return logs.stream().map(this::toLogDTO).collect(Collectors.toList());
    }

    @Override
    @Transactional
    public boolean enableConfig(Long id) {
        OrderPullConfig config = configMapper.selectById(id);
        if (config == null) {
            return false;
        }
        config.setEnabled(true);
        config.setStatus("RUNNING");
        return configMapper.updateById(config) > 0;
    }

    @Override
    @Transactional
    public boolean disableConfig(Long id) {
        OrderPullConfig config = configMapper.selectById(id);
        if (config == null) {
            return false;
        }
        config.setEnabled(false);
        config.setStatus("STOPPED");
        return configMapper.updateById(config) > 0;
    }

    /**
     * 执行拉单
     */
    private OrderPullLogDTO executePull(OrderPullConfig config, String triggerType) {
        String platform = config.getPlatform().toLowerCase();
        Long shopId = config.getShopId();
        String pullType = config.getPullType();

        // 创建日志记录
        OrderPullLog logEntity = new OrderPullLog();
        logEntity.setConfigId(config.getId());
        logEntity.setConfigName(config.getName());
        logEntity.setPlatform(platform);
        logEntity.setShopId(shopId);
        logEntity.setPullType(pullType);
        logEntity.setTriggerType(triggerType);
        logEntity.setStatus("RUNNING");
        logEntity.setStartTime(LocalDateTime.now());
        logEntity.setRetryCount(0);
        logMapper.insert(logEntity);

        // 更新配置状态
        if (config.getId() != null) {
            configMapper.updateStatus(config.getId(), "RUNNING", LocalDateTime.now());
        }

        try {
            PlatformAdapter adapter = adapterFactory.getAdapter(platform);
            if (adapter == null) {
                throw new RuntimeException("不支持的平台: " + platform);
            }

            if (!adapter.isConfigured()) {
                throw new RuntimeException("平台未配置API凭证");
            }

            // 计算拉单时间范围
            LocalDateTime startTime = config.getLastSuccessTime();
            if (startTime == null) {
                startTime = LocalDateTime.now().minusHours(24);
            }
            LocalDateTime endTime = LocalDateTime.now();

            log.info("[订单拉取] 开始拉取: platform={}, shopId={}, pullType={}, startTime={}, endTime={}",
                    platform, shopId, pullType, startTime, endTime);

            // 执行拉单
            List<PullOrdersResponse.PullOrderItem> orders = adapter.pullOrders(
                    shopId, pullType, null, startTime, endTime);

            // 更新日志
            logEntity.setStatus("SUCCESS");
            logEntity.setEndTime(LocalDateTime.now());
            logEntity.setDuration(Duration.between(logEntity.getStartTime(), logEntity.getEndTime()).toMillis());
            logEntity.setOrderCount(orders != null ? orders.size() : 0);
            logEntity.setSuccessCount(orders != null ? orders.size() : 0);
            logEntity.setFailCount(0);
            logEntity.setResponseSummary("成功拉取 " + (orders != null ? orders.size() : 0) + " 条订单");
            logMapper.updateById(logEntity);

            // 更新配置
            if (config.getId() != null) {
                LocalDateTime now = LocalDateTime.now();
                LocalDateTime nextPullTime = calculateNextPullTime(config, now);
                configMapper.updatePullTime(config.getId(), now, nextPullTime, now);
                configMapper.updateLastSuccessTime(config.getId(), now, now);
                configMapper.updateStatus(config.getId(), "RUNNING", now);
            }

            log.info("[订单拉取] 拉取成功: platform={}, shopId={}, count={}", platform, shopId, orders != null ? orders.size() : 0);

        } catch (Exception e) {
            log.error("[订单拉取] 拉取失败: platform={}, shopId={}, error={}", platform, shopId, e.getMessage(), e);

            // 更新日志
            logEntity.setStatus("FAILED");
            logEntity.setEndTime(LocalDateTime.now());
            logEntity.setDuration(Duration.between(logEntity.getStartTime(), logEntity.getEndTime()).toMillis());
            logEntity.setErrorMsg(e.getMessage());
            logEntity.setErrorTrace(getErrorTrace(e));
            logMapper.updateById(logEntity);

            // 更新配置状态
            if (config.getId() != null) {
                configMapper.updateStatus(config.getId(), "ERROR", LocalDateTime.now());
            }

            // 重试逻辑
            if (Boolean.TRUE.equals(config.getRetryEnabled()) && config.getMaxRetries() != null && config.getMaxRetries() > 0) {
                handleRetry(config, logEntity, e);
            }
        }

        return toLogDTO(logEntity);
    }

    /**
     * 处理重试
     */
    private void handleRetry(OrderPullConfig config, OrderPullLog originalLog, Exception e) {
        int maxRetries = config.getMaxRetries() != null ? config.getMaxRetries() : 3;
        int retryInterval = config.getRetryInterval() != null ? config.getRetryInterval() : 5;

        for (int i = 1; i <= maxRetries; i++) {
            try {
                log.info("[订单拉取] 开始重试 {}/{}: configId={}", i, maxRetries, config.getId());

                // 等待间隔
                Thread.sleep(retryInterval * 60 * 1000L);

                // 创建重试日志
                OrderPullLog retryLog = new OrderPullLog();
                retryLog.setConfigId(config.getId());
                retryLog.setConfigName(config.getName());
                retryLog.setPlatform(config.getPlatform());
                retryLog.setShopId(config.getShopId());
                retryLog.setPullType(config.getPullType());
                retryLog.setTriggerType("TRIGGER");
                retryLog.setStatus("RUNNING");
                retryLog.setStartTime(LocalDateTime.now());
                retryLog.setRetryCount(i);
                logMapper.insert(retryLog);

                PlatformAdapter adapter = adapterFactory.getAdapter(config.getPlatform().toLowerCase());
                if (adapter == null || !adapter.isConfigured()) {
                    throw new RuntimeException("平台未配置或不支持");
                }

                LocalDateTime startTime = config.getLastSuccessTime() != null ? config.getLastSuccessTime() : LocalDateTime.now().minusHours(24);
                List<PullOrdersResponse.PullOrderItem> orders = adapter.pullOrders(
                        config.getShopId(), config.getPullType(), null, startTime, LocalDateTime.now());

                retryLog.setStatus("SUCCESS");
                retryLog.setEndTime(LocalDateTime.now());
                retryLog.setDuration(Duration.between(retryLog.getStartTime(), retryLog.getEndTime()).toMillis());
                retryLog.setOrderCount(orders != null ? orders.size() : 0);
                retryLog.setSuccessCount(orders != null ? orders.size() : 0);
                retryLog.setResponseSummary("重试成功，拉取 " + (orders != null ? orders.size() : 0) + " 条订单");
                logMapper.updateById(retryLog);

                // 更新原日志
                originalLog.setStatus("SUCCESS");
                originalLog.setRetryCount(i);
                logMapper.updateById(originalLog);

                // 更新配置
                if (config.getId() != null) {
                    LocalDateTime now = LocalDateTime.now();
                    LocalDateTime nextPullTime = calculateNextPullTime(config, now);
                    configMapper.updatePullTime(config.getId(), now, nextPullTime, now);
                    configMapper.updateLastSuccessTime(config.getId(), now, now);
                    configMapper.updateStatus(config.getId(), "RUNNING", now);
                }

                log.info("[订单拉取] 重试成功: configId={}, retryCount={}", config.getId(), i);
                return;

            } catch (Exception retryException) {
                log.error("[订单拉取] 重试失败 {}/{}: configId={}, error={}", i, maxRetries, config.getId(), retryException.getMessage());
            }
        }

        log.warn("[订单拉取] 重试次数用尽: configId={}, maxRetries={}", config.getId(), maxRetries);
    }

    /**
     * 计算下次拉取时间
     */
    private LocalDateTime calculateNextPullTime(OrderPullConfig config, LocalDateTime from) {
        if (config.getPullInterval() != null && config.getPullInterval() > 0) {
            return from.plusMinutes(config.getPullInterval());
        }
        // 默认1小时
        return from.plusHours(1);
    }

    /**
     * 获取错误堆栈
     */
    private String getErrorTrace(Exception e) {
        StringBuilder sb = new StringBuilder();
        for (StackTraceElement element : e.getStackTrace()) {
            sb.append(element.toString()).append("\n");
            if (sb.length() > 2000) {
                sb.append("... (truncated)");
                break;
            }
        }
        return sb.toString();
    }

    /**
     * 转换为配置DTO
     */
    private OrderPullConfigDTO toConfigDTO(OrderPullConfig config) {
        return OrderPullConfigDTO.builder()
                .id(config.getId())
                .name(config.getName())
                .platform(config.getPlatform())
                .platformName(PLATFORM_NAMES.getOrDefault(config.getPlatform(), config.getPlatform()))
                .shopId(config.getShopId())
                .shopName(config.getShopName())
                .pullType(config.getPullType())
                .pullInterval(config.getPullInterval())
                .cronExpression(config.getCronExpression())
                .enabled(config.getEnabled())
                .retryEnabled(config.getRetryEnabled())
                .maxRetries(config.getMaxRetries())
                .retryInterval(config.getRetryInterval())
                .status(config.getStatus())
                .lastPullTime(config.getLastPullTime())
                .nextPullTime(config.getNextPullTime())
                .lastSuccessTime(config.getLastSuccessTime())
                .remark(config.getRemark())
                .tenantId(config.getTenantId())
                .createdAt(config.getCreatedAt())
                .createdBy(config.getCreatedBy())
                .build();
    }

    /**
     * 转换为日志DTO
     */
    private OrderPullLogDTO toLogDTO(OrderPullLog log) {
        String durationDesc = "";
        if (log.getDuration() != null) {
            long ms = log.getDuration();
            if (ms < 1000) {
                durationDesc = ms + "ms";
            } else if (ms < 60000) {
                durationDesc = String.format("%.2fs", ms / 1000.0);
            } else {
                durationDesc = String.format("%.2fm", ms / 60000.0);
            }
        }

        return OrderPullLogDTO.builder()
                .id(log.getId())
                .configId(log.getConfigId())
                .configName(log.getConfigName())
                .platform(log.getPlatform())
                .platformName(PLATFORM_NAMES.getOrDefault(log.getPlatform(), log.getPlatform()))
                .shopId(log.getShopId())
                .pullType(log.getPullType())
                .triggerType(log.getTriggerType())
                .triggerTypeName(TRIGGER_TYPE_NAMES.getOrDefault(log.getTriggerType(), log.getTriggerType()))
                .status(log.getStatus())
                .statusName(STATUS_NAMES.getOrDefault(log.getStatus(), log.getStatus()))
                .startTime(log.getStartTime())
                .endTime(log.getEndTime())
                .duration(log.getDuration())
                .durationDesc(durationDesc)
                .orderCount(log.getOrderCount())
                .successCount(log.getSuccessCount())
                .failCount(log.getFailCount())
                .errorMsg(log.getErrorMsg())
                .retryCount(log.getRetryCount())
                .tenantId(log.getTenantId())
                .createdAt(log.getCreatedAt())
                .build();
    }
}
