package cn.novalon.manage.gateway.route;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
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. 路由列表查询
 * 5. 路由刷新
 * 
 * @author 张翔
 * @date 2026-03-26
 */
@Service
public class DynamicRouteService {

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

    private final RouteDefinitionWriter routeDefinitionWriter;
    private final RouteDefinitionLocator routeDefinitionLocator;
    private final ApplicationEventPublisher publisher;

    private final Map<String, RouteDefinition> routeCache = new ConcurrentHashMap<>();

    public DynamicRouteService(
            RouteDefinitionWriter routeDefinitionWriter,
            RouteDefinitionLocator routeDefinitionLocator,
            ApplicationEventPublisher publisher) {
        this.routeDefinitionWriter = routeDefinitionWriter;
        this.routeDefinitionLocator = routeDefinitionLocator;
        this.publisher = publisher;

        initializeRouteCache();
    }

    private void initializeRouteCache() {
        routeDefinitionLocator.getRouteDefinitions()
                .doOnNext(route -> routeCache.put(route.getId(), route))
                .subscribe(
                        route -> logger.debug("Cached route: {}", route.getId()),
                        error -> logger.error("Failed to initialize route cache", error),
                        () -> logger.info("Route cache initialized with {} routes", routeCache.size())
                );
    }

    public Mono<Boolean> addRoute(RouteDefinition routeDefinition) {
        if (routeDefinition == null || routeDefinition.getId() == null) {
            logger.error("Invalid route definition: route or route ID is null");
            return Mono.just(false);
        }

        String routeId = routeDefinition.getId();
        logger.info("Adding route: {}", routeId);

        return routeDefinitionWriter.save(Mono.just(routeDefinition))
                .then(Mono.fromRunnable(() -> {
                    routeCache.put(routeId, routeDefinition);
                    refreshRoutes();
                    logger.info("Route added successfully: {}", routeId);
                }))
                .thenReturn(true)
                .onErrorResume(error -> {
                    logger.error("Failed to add route: {}", routeId, error);
                    return Mono.just(false);
                });
    }

    public Mono<Boolean> updateRoute(RouteDefinition routeDefinition) {
        if (routeDefinition == null || routeDefinition.getId() == null) {
            logger.error("Invalid route definition: route or route ID is null");
            return Mono.just(false);
        }

        String routeId = routeDefinition.getId();
        
        if (!routeCache.containsKey(routeId)) {
            logger.warn("Route not found for update: {}", routeId);
            return Mono.just(false);
        }

        logger.info("Updating route: {}", routeId);

        return deleteRoute(routeId)
                .flatMap(success -> {
                    if (success) {
                        return addRoute(routeDefinition);
                    }
                    return Mono.just(false);
                });
    }

    public Mono<Boolean> deleteRoute(String routeId) {
        if (routeId == null || routeId.isEmpty()) {
            logger.error("Invalid route ID: route ID is null or empty");
            return Mono.just(false);
        }

        logger.info("Deleting route: {}", routeId);

        return routeDefinitionWriter.delete(Mono.just(routeId))
                .then(Mono.fromRunnable(() -> {
                    routeCache.remove(routeId);
                    refreshRoutes();
                    logger.info("Route deleted successfully: {}", routeId);
                }))
                .thenReturn(true)
                .onErrorResume(error -> {
                    logger.error("Failed to delete route: {}", routeId, error);
                    return Mono.just(false);
                });
    }

    public Flux<RouteDefinition> getAllRoutes() {
        return Flux.fromIterable(routeCache.values());
    }

    public Mono<RouteDefinition> getRoute(String routeId) {
        if (routeId == null || routeId.isEmpty()) {
            return Mono.empty();
        }
        
        RouteDefinition route = routeCache.get(routeId);
        return route != null ? Mono.just(route) : Mono.empty();
    }

    public void refreshRoutes() {
        logger.info("Refreshing routes");
        publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    public Mono<Boolean> batchAddRoutes(List<RouteDefinition> routeDefinitions) {
        if (routeDefinitions == null || routeDefinitions.isEmpty()) {
            logger.warn("No routes to add");
            return Mono.just(false);
        }

        logger.info("Batch adding {} routes", routeDefinitions.size());

        return Flux.fromIterable(routeDefinitions)
                .flatMap(this::addRoute)
                .all(success -> success)
                .doOnSuccess(allSuccess -> {
                    if (allSuccess) {
                        logger.info("All routes added successfully");
                    } else {
                        logger.warn("Some routes failed to add");
                    }
                });
    }

    public Mono<Boolean> batchDeleteRoutes(List<String> routeIds) {
        if (routeIds == null || routeIds.isEmpty()) {
            logger.warn("No routes to delete");
            return Mono.just(false);
        }

        logger.info("Batch deleting {} routes", routeIds.size());

        return Flux.fromIterable(routeIds)
                .flatMap(this::deleteRoute)
                .all(success -> success)
                .doOnSuccess(allSuccess -> {
                    if (allSuccess) {
                        logger.info("All routes deleted successfully");
                    } else {
                        logger.warn("Some routes failed to delete");
                    }
                });
    }

    public int getRouteCount() {
        return routeCache.size();
    }

    public void clearRouteCache() {
        logger.info("Clearing route cache");
        routeCache.clear();
        initializeRouteCache();
    }
}
