package com.erp.gateway.sentinel;

import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import com.alibaba.csp.sentinel.datasource.apollo.ApolloDataSource;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

@Slf4j
@Configuration
public class SentinelConfig {

    @Autowired
    private ObjectMapper objectMapper;

    @Value("${spring.cloud.nacos.discovery.server-addr:localhost:8848}")
    private String nacosServer;

    /**
     * Sentinel Gateway Filter
     */
    @Bean
    public GatewayFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    /**
     * Sentinel Block Exception Handler
     */
    @Bean
    public WebExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return (ServerWebExchange exchange, Throwable ex) -> {
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
            response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");

            Map<String, Object> result = new HashMap<>();
            result.put("code", 429);
            result.put("message", "请求过于频繁，请稍后重试");
            result.put("data", null);

            try {
                byte[] bytes = objectMapper.writeValueAsBytes(result);
                DataBuffer buffer = response.bufferFactory().wrap(bytes);
                return response.writeWith(Mono.just(buffer));
            } catch (Exception e) {
                log.error("Sentinel block handler error", e);
                return response.setComplete();
            }
        };
    }

    /**
     * Initialize Sentinel API Definitions
     */
    @PostConstruct
    public void initSentinelApiDefinitions() {
        Set<ApiDefinition> definitions = new HashSet<>();

        // Auth API
        definitions.add(createApiDefinition("auth_api", "/api/auth/**"));

        // User API
        definitions.add(createApiDefinition("user_api", "/api/user/**"));

        // Order API
        definitions.add(createApiDefinition("order_api", "/api/order/**"));

        // Product API
        definitions.add(createApiDefinition("product_api", "/api/product/**"));

        // Warehouse API
        definitions.add(createApiDefinition("warehouse_api", "/api/warehouse/**"));

        GatewayApiDefinitionManager.loadApiDefinitions(definitions);

        log.info("Sentinel API definitions initialized");
    }

    private ApiDefinition createApiDefinition(String apiName, String pathPattern) {
        ApiDefinition definition = new ApiDefinition(apiName);

        ApiPathPredicateItem predicateItem = new ApiPathPredicateItem();
        predicateItem.setPattern(pathPattern);
        predicateItem.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX);

        Set<ApiPredicateItem> items = new HashSet<>();
        items.add(predicateItem);
        definition.setPredicateItems(items);

        return definition;
    }
}
