package cn.novalon.gym.manage.gateway.audit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 审计日志服务
 * 
 * 文件定义：记录网关请求的审计日志
 * 涉及业务：安全审计、访问追踪、问题排查
 * 
 * 审计内容：
 * 1. 请求信息：方法、路径、查询参数、请求头
 * 2. 响应信息：状态码、响应时间
 * 3. 安全事件：认证失败、授权失败、限流触发等
 * 4. 错误信息：异常类型、错误消息
 * 
 * @author 张翔
 * @date 2026-03-26
 */
@Service
public class AuditLogService {

    private static final Logger auditLogger = LoggerFactory.getLogger("AUDIT_LOG");

    private final Map<String, AuditEntry> auditEntries = new ConcurrentHashMap<>();

    public void logRequest(ServerHttpRequest request, String userId) {
        String requestId = generateRequestId(request);

        AuditEntry entry = new AuditEntry();
        entry.setRequestId(requestId);
        entry.setMethod(request.getMethod().name());
        entry.setPath(request.getPath().value());
        entry.setUserId(userId);
        entry.setClientIp(getClientIp(request));

        auditEntries.put(requestId, entry);

        auditLogger.info("[REQUEST] {} {} - User: {}, IP: {}, RequestId: {}",
                entry.getMethod(),
                entry.getPath(),
                entry.getUserId(),
                entry.getClientIp(),
                entry.getRequestId());
    }

    public void logResponse(String requestId, int statusCode, long durationMs) {
        AuditEntry entry = auditEntries.get(requestId);

        if (entry != null) {
            entry.setStatusCode(statusCode);
            entry.setDurationMs(durationMs);

            auditLogger.info("[RESPONSE] {} {} - Status: {}, Duration: {}ms, RequestId: {}",
                    entry.getMethod(),
                    entry.getPath(),
                    entry.getStatusCode(),
                    entry.getDurationMs(),
                    entry.getRequestId());

            auditEntries.remove(requestId);
        }
    }

    public void logSecurityEvent(String requestId, String eventType, String details) {
        AuditEntry entry = auditEntries.get(requestId);

        if (entry != null) {
            auditLogger.warn("[SECURITY] {} - Event: {}, Details: {}, User: {}, IP: {}, RequestId: {}",
                    entry.getPath(),
                    eventType,
                    details,
                    entry.getUserId(),
                    entry.getClientIp(),
                    entry.getRequestId());
        } else {
            auditLogger.warn("[SECURITY] Event: {}, Details: {}, RequestId: {}",
                    eventType,
                    details,
                    requestId);
        }
    }

    public void logError(String requestId, String errorType, String errorMessage) {
        AuditEntry entry = auditEntries.get(requestId);

        if (entry != null) {
            auditLogger.error("[ERROR] {} {} - Error: {}, Message: {}, User: {}, IP: {}, RequestId: {}",
                    entry.getMethod(),
                    entry.getPath(),
                    errorType,
                    errorMessage,
                    entry.getUserId(),
                    entry.getClientIp(),
                    entry.getRequestId());
        } else {
            auditLogger.error("[ERROR] Error: {}, Message: {}, RequestId: {}",
                    errorType,
                    errorMessage,
                    requestId);
        }
    }

    private String generateRequestId(ServerHttpRequest request) {
        String requestId = request.getHeaders().getFirst("X-Request-Id");

        if (requestId == null || requestId.isEmpty()) {
            requestId = String.format("%s-%d-%s",
                    request.getMethod().name().toLowerCase(),
                    System.currentTimeMillis(),
                    Integer.toHexString(request.hashCode()));
        }

        return requestId;
    }

    private String getClientIp(ServerHttpRequest request) {
        String ip = request.getHeaders().getFirst("X-Forwarded-For");

        if (ip == null || ip.isEmpty()) {
            ip = request.getHeaders().getFirst("X-Real-IP");
        }

        if (ip == null || ip.isEmpty()) {
            ip = request.getRemoteAddress() != null ? request.getRemoteAddress().getAddress().getHostAddress()
                    : "unknown";
        }

        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }

        return ip;
    }

    private static class AuditEntry {
        private String requestId;
        private String method;
        private String path;
        private String userId;
        private String clientIp;
        private int statusCode;
        private long durationMs;

        public String getRequestId() {
            return requestId;
        }

        public void setRequestId(String requestId) {
            this.requestId = requestId;
        }

        public String getMethod() {
            return method;
        }

        public void setMethod(String method) {
            this.method = method;
        }

        public String getPath() {
            return path;
        }

        public void setPath(String path) {
            this.path = path;
        }

        public String getUserId() {
            return userId;
        }

        public void setUserId(String userId) {
            this.userId = userId;
        }

        public String getClientIp() {
            return clientIp;
        }

        public void setClientIp(String clientIp) {
            this.clientIp = clientIp;
        }

        public int getStatusCode() {
            return statusCode;
        }

        public void setStatusCode(int statusCode) {
            this.statusCode = statusCode;
        }

        public long getDurationMs() {
            return durationMs;
        }

        public void setDurationMs(long durationMs) {
            this.durationMs = durationMs;
        }
    }
}
