package com.sikey.wa04.business.infrastructure.adapter.weather;

import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.sikey.wa04.business.domain.entity.Weather;
import com.sikey.wa04.business.infrastructure.dto.model.MapWeatherModel;
import com.sikey.wa04.business.infrastructure.repository.WeatherRepository;
import com.sikey.wa04.common.enums.WeatherPhenomenon;
import jakarta.annotation.Resource;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.apache.http.client.utils.URIBuilder;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.URISyntaxException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 百度天气
 */
@Setter
@Slf4j
@Component
public class BaiduWeatherService {
    /**
     * 百度获取填写的接口地址
     */
    private final String weatherUrl = "https://api.map.baidu.com/weather/v1/?";

    // ak API控制台申请得到的ak
    private final String ak = "suBnrGHXb5K7iPUtipyLPDBzPHlnSMqc";

    private final String output   = "json";
    private final String dataType = "all";

    private volatile OkHttpClient httpClient;

    private OkHttpClient getHttpClient() {
        if (httpClient == null) {
            synchronized (this) {
                if (httpClient == null) {
                    httpClient = new OkHttpClient.Builder()  // 使用 Builder 模式创建 OkHttpClient
                            .connectTimeout(60, TimeUnit.SECONDS)  // 设置连接超时时间
                            .readTimeout(60, TimeUnit.SECONDS)     // 设置读取超时时间
                            .writeTimeout(60, TimeUnit.SECONDS)    // 设置写入超时时间
                            .build();  // 构建 OkHttpClient 实例
                }
            }
        }
        return httpClient;
    }

    private static final ObjectMapper objectMapper;

    static {
        objectMapper = new Jackson2ObjectMapperBuilder().build();
        objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
    }

    @Resource
    private WeatherRepository weatherRepository;

    /**
     * 百度天气
     *
     * @param cityId 城市id
     * @return
     */
    public Weather getWeather(String cityId)  {
        //查询当前数据库是否有对应城市Id的天气
        MapWeatherModel weatherModel = weatherRepository.findByCityId(cityId);
        Instant curTime = Instant.now();
        if (weatherModel != null) {
            //该数据创建时间加4小时后早于当前时间（已超过4小时）
            if (isOlderThan4Hours(weatherModel.getUpdatedAt())) {
                log.info("[百度天气]该数据创建时间加4小时后早于当前时间（已超过4小时），需重新获取；cityId：{}", cityId);
                //获取新天气
                WeatherData weatherData = requestBaiDuWeather(cityId);

                //组装天气数据存入数据库
                WeatherDataResultNow now = weatherData.result().now();
                WeatherDataResultForecasts forecast = weatherData.result().forecasts().getFirst();
                //更新新天气
                weatherModel.setTextCode(WeatherPhenomenon.valueOfName(forecast.text_day).getBaiduCode());
                weatherModel.setNow(now.temp());
                weatherModel.setDate(forecast.date);
                weatherModel.setHigh(forecast.high);
                weatherModel.setLow(forecast.low);
                weatherModel.setUpdatedAt(curTime);
                weatherRepository.save(weatherModel);
            }
        } else {
            //不存在则查询百度天气获取
            WeatherData weatherData = requestBaiDuWeather(cityId);
            weatherModel = new MapWeatherModel();

            WeatherDataResultNow now = weatherData.result().now();
            WeatherDataResultForecasts forecast = weatherData.result().forecasts().getFirst();

            //更新新天气
            weatherModel.setCityId(cityId);
            weatherModel.setTextCode(WeatherPhenomenon.valueOfName(forecast.text_day).getBaiduCode());
            weatherModel.setNow(now.temp());
            weatherModel.setDate(forecast.date);
            weatherModel.setHigh(forecast.high);
            weatherModel.setLow(forecast.low);
            weatherModel.setCreatedAt(curTime);
            weatherModel.setUpdatedAt(curTime);
            //保存新天气
            weatherRepository.save(weatherModel);
        }
        log.info("[百度天气]天气查询结果；cityId：{}； weatherModel：{}", cityId, JSONUtil.toJsonPrettyStr(weatherModel));
        //获取自定义的天气code
        String myTextCode = WeatherPhenomenon.valueOfBaiduCode(weatherModel.getTextCode()).getMyCode();
        //返回天气
        return new Weather()
                .setCityId(weatherModel.getCityId())
                .setTextCode(weatherModel.getTextCode())
                .setMyTextCode(myTextCode)
                .setHigh(weatherModel.getHigh())
                .setLow(weatherModel.getLow())
                .setNow(weatherModel.getNow());
    }

    /**
     * 发起request请求百度天气获取最新天气信息
     *
     * @param cityId
     * @return
     */
    public WeatherData requestBaiDuWeather(String cityId)  {
        //组装请求地址和参数
        String reqUrl = buildWeatherUrl(cityId);

        OkHttpClient client = getHttpClient();
        Request request = new Request.Builder()
                .url(reqUrl) // OkHttp直接接受完整URL
                .build();

        // 使用newCall替换send
        try (Response response = client.newCall(request).execute()) { // 同步请求
            if (!response.isSuccessful()) {
                throw new RuntimeException("[获取百度天气] Request failed with code: " + response.code());
            }

            ResponseBody body = response.body();
            if (body == null) {
                throw new RuntimeException("[获取百度天气] Response body is null");
            }

            String bodyString = body.string();
            WeatherData  weatherData  = objectMapper.readValue(bodyString, WeatherData.class);
            if (weatherData.status != 0){
                log.info("[获取百度天气]status 不等于 0 ；message:{}",weatherData.message);
            }
            return weatherData;
        } catch (IOException e) {
            throw new RuntimeException("[获取百度天气]失败eror city: " + cityId, e);
        }
    }

    /**
     * 使用Instant检查数据是否超过4小时
     * @param createTime 数据对象
     * @return true如果创建时间+4小时早于当前时间
     */
    public static boolean isOlderThan4Hours(Instant createTime) {
        // 当前时间
        Instant now = Instant.now();

        // 创建时间加4小时
        Instant createPlus4Hours = createTime.plus(4, ChronoUnit.HOURS);

        // 比较
        return createPlus4Hours.isBefore(now);
    }

    /**
     * 组装请求地址和参数
     * @param cityId
     * @return
     */
    public String buildWeatherUrl(String cityId) {
        try {
            return new URIBuilder(weatherUrl)
                    .addParameter("district_id", cityId)
                    .addParameter("data_type", dataType)
                    .addParameter("output", output)
                    .addParameter("ak", ak)
                    .build()
                    .toString();
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException("[获取百度天气]buildWeatherUrl URL Invalid", e);
        }
    }

    record WeatherData(Integer status, WeatherDataResult result, String message) {
    }

    private record WeatherDataResult(WeatherDataResultLocation location, WeatherDataResultNow now, List<WeatherDataResultForecasts> forecasts) {
    }


    private record WeatherDataResultLocation(String country, String province, String city, String name, String id) {
    }

    private record WeatherDataResultNow(String text, Integer temp, Integer feels_like, Integer rh, String wind_class, String wind_dir, String uptime) {
    }


    private record WeatherDataResultForecasts(String text_day, String text_night, Integer high, Integer low, String wc_day, String wd_day, String wc_night, String wd_night, String date, String week) {
    }

}
