package com.usky.issue.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.usky.common.core.exception.BusinessException;
import com.usky.common.redis.core.RedisHelper;
import com.usky.issue.service.constant.CloudIntegrationConstants;
import com.usky.issue.domain.IssueCloudConfig;
import com.usky.issue.domain.IssueSyncStatus;
import com.usky.issue.domain.IssueSyncTask;
import com.usky.issue.domain.IssueTriggerEvent;
import com.usky.issue.service.vo.SyncStatusResponse;
import com.usky.issue.service.vo.SyncTaskResponse;
import com.usky.issue.service.enums.SyncDirection;
import com.usky.issue.service.enums.SyncTypeEnum;
import com.usky.issue.service.enums.TriggerEventCodeEnum;
import com.usky.issue.mapper.IssueSyncStatusMapper;
import com.usky.issue.mapper.IssueSyncTaskMapper;
import com.usky.issue.mapper.IssueTriggerEventMapper;
import com.usky.issue.service.CloudConfigService;
import com.usky.issue.service.CloudOperationLogService;
import com.usky.issue.service.CloudSyncService;
import com.usky.issue.service.CloudSyncTaskRunner;
import com.usky.issue.service.support.CloudEntitySupport;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 云平台数据同步服务实现
 *
 * @author fyc
 * @date 2026-05-21
 */
@DS("usky-cloud")
@Slf4j
@Service
public class CloudSyncServiceImpl implements CloudSyncService {

    @Autowired
    private IssueSyncStatusMapper syncStatusMapper;
    @Autowired
    private IssueSyncTaskMapper syncTaskMapper;
    @Autowired
    private IssueTriggerEventMapper triggerEventMapper;
    @Autowired
    private CloudConfigService cloudConfigService;
    @Autowired
    private CloudOperationLogService operationLogService;
    @Autowired
    private CloudSyncTaskRunner cloudSyncTaskRunner;
    @Autowired
    private RedisHelper redisHelper;
    @Autowired
    @Qualifier("cloudSyncExecutor")
    private Executor cloudSyncExecutor;

    @Override
    public List<SyncStatusResponse> listSyncStatus() {
        IssueCloudConfig config = cloudConfigService.requireActiveConfig();
        String cacheKey = CloudIntegrationConstants.CACHE_STATUS_PREFIX + config.getId();
        Object cached = redisHelper.get(cacheKey);
        if (cached != null) {
            return JSON.parseArray(cached.toString(), SyncStatusResponse.class);
        }
        List<SyncStatusResponse> list = loadStatusFromDb(config.getId());
        redisHelper.set(cacheKey, JSON.toJSONString(list),
                CloudIntegrationConstants.CACHE_STATUS_TTL_SECONDS, TimeUnit.SECONDS);
        return list;
    }

    @Override
    public SyncTaskResponse triggerManualSync(String syncType, String direction) {
        String operator = CloudEntitySupport.resolveOperator();
        String requestIp = CloudEntitySupport.resolveRequestIp();
        SyncTypeEnum typeEnum;
        try {
            typeEnum = SyncTypeEnum.fromCode(syncType);
        } catch (IllegalArgumentException ex) {
            throw new BusinessException("无效的同步类型: " + syncType);
        }
        SyncDirection directionEnum;
        try {
            directionEnum = SyncDirection.fromCode(direction);
        } catch (IllegalArgumentException ex) {
            throw new BusinessException("无效的同步方向: " + direction);
        }
        if (directionEnum == SyncDirection.BOTH) {
            throw new BusinessException("手动同步请指定 UP 或 DOWN 方向");
        }
        IssueCloudConfig config = cloudConfigService.requireActiveConfig();
        String lockKey = CloudIntegrationConstants.LOCK_SYNC_PREFIX + typeEnum.getCode() + ":" + directionEnum.getCode();
        String lockValue = UUID.randomUUID().toString();
        boolean locked = redisHelper.lock(lockKey, lockValue, CloudIntegrationConstants.LOCK_SYNC_SECONDS);
        if (!locked) {
            throw new BusinessException("该类型同步正在进行中，请稍后再试");
        }
        try {
            IssueSyncTask task = cloudSyncTaskRunner.createTask(config.getId(), typeEnum, "MANUAL", directionEnum);
            operationLogService.log(config.getId(), CloudIntegrationConstants.OPERATION_MANUAL_SYNC,
                    "手动同步 type=" + typeEnum.getCode() + " direction=" + directionEnum.getCode(),
                    operator, requestIp);
            redisHelper.delete(CloudIntegrationConstants.CACHE_STATUS_PREFIX + config.getId());
            if (typeEnum.isAsync()) {
                cloudSyncExecutor.execute(() -> cloudSyncTaskRunner.executeSyncTask(task.getId(), typeEnum, directionEnum));
                return toTaskResponse(syncTaskMapper.selectById(task.getId()));
            }
            cloudSyncTaskRunner.executeSyncTask(task.getId(), typeEnum, directionEnum);
            return toTaskResponse(syncTaskMapper.selectById(task.getId()));
        } finally {
            redisHelper.unlockLua(lockKey, lockValue);
        }
    }

