package cn.reghao.tnb.gateway.admin.handler;

import cn.reghao.tnb.gateway.admin.db.RedisRouteDefinitionRepository;
import cn.reghao.tnb.gateway.admin.model.RouteDelete;
import org.springframework.cloud.gateway.config.PropertiesRouteDefinitionLocator;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.BodyExtractors;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * 动态注册服务路由
 *
 * @author reghao
 * @date 2023-03-20 09:33:01
 */
@Service
public class DynamicRouteHandler {
    private final RedisRouteDefinitionRepository routeDefinitionRepository;
    private final PropertiesRouteDefinitionLocator locator;

    public DynamicRouteHandler(RedisRouteDefinitionRepository routeDefinitionRepository,
                               PropertiesRouteDefinitionLocator locator) {
        this.routeDefinitionRepository = routeDefinitionRepository;
        this.locator = locator;
    }

    public Mono<ServerResponse> add(ServerRequest serverRequest) {
        // MonoOnErrorResume 对象
        Mono<RouteDefinition> mono = serverRequest.bodyToMono(RouteDefinition.class);
        return mono.doOnNext(r -> {
                    routeDefinitionRepository.save(Mono.just(r)).subscribe();
                })
                .then( ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(Mono.just("add"), String.class)
                        .onErrorResume(e ->
                                Mono.just(e.getCause())
                                        .flatMap(s -> ServerResponse.ok().contentType(MediaType.TEXT_PLAIN).bodyValue(s))));
    }

    public Mono<ServerResponse> update(ServerRequest serverRequest) {
        Mono<RouteDefinition> mono = serverRequest.bodyToMono(RouteDefinition.class);
        return mono.doOnNext(r -> {
                    routeDefinitionRepository.delete(Mono.just(r.getId())).subscribe();
                    routeDefinitionRepository.save(Mono.just(r)).subscribe();
                })
                .then( ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(Mono.just("update"), String.class)
                        .onErrorResume(e ->
                                Mono.just(e.getCause())
                                        .flatMap(s -> ServerResponse.ok().contentType(MediaType.TEXT_PLAIN).bodyValue(s))));
    }

    public Mono<ServerResponse> delete(ServerRequest serverRequest) {
        routeDefinitionRepository.delete(serverRequest.body(BodyExtractors.toMono(RouteDelete.class))
                .map(RouteDelete::getRouteId));
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(Mono.just("delete"), String.class);
    }

    public Mono<ServerResponse> getRoutes(ServerRequest serverRequest) {
        Flux<RouteDefinition> flux = routeDefinitionRepository.getRouteDefinitions();
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
                .body(flux, RouteDefinition.class);
    }

    public Mono<ServerResponse> getRoute(ServerRequest serverRequest) {
        String routeId = serverRequest.queryParam("id").orElse(null);
        if (routeId != null) {
            Mono<RouteDefinition> mono = routeDefinitionRepository.get(Mono.just(routeId));
            return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
                    .body(mono, RouteDefinition.class);
        }

        Flux<RouteDefinition> flux = routeDefinitionRepository.getRouteDefinitions();
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
                .body(flux, RouteDefinition.class);
    }

    public Mono<ServerResponse> getAllRoutes(ServerRequest serverRequest) {
        Flux<RouteDefinition> flux = locator.getRouteDefinitions();
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
                .body(flux, RouteDefinition.class);
    }
}
