package com.erp.gateway.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
import org.springframework.data.redis.core.ReactiveZSetOperations;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.time.Instant;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;

/**
 * API调用量监控过滤器
 * 
 * 功能：
 * 1. 记录每个API的调用次数（按分钟/小时/天）
 * 2. 记录响应时间和状态码
 * 3. 提供统计接口供监控系统查询
 */
@Slf4j
@Component
public class ApiCounterFilter implements GlobalFilter, Ordered {

    private static final String COUNTER_KEY_MINUTE = "gateway:api:minute:";
    private static final String COUNTER_KEY_HOUR = "gateway:api:hour:";
    private static final String COUNTER_KEY_DAY = "gateway:api:day:";
    private static final String COUNTER_KEY_TENANT = "gateway:api:tenant:";
    private static final String COUNTER_KEY_ENDPOINT = "gateway:api:endpoint:";
    private static final String REQUEST_LOG_KEY = "gateway:api:requests:";

    private final ReactiveStringRedisTemplate redisTemplate;

    public ApiCounterFilter(ReactiveStringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getPath().value();
        String method = request.getMethod().name();
        long startTime = System.currentTimeMillis();

        // 获取租户ID（从Header）
        String tenantId = request.getHeaders().getFirst("X-Tenant-ID");
        if (tenantId == null || tenantId.isEmpty()) {
            tenantId = "anonymous";
        }

        // 记录请求开始
        String requestId = request.getHeaders().getFirst("X-Request-ID");
        if (requestId == null) {
            requestId = java.util.UUID.randomUUID().toString();
        }

        // 添加Request-ID到响应头
        exchange.getResponse().getHeaders().add("X-Request-ID", requestId);

        return chain.filter(exchange)
                .then(Mono.fromRunnable(() -> {
                    long duration = System.currentTimeMillis() - startTime;
                    int statusCode = exchange.getResponse().getStatusCode() != null
                            ? exchange.getResponse().getStatusCode().value()
                            : 500;

                    // 异步记录（不阻塞响应）
                    try {
                        recordApiCall(path, method, tenantId, statusCode, duration);
                    } catch (Exception e) {
                        log.warn("记录API调用失败: path={}, error={}", path, e.getMessage());
                    }
                }));
    }

    /**
     * 记录API调用
     */
    private void recordApiCall(String path, String method, String tenantId, int statusCode, long durationMs) {
        ReactiveZSetOperations<String, String> zsetOps = redisTemplate.opsForZSet();
        long now = Instant.now().getEpochSecond();
        String minute = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm"));
        String hour = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH"));
        String day = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));

        // 1. 总体计数器（按时间粒度）
        zsetOps.incrementScore(COUNTER_KEY_MINUTE + minute, "total", 1);
        zsetOps.incrementScore(COUNTER_KEY_HOUR + hour, "total", 1);
        zsetOps.incrementScore(COUNTER_KEY_DAY + day, "total", 1);

        // 2. 按租户计数器
        zsetOps.incrementScore(COUNTER_KEY_TENANT + tenantId + ":" + day, "count", 1);
        if (statusCode >= 400) {
            zsetOps.incrementScore(COUNTER_KEY_TENANT + tenantId + ":" + day, "errors", 1);
        }

        // 3. 按端点计数器
        String endpointKey = COUNTER_KEY_ENDPOINT + path + ":" + day;
        zsetOps.incrementScore(endpointKey, "count", 1);
        if (statusCode >= 400) {
            zsetOps.incrementScore(endpointKey, "errors", 1);
        }
        // 记录响应时间用于P95/P99计算
        redisTemplate.opsForList().rightPush(
                "gateway:api:duration:" + path + ":" + day,
                String.format("%d:%d", now, durationMs)
        );

        // 4. 请求日志（保留最近1000条）
        String logJson = String.format(
                "{\"path\":\"%s\",\"method\":\"%s\",\"tenant\":\"%s\",\"status\":%d,\"duration\":%d,\"time\":%d}",
                path, method, tenantId, statusCode, durationMs, now
        );
        redisTemplate.opsForList().rightPush(REQUEST_LOG_KEY + day, logJson);
        redisTemplate.opsForList().trim(REQUEST_LOG_KEY + day, -1000, -1);

        // 设置过期时间
        redisTemplate.expire(COUNTER_KEY_MINUTE + minute, 2, TimeUnit.HOURS);
        redisTemplate.expire(COUNTER_KEY_HOUR + hour, 2, TimeUnit.DAYS);
        redisTemplate.expire(COUNTER_KEY_DAY + day, 7, TimeUnit.DAYS);
        redisTemplate.expire(COUNTER_KEY_TENANT + tenantId + ":" + day, 7, TimeUnit.DAYS);
        redisTemplate.expire(endpointKey, 7, TimeUnit.DAYS);
        redisTemplate.expire("gateway:api:duration:" + path + ":" + day, 1, TimeUnit.DAYS);
    }

    @Override
    public int getOrder() {
        // 高优先级，在认证过滤器之后执行
        return Ordered.LOWEST_PRECEDENCE - 100;
    }
}
