package com.seer.teach.user.admin.service;

import cn.hutool.json.JSONUtil;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.seer.teach.common.PageListBean;
import com.seer.teach.common.auth.dto.PermissionDTO;
import com.seer.teach.common.auth.service.PermissionScannerService;
import com.seer.teach.common.utils.PageConverterUtils;
import com.seer.teach.user.admin.convert.AdminAuthorityConvert;
import com.seer.teach.user.admin.convert.PermissionSyncLogConvert;
import com.seer.teach.user.admin.request.SyncPageReq;
import com.seer.teach.user.admin.response.PermissionSyncLogResp;
import com.seer.teach.user.entity.AuthorityEntity;
import com.seer.teach.user.entity.PermissionSyncLogEntity;
import com.seer.teach.user.service.IAuthorityService;
import com.seer.teach.user.service.IPermissionSyncLogService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class PermissionSyncService {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private SeerNacosServiceDiscovery seerNacosServiceDiscovery;

    @Autowired
    private ContextPathResolver contextPathResolver;

    @Autowired
    private IAuthorityService authorityService;

    @Autowired
    private IPermissionSyncLogService permissionSyncLogService;

    @Autowired
    private PermissionScannerService permissionScannerService;

    @Autowired
    private Environment environment;

    /**
     * 异步同步权限
     *
     * @param serviceNames 服务名称列表
     */
    @Transactional(rollbackFor = Exception.class, timeout = 360)
    public void syncPermissions(List<String> serviceNames, Integer userId) {
        for (String serviceName : serviceNames) {
            long startTime = System.currentTimeMillis();
            PermissionSyncLogEntity permissionSyncLog = syncSingleService(serviceName, userId);
            log.info("服务[{}]同步完成，耗时{}ms,结果：{}", serviceName, System.currentTimeMillis() - startTime, permissionSyncLog);
        }
    }

    /**
     * 查询同步结果
     *
     * @param req 查询参数，服务名称列表
     * @return 分页结果
     */
    public PageListBean<PermissionSyncLogResp> getSyncResult(SyncPageReq req) {
        Page<PermissionSyncLogEntity> page = new Page<>(req.getPageNo(), req.getPageSize());

        Page<PermissionSyncLogEntity> syncLogEntityPage = permissionSyncLogService.page(page, new LambdaQueryWrapper<>(PermissionSyncLogEntity.class)
                .in(!org.springframework.util.CollectionUtils.isEmpty(req.getServiceNames()), PermissionSyncLogEntity::getServiceName, req.getServiceNames())
                .orderByDesc(PermissionSyncLogEntity::getId));

        return PageConverterUtils.convertPageListBean(syncLogEntityPage, PermissionSyncLogConvert.INSTANCE::toResponseList);
    }

    private PermissionSyncLogEntity syncSingleService(String serviceName, Integer userId) {
        long startTime = System.currentTimeMillis();
        int addedCount = 0;
        int updatedCount = 0;
        int deletedCount = 0;
        List<String> addedPermissions = new ArrayList<>();
        List<String> deletedPermissions = new ArrayList<>();
        List<Map<String, String>> updatedPermissions = new ArrayList<>();

        try {
            // 1. 获取服务上下文根
            String contextPath = contextPathResolver.resolveContextPath(serviceName);

            // 2. 调用目标服务的扫描接口
            List<PermissionDTO> remoteScanPermissions = fetchPermissions(serviceName, contextPath);
            if (CollectionUtils.isEmpty(remoteScanPermissions)) {
                log.info("服务[{}]没有扫描到权限", serviceName);
                return new PermissionSyncLogEntity();
            }
            log.info("服务[{}]扫描到的权限数量为{}", serviceName, remoteScanPermissions.size());

            // 3. 获取数据库现有权限（按服务名称过滤）
            List<AuthorityEntity> localPermissions = authorityService.list(
                    new LambdaQueryWrapper<AuthorityEntity>()
                            .eq(AuthorityEntity::getModule, serviceName)
            );
            log.info("服务[{}]数据库现有权限数量为{}", serviceName, localPermissions.size());

            Map<String, AuthorityEntity> localCodeMap = localPermissions.stream().filter(entity -> StringUtils.hasText(entity.getAuthorityCode()))
                    .collect(Collectors.toMap(AuthorityEntity::getAuthorityCode, entity -> entity, (entity1, entity2) -> entity1));

            Map<String, AuthorityEntity> localClassNameMap = localPermissions.stream().filter(entity -> StringUtils.hasText(entity.getClassName()))
                    .collect(Collectors.toMap(AuthorityEntity::getClassName, entity -> entity, (entity1, entity2) -> entity1));

            // 4. 计算需要新增、更新、删除的权限
            List<AuthorityEntity> addList = new ArrayList<>();
            List<AuthorityEntity> updateList = new ArrayList<>();
            
            Set<String> processedCodes = new HashSet<>();
            
            for (PermissionDTO remotePermission : remoteScanPermissions) {
                List<PermissionDTO.ApiItem> apiItems = remotePermission.getApiItems();
                if (CollectionUtils.isEmpty(apiItems)) {
                    continue;
                }
                AuthorityEntity authorityEntity = AdminAuthorityConvert.INSTANCE.permissionDTO2Entity(remotePermission);
                String className = remotePermission.getClassName();
                if (!StringUtils.hasText(className)) {
                    continue;
                }
                Integer pId = 0;
                AuthorityEntity localEntity = localClassNameMap.get(className);
                if (Objects.isNull(localEntity)) {
                    authorityEntity.setModule(serviceName);
                    boolean saved = authorityService.save(authorityEntity);
                    log.info("保存权限数据[{}]的结果:{}",JSONUtil.toJsonStr(authorityEntity), saved);
                    pId = authorityEntity.getId();
                }else{
                    pId = localEntity.getId();
                    authorityEntity.setId(pId);
                    if(StringUtils.hasText(localEntity.getTitle()) && !localEntity.getTitle().equals(authorityEntity.getTitle())){
                        String newTitle = authorityEntity.getTitle().replace("管理端 - ", "").replace("-", "").replace(" ", "").replace("后台-", "");
                        authorityEntity.setTitle(newTitle);
                        boolean updated = authorityService.updateById(authorityEntity);
                        log.info("更新权限数据[{}]的结果:{}",JSONUtil.toJsonStr(authorityEntity), updated);
                    }
                }
                List<AuthorityEntity> remoteScanAuthorityList = AdminAuthorityConvert.INSTANCE.permissionDTOtoEntityList(apiItems);
                for (AuthorityEntity remoteEntity : remoteScanAuthorityList) {
                    remoteEntity.setModule(serviceName);
                    remoteEntity.setPid(pId);
                    remoteEntity.setHidden(0);
                    String code = remoteEntity.getAuthorityCode();
                    // 修改：记录已处理的权限码
                    processedCodes.add(code);
                    AuthorityEntity localAuthority = localCodeMap.get(code);
                    if (Objects.nonNull(localAuthority) ) {
                        if(StringUtils.hasText(localAuthority.getTitle()) && !localAuthority.getTitle().equals(remoteEntity.getTitle())){
                            remoteEntity.setId(localAuthority.getId());
                            updateList.add(remoteEntity);
                            // 记录更新信息
                            Map<String, String> updateMap = new HashMap<>();
                            updateMap.put("old", localAuthority.getAuthorityCode());
                            updateMap.put("new", remoteEntity.getAuthorityCode());
                            updatedPermissions.add(updateMap);
                        }
                    } else {
                        addList.add(remoteEntity);
                    }
                }
            }
            
            // 修改：只删除未被处理的权限
            List<AuthorityEntity> deleteList = localCodeMap.values().stream()
                    .filter(entity -> !processedCodes.contains(entity.getAuthorityCode()))
                    .collect(Collectors.toList());

            // 5. 执行数据库操作
            // 新增权限
            if (CollectionUtils.isNotEmpty(addList)) {
                addedCount = addList.size();
                addedPermissions = addList.stream()
                        .map(AuthorityEntity::getAuthorityCode)
                        .collect(Collectors.toList());
                log.info("用户[{}]新增权限数量: {}", userId, JSONUtil.toJsonStr(addedPermissions));
                boolean saved = authorityService.saveBatch(addList);
                log.info("用户[{}]新增权限数量: {}, 结果: {}", userId, addedCount, saved);
            }

            // 更新权限
            if (CollectionUtils.isNotEmpty(updateList)) {
                updatedCount = updateList.size();
                log.info("用户[{}]更新权限数量: {}", userId, JSONUtil.toJsonStr(updateList));
                boolean updated = authorityService.updateBatchById(updateList);
                log.info("用户[{}]更新权限数量: {}, 结果: {}", userId, updatedCount, updated);
            }

            // 删除权限
            if (CollectionUtils.isNotEmpty(deleteList)) {
                deletedCount = deleteList.size();
                deletedPermissions = deleteList.stream()
                        .map(AuthorityEntity::getAuthorityCode)
                        .collect(Collectors.toList());
                log.info("用户[{}]删除权限数量: {}", userId, JSONUtil.toJsonStr(deleteList));
                boolean deleted = authorityService.removeByIds(deleteList.stream().map(AuthorityEntity::getId).collect(Collectors.toList()));
                log.info("用户[{}]删除权限数量: {}, 结果: {}", userId, deletedCount, deleted);
            }
        } catch (Exception e) {
            log.error("同步服务 {} 权限失败", serviceName, e);
        } finally {
            log.info("用户[{}]同步权限完成", userId);
            // 6. 记录同步日志
            long duration = System.currentTimeMillis() - startTime;
            PermissionSyncLogEntity permissionSyncLog = saveSyncLog(serviceName, duration, addedCount, addedPermissions,
                    deletedCount, deletedPermissions, updatedCount, updatedPermissions, userId);
            log.info("权限同步日志: {}", permissionSyncLog);
            return permissionSyncLog;
        }
    }

    private PermissionSyncLogEntity saveSyncLog(String serviceName, long durationMs,
                                                int addedCount, List<String> addedPermissions,
                                                int deletedCount, List<String> deletedPermissions,
                                                int updatedCount, List<Map<String, String>> updatedPermissions, Integer userId) {

        PermissionSyncLogEntity logEntity = new PermissionSyncLogEntity();
        logEntity.setServiceName(serviceName);
        logEntity.setDurationMs((int) durationMs);
        logEntity.setSyncTime(LocalDateTime.now());
        logEntity.setCreateBy(String.valueOf(userId));
        logEntity.setUpdateBy(String.valueOf(userId));
        logEntity.setAddedCount(addedCount);
        logEntity.setAddedPermissions(JSONUtil.toJsonStr(addedPermissions));
        logEntity.setDeletedCount(deletedCount);
        logEntity.setDeletedPermissions(JSONUtil.toJsonStr(deletedPermissions));
        logEntity.setUpdatedCount(updatedCount);
        logEntity.setUpdatedPermissions(JSONUtil.toJsonStr(updatedPermissions));

        permissionSyncLogService.save(logEntity);

        return logEntity;
    }

    private List<PermissionDTO> fetchPermissions(String serviceName, String contextPath) {
        if (environment.getProperty("spring.application.name", "seer-admin").equals(serviceName)) {
            return permissionScannerService.scanPermissionAnnotations();
        }
        List<PermissionDTO> remotePermissions = fetchRemotePermissions(serviceName, contextPath);
        if (remotePermissions == null) {
            return Collections.emptyList();
        }
        return remotePermissions.stream().filter(permission -> StringUtils.hasText(permission.getTitle())).collect(Collectors.toList());
    }

    private List<PermissionDTO> fetchRemotePermissions(String serviceName, String contextPath) {
        int maxAttempts = 3;
        long initialDelay = 1000; // 1秒
        long multiplier = 2;
        long delay = initialDelay;
        List<PermissionDTO> result = Collections.emptyList();
        for (int attempt = 1; attempt <= maxAttempts; attempt++) {
            try {
                Instance instance = seerNacosServiceDiscovery.getAppServicesByServiceName(serviceName);
                String baseUrl = "http://" + instance.getIp() + ":" + instance.getPort();
                String fullUrl = baseUrl + contextPath + "/internal/permissions";
                log.info("调用服务 {} 权限扫描接口: {} (尝试第{}次)", serviceName, fullUrl, attempt);
                result = restTemplate.exchange(
                        fullUrl,
                        HttpMethod.GET,
                        null,
                        new ParameterizedTypeReference<List<PermissionDTO>>() {
                        }
                ).getBody();
                break;
            } catch (Exception e) {
                log.error("调用服务 {} 权限扫描接口失败 (第{}次尝试): {}", serviceName, attempt, e.getMessage());
                if (attempt == maxAttempts) {
                    log.error("调用服务 {} 权限扫描接口失败，已达最大重试次数", serviceName);
                    return Collections.emptyList();
                }
                try {
                    Thread.sleep(delay);
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                    log.error("重试等待被中断", ex);
                    return Collections.emptyList();
                }
                delay *= multiplier; // 延迟时间翻倍
            }
        }
        return result;
    }
}