package com.sckw.transport.api.feign;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import feign.Logger;
import feign.Request;
import feign.Response;
import feign.Retryer;
import feign.codec.Decoder;
import feign.codec.ErrorDecoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.util.StreamUtils;

import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

/**
 * 远程服务 Feign 配置
 * @author system
 * @date 2024
 */
@Slf4j
@Configuration
public class VehicleTraceFeignConfig {

    /**
     * 配置日志级别
     */
    @Bean
    public Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }

    /**
     * 配置请求超时时间
     * 连接超时：5秒
     * 读取超时：10秒
     */
    @Bean
    public Request.Options options() {
        return new Request.Options(
            5000, TimeUnit.MILLISECONDS,  // 连接超时
            10000, TimeUnit.MILLISECONDS, // 读取超时
            true  // 跟随重定向
        );
    }

    /**
     * 配置重试策略
     * 最多重试2次，初始间隔100ms，最大间隔1000ms
     */
    @Bean
    public Retryer retryer() {
        return new Retryer.Default(100, 1000, 2);
    }

    /**
     * 自定义错误解码器
     */
    @Bean
    public ErrorDecoder errorDecoder() {
        return (methodKey, response) -> {
            String responseBody = "";
            try {
                if (response.body() != null) {
                    responseBody = new String(StreamUtils.copyToByteArray(response.body().asInputStream()), StandardCharsets.UTF_8);
                }
            } catch (IOException e) {
                log.warn("读取响应体失败", e);
            }
            log.error("远程服务调用失败, 方法: {}, 状态码: {}, 响应体: {}",
                methodKey, response.status(), responseBody);
            return new RuntimeException("远程服务调用失败: " + response.reason() + ", 响应: " + responseBody);
        };
    }

    /**
     * 自定义 Feign 解码器，支持 Java 8 时间类型（LocalDateTime 等）
     * 支持 ISO 8601 格式：2025-12-05T00:00:00.000+00:00
     */
    @Bean
    public Decoder feignDecoder() {
        ObjectMapper objectMapper = new ObjectMapper();
        // 注册 Java 8 时间模块（自动支持 ISO 8601 格式）
        objectMapper.registerModule(new JavaTimeModule());
        // 禁用将日期写为时间戳（使用 ISO 8601 字符串格式）
        objectMapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        // 忽略未知属性，避免外部服务返回多余字段导致解析失败
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 允许空对象
        objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
        // 允许单值作为数组
        objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
        // 允许空值
        objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true);
        
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(objectMapper);
        ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(converter);
        
        // 包装解码器，添加错误日志
        Decoder defaultDecoder = new ResponseEntityDecoder(new SpringDecoder(objectFactory));
        return new Decoder() {
            @Override
            public Object decode(Response response, Type type) throws IOException {
                // 先保存响应体内容，以便在解码失败时记录日志
                String responseBody = "";
                byte[] bodyBytes = null;
                if (response.body() != null) {
                    try {
                        bodyBytes = StreamUtils.copyToByteArray(response.body().asInputStream());
                        responseBody = new String(bodyBytes, StandardCharsets.UTF_8);
                    } catch (IOException ex) {
                        log.warn("读取响应体失败", ex);
                    }
                }
                
                try {
                    // 使用保存的响应体重新构建 Response，因为流已经被读取
                    if (bodyBytes != null) {
                        Response rebuiltResponse = response.toBuilder()
                            .body(bodyBytes)
                            .build();
                        return defaultDecoder.decode(rebuiltResponse, type);
                    } else {
                        return defaultDecoder.decode(response, type);
                    }
                } catch (Exception e) {
                    log.error("Feign 解码失败, 类型: {}, 状态码: {}, 响应体: {}, 错误: {}", 
                        type, response.status(), responseBody, e.getMessage(), e);
                    throw e;
                }
            }
        };
    }
}
