package cn.novalon.gym.manage.gateway.service.impl;

import cn.novalon.gym.manage.gateway.service.IServiceDiscoveryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 服务发现服务实现类
 * 
 * 文件定义：实现服务实例的发现、监控和管理
 * 涉及业务：服务实例查询、健康检查、服务状态监控
 * 
 * 核心功能：
 * 1. 服务实例查询
 * 2. 服务健康检查
 * 3. 服务状态监控
 * 4. 服务实例缓存
 * 
 * @author 张翔
 * @date 2026-04-14
 */
@Service
public class ServiceDiscoveryService implements IServiceDiscoveryService {

    private static final Logger logger = LoggerFactory.getLogger(ServiceDiscoveryService.class);

    private final ReactiveDiscoveryClient reactiveDiscoveryClient;
    private final DiscoveryClient discoveryClient;

    private final Map<String, List<ServiceInstance>> serviceCache = new ConcurrentHashMap<>();
    private final Map<String, Long> lastUpdateTime = new ConcurrentHashMap<>();

    private static final long CACHE_TTL_MS = 30000;

    public ServiceDiscoveryService(
            ReactiveDiscoveryClient reactiveDiscoveryClient,
            DiscoveryClient discoveryClient) {
        this.reactiveDiscoveryClient = reactiveDiscoveryClient;
        this.discoveryClient = discoveryClient;

        initializeServiceCache();
    }

    private void initializeServiceCache() {
        logger.info("Initializing service cache");
        
        discoveryClient.getServices().forEach(serviceId -> {
            List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
            if (!instances.isEmpty()) {
                serviceCache.put(serviceId, instances);
                lastUpdateTime.put(serviceId, System.currentTimeMillis());
                logger.debug("Cached {} instances for service: {}", instances.size(), serviceId);
            }
        });

        logger.info("Service cache initialized with {} services", serviceCache.size());
    }

    @Override
    public Flux<ServiceInstance> getInstances(String serviceId) {
        if (serviceId == null || serviceId.isEmpty()) {
            logger.warn("Service ID is null or empty");
            return Flux.empty();
        }

        if (isCacheValid(serviceId)) {
            List<ServiceInstance> cachedInstances = serviceCache.get(serviceId);
            if (cachedInstances != null && !cachedInstances.isEmpty()) {
                logger.debug("Returning {} cached instances for service: {}", 
                        cachedInstances.size(), serviceId);
                return Flux.fromIterable(cachedInstances);
            }
        }

        logger.debug("Fetching instances for service: {}", serviceId);
        
        return reactiveDiscoveryClient.getInstances(serviceId)
                .doOnNext(instance -> logger.debug("Found instance: {}:{} for service: {}", 
                        instance.getHost(), instance.getPort(), serviceId))
                .collectList()
                .doOnNext(instances -> {
                    serviceCache.put(serviceId, instances);
                    lastUpdateTime.put(serviceId, System.currentTimeMillis());
                    logger.info("Updated cache with {} instances for service: {}", 
                            instances.size(), serviceId);
                })
                .flatMapMany(Flux::fromIterable);
    }

    @Override
    public Flux<String> getServices() {
        return reactiveDiscoveryClient.getServices()
                .doOnNext(serviceId -> logger.debug("Found service: {}", serviceId));
    }

    @Override
    public Mono<Boolean> isServiceHealthy(String serviceId) {
        return getInstances(serviceId)
                .hasElements()
                .map(hasInstances -> {
                    if (hasInstances) {
                        logger.debug("Service {} is healthy - has instances", serviceId);
                        return true;
                    } else {
                        logger.warn("Service {} is unhealthy - no instances found", serviceId);
                        return false;
                    }
                });
    }

    @Override
    public Mono<Long> getInstanceCount(String serviceId) {
        return getInstances(serviceId)
                .count()
                .doOnNext(count -> logger.debug("Service {} has {} instances", serviceId, count));
    }

    @Override
    public Mono<Void> refreshServiceCache(String serviceId) {
        return Mono.fromRunnable(() -> {
            if (serviceId != null) {
                serviceCache.remove(serviceId);
                lastUpdateTime.remove(serviceId);
                logger.info("Refreshed cache for service: {}", serviceId);
            }
        });
    }

    @Override
    public Mono<Void> refreshAllServiceCache() {
        return Mono.fromRunnable(() -> {
            serviceCache.clear();
            lastUpdateTime.clear();
            initializeServiceCache();
            logger.info("Refreshed all service cache");
        });
    }

    @Override
    public Mono<Long> getServiceCount() {
        return getServices()
                .count()
                .doOnNext(count -> logger.debug("Found {} services", count));
    }

    @Override
    public Mono<Boolean> serviceExists(String serviceId) {
        if (serviceId == null || serviceId.isEmpty()) {
            return Mono.just(false);
        }
        return getServices()
                .any(s -> s.equals(serviceId))
                .doOnNext(exists -> logger.debug("Service {} exists: {}", serviceId, exists));
    }

    @Override
    public Mono<Void> clearServiceCache() {
        return Mono.fromRunnable(() -> {
            serviceCache.clear();
            lastUpdateTime.clear();
            logger.info("Cleared service cache");
        });
    }

    private boolean isCacheValid(String serviceId) {
        Long lastUpdate = lastUpdateTime.get(serviceId);
        if (lastUpdate == null) {
            return false;
        }
        return System.currentTimeMillis() - lastUpdate < CACHE_TTL_MS;
    }
}