package com.haha.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.haha.common.constant.CommonConstants;
import com.haha.common.constant.DeviceConstants;
import com.haha.common.constant.RedisConstants;
import com.haha.common.constant.SyncConstants;
import com.haha.common.enums.DeviceOnlineStatus;
import com.haha.common.exception.BusinessException;
import com.haha.entity.Device;
import com.haha.entity.Product;
import com.haha.entity.SyncLog;
import com.haha.entity.SyncRecord;
import com.haha.mapper.SyncLogMapper;
import com.haha.mapper.SyncRecordMapper;
import com.haha.sdk.HahaClient;
import com.haha.sdk.exception.HahaException;
import com.haha.sdk.model.DeviceInfo;
import com.haha.service.DataSyncService;
import com.haha.service.DeviceService;
import com.haha.service.ProductService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 数据同步服务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DataSyncServiceImpl extends ServiceImpl<SyncRecordMapper, SyncRecord> implements DataSyncService {

    private final HahaClient hahaClient;
    private final DeviceService deviceService;
    private final ProductService productService;
    private final SyncLogMapper syncLogMapper;
    private final StringRedisTemplate stringRedisTemplate;

    /**
     * 同步类型
     */
    private static final String SYNC_TYPE_DEVICE = "DEVICE";
    private static final String SYNC_TYPE_PRODUCT = "PRODUCT";

    @Async("taskExecutor")
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncDevices(Long operatorId, String operatorName) {
        log.info("开始同步设备数据，操作人: {}", operatorName);

        // 1. 创建同步记录
        SyncRecord record = new SyncRecord();
        record.setSyncType(SYNC_TYPE_DEVICE);
        record.setStatus(SyncConstants.STATUS_SYNCING);
        record.setStartTime(LocalDateTime.now());
        record.setOperatorId(operatorId);
        record.setOperatorName(operatorName);
        record.setCreateTime(LocalDateTime.now());
        record.setTotalCount(0);
        record.setSuccessCount(0);
        record.setFailCount(0);
        save(record);

        int successCount = 0;
        int failCount = 0;
        StringBuilder errorMsg = new StringBuilder();

        try {
            // 2. 从哈哈平台获取设备列表
            List<DeviceInfo> deviceList = hahaClient.getDeviceApi().getDeviceList(1, 100);
            record.setTotalCount(deviceList.size());
            log.info("从哈哈平台获取到 {} 台设备", deviceList.size());

            // 3. 批量同步设备到本地数据库
            List<String> sns = deviceList.stream().map(DeviceInfo::getId).filter(Objects::nonNull).toList();

            // 3.1 批量查询已有设备（1 次 SELECT IN）
            Map<String, Device> existingBySn = new HashMap<>();
            if (!sns.isEmpty()) {
                List<Device> existingDevices = deviceService.lambdaQuery()
                        .in(Device::getDeviceId, sns).list();
                for (Device d : existingDevices) {
                    existingBySn.put(d.getDeviceId(), d);
                }
            }

            // 3.2 分类新建/更新，批量操作
            List<Device> toInsert = new ArrayList<>();
            List<Device> toUpdate = new ArrayList<>();
            List<SyncLog> syncLogs = new ArrayList<>();

            for (DeviceInfo deviceInfo : deviceList) {
                try {
                    Device existDevice = existingBySn.get(deviceInfo.getId());
                    if (existDevice != null) {
                        existDevice.setName(deviceInfo.getName());
                        existDevice.setAddress(deviceInfo.getAddress());
                        existDevice.setStatus("1".equals(deviceInfo.getStatus()) ? DeviceConstants.STATUS_ONLINE : DeviceOnlineStatus.OFFLINE.getCode());
                        toUpdate.add(existDevice);
                        syncLogs.add(buildSyncLog(record.getId(), SYNC_TYPE_DEVICE, deviceInfo.getId(),
                                deviceInfo.getName(), 1, "设备信息更新成功", null));
                    } else {
                        Device newDevice = new Device();
                        newDevice.setDeviceId(deviceInfo.getId());
                        newDevice.setName(deviceInfo.getName());
                        newDevice.setAddress(deviceInfo.getAddress());
                        newDevice.setStatus("1".equals(deviceInfo.getStatus()) ? DeviceConstants.STATUS_ONLINE : DeviceOnlineStatus.OFFLINE.getCode());
                        toInsert.add(newDevice);
                        // 先加入列表，saveBatch 后 ID 会自动回填
                        syncLogs.add(buildSyncLog(record.getId(), SYNC_TYPE_DEVICE, deviceInfo.getId(),
                                deviceInfo.getName(), 1, "设备新增成功", null));
                    }
                    successCount++;
                } catch (Exception e) {
                    failCount++;
                    String errMsg = String.format("设备 %s 同步失败: %s", deviceInfo.getId(), e.getMessage());
                    errorMsg.append(errMsg).append("; ");
                    log.error(errMsg, e);
                    syncLogs.add(buildSyncLog(record.getId(), SYNC_TYPE_DEVICE, deviceInfo.getId(),
                            deviceInfo.getName(), 0, errMsg, JSON.toJSONString(deviceInfo)));
                }
            }

            // 3.3 批量保存/更新（各 1 次批量操作）
            if (!toInsert.isEmpty()) {
                deviceService.saveBatch(toInsert);
            }
            if (!toUpdate.isEmpty()) {
                deviceService.updateBatchById(toUpdate);
            }
            // 3.4 批量保存同步日志
            saveSyncLogBatch(syncLogs);

            // 4. 更新同步记录
            record.setSuccessCount(successCount);
            record.setFailCount(failCount);
            record.setStatus(failCount == 0 ? SyncConstants.STATUS_SYNCED : (successCount > 0 ? SyncConstants.STATUS_SYNCED : SyncConstants.STATUS_SYNC_FAILED));
            record.setEndTime(LocalDateTime.now());
            record.setDuration(java.time.Duration.between(record.getStartTime(), record.getEndTime()).toMillis());
            if (errorMsg.length() > 0) {
                record.setErrorMessage(errorMsg.toString());
            }
            updateById(record);

            log.info("设备数据同步完成，成功: {}, 失败: {}", successCount, failCount);

        } catch (HahaException e) {
            log.error("调用哈哈平台API失败", e);
            record.setStatus(SyncConstants.STATUS_SYNC_FAILED);
            record.setEndTime(LocalDateTime.now());
            record.setDuration(java.time.Duration.between(record.getStartTime(), record.getEndTime()).toMillis());
            record.setErrorMessage("调用哈哈平台API失败: " + e.getMessage());
            updateById(record);
        } catch (Exception e) {
            log.error("设备数据同步异常", e);
            record.setStatus(SyncConstants.STATUS_SYNC_FAILED);
            record.setEndTime(LocalDateTime.now());
            record.setDuration(java.time.Duration.between(record.getStartTime(), record.getEndTime()).toMillis());
            record.setErrorMessage("同步异常: " + e.getMessage());
            updateById(record);
        }
    }

    /**
     * 保存同步日志
     */
    private SyncLog buildSyncLog(Long recordId, String syncType, String dataId,
                                  String dataName, Integer status, String message, String detail) {
        SyncLog syncLog = new SyncLog();
        syncLog.setRecordId(recordId);
        syncLog.setSyncType(syncType);
        syncLog.setDataId(dataId);
        syncLog.setDataName(dataName);
        syncLog.setStatus(status);
        syncLog.setMessage(message);
        syncLog.setDetail(detail);
        syncLog.setCreateTime(LocalDateTime.now());
        return syncLog;
    }

    private void saveSyncLogBatch(List<SyncLog> logs) {
        if (logs != null && !logs.isEmpty()) {
            for (SyncLog log : logs) {
                syncLogMapper.insert(log);
            }
        }
    }

    @Override
    public Map<String, Object> getSyncRecordList(String syncType, Integer status, Integer page, Integer pageSize) {
        LambdaQueryWrapper<SyncRecord> wrapper = new LambdaQueryWrapper<>();
        if (syncType != null && !syncType.isEmpty()) {
            wrapper.eq(SyncRecord::getSyncType, syncType);
        }
        if (status != null) {
            wrapper.eq(SyncRecord::getStatus, status);
        }
        wrapper.orderByDesc(SyncRecord::getCreateTime);

        IPage<SyncRecord> pageResult = page(new Page<>(page, pageSize), wrapper);

        Map<String, Object> result = new HashMap<>();
        result.put("list", pageResult.getRecords());
        result.put("total", pageResult.getTotal());
        result.put("page", page);
        result.put("pageSize", pageSize);
        return result;
    }

    @Override
    public Map<String, Object> getSyncLogList(Long recordId, Integer page, Integer pageSize) {
        LambdaQueryWrapper<SyncLog> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SyncLog::getRecordId, recordId);
        wrapper.orderByDesc(SyncLog::getCreateTime);

        IPage<SyncLog> pageResult = syncLogMapper.selectPage(new Page<>(page, pageSize), wrapper);

        Map<String, Object> result = new HashMap<>();
        result.put("list", pageResult.getRecords());
        result.put("total", pageResult.getTotal());
        result.put("page", page);
        result.put("pageSize", pageSize);
        return result;
    }

    @Override
    public Map<String, Object> getSyncStatistics() {
        Map<String, Object> stats = new HashMap<>();

        // 总同步次数
        long totalRecords = count();
        stats.put("totalRecords", totalRecords);

        // 成功次数
        long successRecords = lambdaQuery().eq(SyncRecord::getStatus, SyncConstants.STATUS_SYNCED).count();
        stats.put("successRecords", successRecords);

        // 失败次数
        long failedRecords = lambdaQuery().eq(SyncRecord::getStatus, SyncConstants.STATUS_SYNC_FAILED).count();
        stats.put("failedRecords", failedRecords);

        // 设备同步统计
        long deviceSyncCount = lambdaQuery().eq(SyncRecord::getSyncType, SYNC_TYPE_DEVICE).count();
        stats.put("deviceSyncCount", deviceSyncCount);

        // 最近一次同步记录
        SyncRecord latestRecord = lambdaQuery()
                .orderByDesc(SyncRecord::getCreateTime)
                .last("LIMIT 1")
                .one();
        stats.put("latestRecord", latestRecord);

        return stats;
    }

    @Async("taskExecutor")
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncProducts(Long operatorId, String operatorName) {
        log.info("开始同步商品数据，操作人: {}", operatorName);

        // 1. 创建同步记录
        SyncRecord record = new SyncRecord();
        record.setSyncType(SYNC_TYPE_PRODUCT);
        record.setStatus(SyncConstants.STATUS_SYNCING);
        record.setStartTime(LocalDateTime.now());
        record.setOperatorId(operatorId);
        record.setOperatorName(operatorName);
        record.setCreateTime(LocalDateTime.now());
        record.setTotalCount(0);
        record.setSuccessCount(0);
        record.setFailCount(0);
        save(record);

        int successCount = 0;
        int failCount = 0;
        int totalCount = 0;
        int deletedCount = 0;
        StringBuilder errorMsg = new StringBuilder();
        java.util.Set<String> hahaCodes = new java.util.HashSet<>();

        try {
            // 2. 分页从哈哈平台商家商品库获取商品列表
            int pageNo = 1;
            int pageSize = 50;
            boolean hasMore = true;

            while (hasMore) {
                // 调用哈哈平台API获取商家商品库列表
                Map<String, Object> result = hahaClient.getGoodsApi().getMerchantList(pageNo, pageSize, null, null, null, null);

                if (result == null || !result.containsKey("listData")) {
                    log.warn("获取商品列表返回数据为空");
                    break;
                }

                List<Map<String, Object>> listData = (List<Map<String, Object>>) result.get("listData");
                if (listData == null || listData.isEmpty()) {
                    hasMore = false;
                    break;
                }

                // 3. 批量同步本页商品到本地数据库
                for (Map<String, Object> productData : listData) {
                    totalCount++;
                    String code = productData.get("code") != null ? productData.get("code").toString() : null;
                    if (code != null) {
                        hahaCodes.add(code);
                    }
                }

                // 3.1 收集本页所有商品编码，批量查询已有商品（1 次 SELECT IN）
                List<String> pageCodes = listData.stream()
                        .map(p -> p.get("code") != null ? p.get("code").toString() : null)
                        .filter(Objects::nonNull).toList();
                Map<String, Product> existingByCode = new HashMap<>();
                if (!pageCodes.isEmpty()) {
                    List<Product> existingProducts = productService.listByCodes(pageCodes);
                    for (Product p : existingProducts) {
                        existingByCode.put(p.getCode(), p);
                    }
                }

                // 3.2 分类新建/更新
                List<Product> toInsert = new ArrayList<>();
                List<Product> toUpdate = new ArrayList<>();
                List<SyncLog> pageSyncLogs = new ArrayList<>();

                for (Map<String, Object> productData : listData) {
                    String code = productData.get("code") != null ? productData.get("code").toString() : null;
                    String name = productData.get("name") != null ? productData.get("name").toString() : "";
                    try {
                        if (code == null || code.isEmpty()) {
                            throw new BusinessException(400, "商品编码为空");
                        }

                        Product existProduct = existingByCode.get(code);
                        if (existProduct != null) {
                            existProduct.setName(name);
                            existProduct.setBarcode(productData.get("bar_code") != null ? productData.get("bar_code").toString() : null);
                            existProduct.setType(productData.get("type") != null ? productData.get("type").toString() : null);
                            existProduct.setPic(productData.get("pic") != null ? productData.get("pic").toString() : null);
                            existProduct.setPlanogram(productData.get("planogram") != null ? productData.get("planogram").toString() : null);
                            existProduct.setApplyStatus(productData.get("apply_status") != null ? productData.get("apply_status").toString() : null);
                            existProduct.setCostPrice(parseDouble(productData.get("cost_price")));
                            existProduct.setRetailPrice(parseDouble(productData.get("cprice")));
                            existProduct.setCustomerId(productData.get("customer_id") != null ? productData.get("customer_id").toString() : null);
                            existProduct.setSyncStatus(SyncConstants.STATUS_SYNCED);
                            existProduct.setSyncTime(LocalDateTime.now());
                            existProduct.setUpdateTime(LocalDateTime.now());
                            toUpdate.add(existProduct);
                            pageSyncLogs.add(buildSyncLog(record.getId(), SYNC_TYPE_PRODUCT, code, name, 1, "商品信息更新成功", null));
                        } else {
                            Product newProduct = new Product();
                            newProduct.setProductId(productData.get("product_id") != null ? productData.get("product_id").toString() : null);
                            newProduct.setCode(code);
                            newProduct.setName(name);
                            newProduct.setBarcode(productData.get("bar_code") != null ? productData.get("bar_code").toString() : null);
                            newProduct.setType(productData.get("type") != null ? productData.get("type").toString() : null);
                            newProduct.setPic(productData.get("pic") != null ? productData.get("pic").toString() : null);
                            newProduct.setPlanogram(productData.get("planogram") != null ? productData.get("planogram").toString() : null);
                            newProduct.setApplyStatus(productData.get("apply_status") != null ? productData.get("apply_status").toString() : null);
                            newProduct.setCostPrice(parseDouble(productData.get("cost_price")));
                            newProduct.setRetailPrice(parseDouble(productData.get("cprice")));
                            newProduct.setCustomerId(productData.get("customer_id") != null ? productData.get("customer_id").toString() : null);
                            newProduct.setSyncStatus(SyncConstants.STATUS_SYNCED);
                            newProduct.setSyncTime(LocalDateTime.now());
                            newProduct.setIsDeleted(CommonConstants.NOT_DELETED);
                            newProduct.setCreateTime(LocalDateTime.now());
                            newProduct.setUpdateTime(LocalDateTime.now());
                            toInsert.add(newProduct);
                            pageSyncLogs.add(buildSyncLog(record.getId(), SYNC_TYPE_PRODUCT, code, name, 1, "商品新增成功", null));
                        }
                        successCount++;
                    } catch (Exception e) {
                        failCount++;
                        String errMsg = String.format("商品 %s(%s) 同步失败: %s", code != null ? code : "unknown", name, e.getMessage());
                        errorMsg.append(errMsg).append("; ");
                        log.error(errMsg, e);
                        pageSyncLogs.add(buildSyncLog(record.getId(), SYNC_TYPE_PRODUCT, code != null ? code : "unknown", name, 0, errMsg, JSON.toJSONString(productData)));
                    }
                }

                // 3.3 批量保存/更新本页商品（各 1 次批量操作）
                if (!toInsert.isEmpty()) {
                    productService.saveBatch(toInsert);
                }
                if (!toUpdate.isEmpty()) {
                    productService.updateBatchById(toUpdate);
                }
                saveSyncLogBatch(pageSyncLogs);

                // 检查是否还有更多数据
                Object totals = result.get("totals");
                int total = totals != null ? Integer.parseInt(totals.toString()) : 0;
                if (pageNo * pageSize >= total) {
                    hasMore = false;
                } else {
                    pageNo++;
                }
            }

            // 4. 清理哈哈中已删除的商品：标记本地已同步但哈哈中不存在的商品为删除
            if (totalCount > 0 && !hahaCodes.isEmpty()) {
                List<Product> orphanProducts = productService.lambdaQuery()
                        .eq(Product::getIsDeleted, CommonConstants.NOT_DELETED)
                        .eq(Product::getSyncStatus, SyncConstants.STATUS_SYNCED)
                        .notIn(Product::getCode, hahaCodes)
                        .list();

                if (!orphanProducts.isEmpty()) {
                    deletedCount = orphanProducts.size();
                    List<Long> orphanIds = orphanProducts.stream().map(Product::getId).toList();
                    productService.lambdaUpdate()
                            .set(Product::getIsDeleted, CommonConstants.DELETED)
                            .set(Product::getUpdateTime, LocalDateTime.now())
                            .in(Product::getId, orphanIds)
                            .update();

                    log.info("标记已删除商品: {} 条, codes={}",
                            deletedCount,
                            orphanProducts.stream().map(Product::getCode).limit(20).toList());
                }
            }

            // 5. 更新同步记录
            record.setTotalCount(totalCount);
            record.setSuccessCount(successCount);
            record.setFailCount(failCount);
            record.setStatus(failCount == 0 ? SyncConstants.STATUS_SYNCED : (successCount > 0 ? SyncConstants.STATUS_SYNCED : SyncConstants.STATUS_SYNC_FAILED));
            record.setEndTime(LocalDateTime.now());
            record.setDuration(java.time.Duration.between(record.getStartTime(), record.getEndTime()).toMillis());
            if (errorMsg.length() > 0) {
                record.setErrorMessage(errorMsg.toString());
            }
            updateById(record);

            log.info("商品数据同步完成，总数: {}, 成功: {}, 失败: {}, 标记删除: {}", totalCount, successCount, failCount, deletedCount);

        } catch (HahaException e) {
            log.error("调用哈哈平台API失败", e);
            record.setStatus(SyncConstants.STATUS_SYNC_FAILED);
            record.setEndTime(LocalDateTime.now());
            record.setDuration(java.time.Duration.between(record.getStartTime(), record.getEndTime()).toMillis());
            record.setErrorMessage("调用哈哈平台API失败: " + e.getMessage());
            updateById(record);
        } catch (Exception e) {
            log.error("商品数据同步异常", e);
            record.setStatus(SyncConstants.STATUS_SYNC_FAILED);
            record.setEndTime(LocalDateTime.now());
            record.setDuration(java.time.Duration.between(record.getStartTime(), record.getEndTime()).toMillis());
            record.setErrorMessage("同步异常: " + e.getMessage());
            updateById(record);
        }
    }

    /**
     * 解析Double值
     */
    private Double parseDouble(Object value) {
        if (value == null) return null;
        try {
            return Double.parseDouble(value.toString());
        } catch (NumberFormatException e) {
            return null;
        }
    }

    /**
     * 按设备编号同步单个设备（异步）
     * 从哈哈平台设备列表搜索指定设备，若存在且本地未登记则自动入库
     *
     * @param deviceId 设备编号
     * @return true 创建成功，false 跳过
     */
    @Async("taskExecutor")
    @Override
    public boolean syncDeviceBySn(String deviceId) {
        String debounceKey = RedisConstants.DEVICE_SYNC_DEBOUNCE + deviceId;
        Boolean locked = stringRedisTemplate.opsForValue()
                .setIfAbsent(debounceKey, "1", 5, TimeUnit.MINUTES);
        if (locked == null || !locked) {
            log.debug("设备同步防抖中，跳过: {}", deviceId);
            return false;
        }

        try {
            List<DeviceInfo> deviceList = hahaClient.getDeviceApi().getDeviceList(1, 200);
            DeviceInfo target = deviceList.stream()
                    .filter(d -> deviceId.equals(d.getId()))
                    .findFirst()
                    .orElse(null);

            if (target == null) {
                log.warn("自动同步：设备 {} 在哈哈平台设备列表中未找到", deviceId);
                return false;
            }

            Device existDevice = deviceService.getDeviceBySn(deviceId);
            if (existDevice != null) {
                log.info("自动同步：设备 {} 已存在，跳过创建", deviceId);
                return false;
            }

            Device newDevice = new Device();
            newDevice.setDeviceId(target.getId());
            newDevice.setName(target.getName());
            newDevice.setAddress(target.getAddress());
            newDevice.setStatus("1".equals(target.getStatus()) ? DeviceConstants.STATUS_ONLINE : DeviceOnlineStatus.OFFLINE.getCode());
            deviceService.save(newDevice);

            log.info("自动同步：设备 {} ({}) 创建成功", deviceId, target.getName());
            return true;
        } catch (Exception e) {
            log.error("自动同步：设备 {} 同步失败", deviceId, e);
            return false;
        }
    }
}
