package com.usky.issue.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.usky.issue.domain.IssueCloudConfig;
import com.usky.issue.domain.IssueSyncCursor;
import com.usky.issue.domain.IssueSyncData;
import com.usky.issue.mapper.IssueSyncCursorMapper;
import com.usky.issue.mapper.IssueSyncDataMapper;
import com.usky.issue.service.LocalPushService;
import com.usky.issue.service.client.CloudPlatformClient;
import com.usky.issue.service.enums.SyncDirection;
import com.usky.issue.service.support.CloudEntitySupport;
import com.usky.issue.service.vo.SyncPacket;
import com.usky.issue.service.vo.SyncResponse;
import com.usky.issue.service.vo.SyncResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * 本地推送服务实现
 *
 * @author fyc
 * @date 2026-06-08
 */
@Slf4j
@Service
public class LocalPushServiceImpl implements LocalPushService {

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

    @Autowired
    private IssueSyncDataMapper syncDataMapper;
    @Autowired
    private IssueSyncCursorMapper cursorMapper;
    @Autowired
    private CloudPlatformClient cloudPlatformClient;

    @Override
    public SyncResult pushAllPending(String tableName, IssueCloudConfig config) {
        if (config == null || config.getTenantId() == null) {
            return SyncResult.empty();
        }
        try {
            return pushByTenant(config.getTenantId(), tableName, config);
        } catch (Exception e) {
            log.error("推送租户{}失败", config.getTenantId(), e);
            SyncResult result = SyncResult.empty();
            result.setFailureCount(1);
            result.setErrorMessage(e.getMessage());
            return result;
        }
    }

    @Override
    public SyncResult pushByTenant(Integer tenantId, String tableName, IssueCloudConfig config) {
        SyncResult result = SyncResult.empty();
        IssueSyncCursor cursor = getOrInitCursor(tenantId, tableName, SyncDirection.UP.getCode());
        Long lastVersion = cursor.getLastSyncVersion() == null ? 0L : cursor.getLastSyncVersion();

        while (true) {
            List<IssueSyncData> dataList = syncDataMapper.selectList(new LambdaQueryWrapper<IssueSyncData>()
                    .eq(IssueSyncData::getTenantId, tenantId)
                    .eq(IssueSyncData::getTableName, tableName)
                    .in(IssueSyncData::getSyncStatus, 0, 2)
                    .gt(IssueSyncData::getSyncVersion, lastVersion)
                    .eq(IssueSyncData::getIsDeleted, 0)
                    .orderByAsc(IssueSyncData::getSyncVersion)
                    .last("LIMIT " + batchSize));

            if (dataList.isEmpty()) {
                break;
            }

            result.setTotalCount(result.getTotalCount() + dataList.size());
            Long batchVersion = dataList.stream()
                    .mapToLong(IssueSyncData::getSyncVersion)
                    .max()
                    .orElse(lastVersion);

            SyncPacket packet = SyncPacket.builder()
                    .tenantId(String.valueOf(tenantId))
                    .tableName(tableName)
                    .direction(SyncDirection.UP)
                    .dataList(dataList)
                    .batchVersion(batchVersion)
                    .hasMore(dataList.size() >= batchSize)
                    .nonce(UUID.randomUUID().toString())
                    .build();

            SyncResponse response = cloudPlatformClient.pushToCloud(packet, config);
            if (response != null && response.isSuccess()) {
                lastVersion = response.getConfirmedVersion() != null ? response.getConfirmedVersion() : batchVersion;
                updateCursor(tenantId, tableName, SyncDirection.UP.getCode(), lastVersion);
                List<Long> ids = dataList.stream().map(IssueSyncData::getId).collect(Collectors.toList());
                markSynced(ids);
                result.setSuccessCount(result.getSuccessCount() + dataList.size());
                if (Boolean.FALSE.equals(packet.getHasMore())) {
                    break;
                }
            } else {
                String message = response != null ? response.getMessage() : "云端无响应";
                log.error("云端接收失败: {}", message);
                result.setFailureCount(result.getFailureCount() + dataList.size());
                result.setErrorMessage(message);
                markFailed(dataList.stream().map(IssueSyncData::getId).collect(Collectors.toList()));
                break;
            }
        }
        return result;
    }

    @Override
    public void resetCursor(Integer tenantId, String tableName) {
        cursorMapper.update(null, new LambdaUpdateWrapper<IssueSyncCursor>()
                .eq(IssueSyncCursor::getTenantId, tenantId)
                .eq(IssueSyncCursor::getTableName, tableName)
                .eq(IssueSyncCursor::getDirection, SyncDirection.UP.getCode())
                .set(IssueSyncCursor::getLastSyncVersion, 0L)
                .set(IssueSyncCursor::getLastSyncTime, LocalDateTime.now()));
    }

    private IssueSyncCursor getOrInitCursor(Integer tenantId, String tableName, String direction) {
        IssueSyncCursor cursor = cursorMapper.selectOne(new LambdaQueryWrapper<IssueSyncCursor>()
                .eq(IssueSyncCursor::getTenantId, tenantId)
                .eq(IssueSyncCursor::getTableName, tableName)
                .eq(IssueSyncCursor::getDirection, direction)
                .eq(IssueSyncCursor::getIsDeleted, 0));
        if (cursor == null) {
            cursor = new IssueSyncCursor();
            cursor.setTenantId(tenantId);
            cursor.setTableName(tableName);
            cursor.setDirection(direction);
            cursor.setLastSyncVersion(0L);
            cursor.setLastSyncTime(LocalDateTime.now());
            CloudEntitySupport.fillOnInsert(cursor);
            cursorMapper.insert(cursor);
        }
        return cursor;
    }

    private void updateCursor(Integer tenantId, String tableName, String direction, Long version) {
        cursorMapper.update(null, new LambdaUpdateWrapper<IssueSyncCursor>()
                .eq(IssueSyncCursor::getTenantId, tenantId)
                .eq(IssueSyncCursor::getTableName, tableName)
                .eq(IssueSyncCursor::getDirection, direction)
                .set(IssueSyncCursor::getLastSyncVersion, version)
                .set(IssueSyncCursor::getLastSyncTime, LocalDateTime.now()));
    }

    private void markSynced(List<Long> ids) {
        if (ids == null || ids.isEmpty()) {
            return;
        }
        syncDataMapper.update(null, new LambdaUpdateWrapper<IssueSyncData>()
                .in(IssueSyncData::getId, ids)
                .set(IssueSyncData::getSyncStatus, 1));
    }

    private void markFailed(List<Long> ids) {
        if (ids == null || ids.isEmpty()) {
            return;
        }
        syncDataMapper.update(null, new LambdaUpdateWrapper<IssueSyncData>()
                .in(IssueSyncData::getId, ids)
                .set(IssueSyncData::getSyncStatus, 2));
    }
}
