package com.opencloud.gateway.zuul.server.locator;

import com.google.common.collect.Maps;
import com.opencloud.base.client.model.entity.GatewayRoute;
import com.opencloud.common.event.RemoteRefreshRouteEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent;
import org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.util.StringUtils;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 自定义动态路由加载器
 *
 * @author: liuyadu
 * @date: 2018/10/23 10:31
 * @description:
 */
@Slf4j
public class JdbcRouteLocator extends SimpleRouteLocator implements ApplicationListener<RemoteRefreshRouteEvent> {

    private JdbcTemplate jdbcTemplate;
    private ZuulProperties properties;
    private List<GatewayRoute> routeList;
    private ApplicationEventPublisher publisher;

    public JdbcRouteLocator(String servletPath, ZuulProperties properties, JdbcTemplate jdbcTemplate, ApplicationEventPublisher publisher) {
        super(servletPath, properties);
        this.properties = properties;
        this.jdbcTemplate = jdbcTemplate;
        this.publisher = publisher;
    }

    @Override
    public void doRefresh() {
        super.doRefresh();
        // 发布本地刷新事件, 更新相关本地缓存, 解决动态加载完,新路由映射无效的问题
        publisher.publishEvent(new RoutesRefreshedEvent(this));
    }

    /**
     * 加载路由配置
     *
     * @return
     */
    @Override
    protected Map<String, ZuulProperties.ZuulRoute> locateRoutes() {
        LinkedHashMap<String, ZuulProperties.ZuulRoute> routesMap = Maps.newLinkedHashMap();
        routesMap.putAll(super.locateRoutes());
        //从db中加载路由信息
        routesMap.putAll(loadRoutes());
        //优化一下配置
        LinkedHashMap<String, ZuulProperties.ZuulRoute> values = Maps.newLinkedHashMap();
        for (Map.Entry<String, ZuulProperties.ZuulRoute> entry : routesMap.entrySet()) {
            String path = entry.getKey();
            // Prepend with slash if not already present.
            if (!path.startsWith("/")) {
                path = "/" + path;
            }
            if (StringUtils.hasText(this.properties.getPrefix())) {
                path = this.properties.getPrefix() + path;
                if (!path.startsWith("/")) {
                    path = "/" + path;
                }
            }
            values.put(path, entry.getValue());
        }
        return values;
    }

    /**
     * 加载路由配置
     */
    public Map<String, ZuulRoute> loadRoutes() {
        Map<String, ZuulProperties.ZuulRoute> routes = Maps.newLinkedHashMap();
        routeList = new CopyOnWriteArrayList<>();
        try {
            routeList = jdbcTemplate.query("SELECT * FROM gateway_route WHERE status = 1", new RowMapper<GatewayRoute>() {
                @Override
                public GatewayRoute mapRow(ResultSet rs, int i) throws SQLException {
                    GatewayRoute result = new GatewayRoute();
                    result.setRouteId(rs.getLong("route_id"));
                    result.setPath(rs.getString("path"));
                    result.setServiceId(rs.getString("service_id"));
                    result.setUrl(rs.getString("url"));
                    result.setStatus(rs.getInt("status"));
                    result.setRetryable(rs.getInt("retryable"));
                    result.setStripPrefix(rs.getInt("strip_prefix"));
                    result.setIsPersist(rs.getInt("is_persist"));
                    result.setRouteName(rs.getString("route_name"));
                    return result;
                }
            });
            if (routeList != null && routeList.size() > 0) {
                for (GatewayRoute result : routeList) {
                    if (StringUtils.isEmpty(result.getPath())) {
                        continue;
                    }
                    if (StringUtils.isEmpty(result.getServiceId()) && StringUtils.isEmpty(result.getUrl())) {
                        continue;
                    }
                    ZuulProperties.ZuulRoute zuulRoute = new ZuulProperties.ZuulRoute();

                    BeanUtils.copyProperties(result, zuulRoute);
                    zuulRoute.setId(result.getRouteName());
                    routes.put(zuulRoute.getPath(), zuulRoute);
                }
            }
            log.info("=============加载动态路由:{}==============", routeList.size());
        } catch (Exception e) {
            log.error("加载动态路由错误:", e);
        }
        return routes;
    }


    public List<GatewayRoute> getRouteList() {
        return routeList;
    }

    public void setRouteList(List<GatewayRoute> routeList) {
        this.routeList = routeList;
    }

    /**
     * 远程刷新事件
     *
     * @param gatewayRemoteRefreshRouteEvent
     */
    @Override
    public void onApplicationEvent(RemoteRefreshRouteEvent gatewayRemoteRefreshRouteEvent) {
        doRefresh();
    }
}