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

import cn.reghao.jutil.jdk.serializer.JsonConverter;
import cn.reghao.tnb.gateway.admin.model.RouteDefinitionInfo;
import org.springframework.beans.BeanUtils;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author reghao
 * @date 2023-03-20 09:30:08
 */
@Repository
public class RedisRouteDefinitionRepository implements RouteDefinitionRepository, ApplicationEventPublisherAware {
    private final static String GATEWAY_ROUTES = "gateway_routes";
    private ApplicationEventPublisher publisher;
    private final StringRedisTemplate redisTemplate;

    public RedisRouteDefinitionRepository(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }

    private void notifyChanged() {
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        route.subscribe(r -> {
            RouteDefinitionInfo info = new RouteDefinitionInfo();
            BeanUtils.copyProperties(r, info);
            redisTemplate.opsForHash().put(GATEWAY_ROUTES, r.getId(), JsonConverter.objectToJson(info));
            notifyChanged();
        });

        return Mono.empty();
        /*return route.flatMap(r -> {
            RouteDefinitionInfo info = new RouteDefinitionInfo();
            BeanUtils.copyProperties(r, info);
            redisTemplate.opsForHash().put(GATEWAY_ROUTES, r.getId(), JsonConverter.objectToJson(info));
            return Mono.empty();
        });*/
    }

    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        return routeId.flatMap(id -> {
            redisTemplate.opsForHash().delete(GATEWAY_ROUTES, id);
            notifyChanged();
            return Mono.empty();
        });
    }

    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        List<RouteDefinition> list = redisTemplate.opsForHash().values(GATEWAY_ROUTES).stream()
                .map(json -> JsonConverter.jsonToObject((String) json, RouteDefinition.class))
                .collect(Collectors.toList());
        return Flux.fromIterable(list);
    }

    public Mono<RouteDefinition> get(Mono<String> routeId) {
        return routeId.flatMap(id -> {
            Object object = redisTemplate.opsForHash().get(GATEWAY_ROUTES, id);
            RouteDefinition routeDefinition = JsonConverter.jsonToObject((String) object, RouteDefinition.class);
            return Mono.just(routeDefinition);
        });
    }
}