    @Override
    public SyncTaskResponse getTaskProgress(Long taskId) {
        IssueSyncTask task = syncTaskMapper.selectById(taskId);
        if (task == null || (task.getIsDeleted() != null && task.getIsDeleted() == 1)) {
            throw new BusinessException("同步任务不存在");
        }
        return toTaskResponse(task);
    }

    @Override
    public void onOrgChanged() {
        fireEventIfEnabled(TriggerEventCodeEnum.SYNC_ORG, SyncTypeEnum.TENANT);
    }

    @Override
    public void onUserChanged() {
        fireEventIfEnabled(TriggerEventCodeEnum.SYNC_USER, SyncTypeEnum.USER);
    }

    private void fireEventIfEnabled(TriggerEventCodeEnum eventCode, SyncTypeEnum syncType) {
        IssueCloudConfig config;
        try {
            config = cloudConfigService.requireActiveConfig();
        } catch (BusinessException ex) {
            log.debug("跳过事件同步: {}", ex.getMessage());
            return;
        }
        IssueTriggerEvent event = triggerEventMapper.selectOne(new LambdaQueryWrapper<IssueTriggerEvent>()
                .eq(IssueTriggerEvent::getConfigId, config.getId())
                .eq(IssueTriggerEvent::getEventCode, eventCode.getCode())
                .eq(IssueTriggerEvent::getIsDeleted, 0));
        if (event == null || event.getEnabled() == null || event.getEnabled() == 0) {
            return;
        }
        IssueSyncTask task = cloudSyncTaskRunner.createTask(config.getId(), syncType, "EVENT", SyncDirection.BOTH);
        cloudSyncExecutor.execute(() -> cloudSyncTaskRunner.executeSyncTask(task.getId(), syncType, SyncDirection.BOTH));
    }

    private List<SyncStatusResponse> loadStatusFromDb(Long configId) {
        return syncStatusMapper.selectList(new LambdaQueryWrapper<IssueSyncStatus>()
                        .eq(IssueSyncStatus::getConfigId, configId)
                        .eq(IssueSyncStatus::getIsDeleted, 0)
                        .orderByAsc(IssueSyncStatus::getId))
                .stream()
                .map(this::toStatusResponse)
                .collect(Collectors.toList());
    }

    private SyncStatusResponse toStatusResponse(IssueSyncStatus status) {
        SyncStatusResponse resp = new SyncStatusResponse();
        resp.setSyncType(status.getSyncType());
        resp.setTypeName(status.getTypeName());
        resp.setTotalCount(status.getTotalCount());
        resp.setSuccessCount(status.getSuccessCount());
        resp.setFailureCount(status.getFailureCount());
        resp.setUnsyncedCount(status.getUnsyncedCount());
        resp.setLastSyncTime(status.getLastSyncTime());
        return resp;
    }

    private SyncTaskResponse toTaskResponse(IssueSyncTask task) {
        SyncTaskResponse resp = new SyncTaskResponse();
        resp.setTaskId(task.getId());
        resp.setSyncType(task.getSyncType());
        resp.setTaskStatus(task.getTaskStatus());
        resp.setTriggerMode(task.getTriggerMode());
        resp.setTotalCount(task.getTotalCount());
        resp.setProcessedCount(task.getProcessedCount());
        resp.setSuccessCount(task.getSuccessCount());
        resp.setFailureCount(task.getFailureCount());
        resp.setErrorSummary(task.getErrorSummary());
        resp.setStartTime(task.getStartTime());
        resp.setFinishTime(task.getFinishTime());
        if (task.getTotalCount() != null && task.getTotalCount() > 0 && task.getProcessedCount() != null) {
            resp.setProgressPercent(task.getProcessedCount() * 100 / task.getTotalCount());
        } else {
            resp.setProgressPercent(0);
        }
        return resp;
    }
}
