package com.xzzn.ems.service.impl;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.xzzn.common.exception.ServiceException;
import com.xzzn.common.utils.StringUtils;
import com.xzzn.common.utils.http.HttpUtils;
import com.xzzn.ems.config.WeatherApiProperties;
import com.xzzn.ems.domain.EmsSiteSetting;
import com.xzzn.ems.domain.vo.StatisAmmeterDateRequest;
import com.xzzn.ems.domain.vo.WeatherSyncResultVo;
import com.xzzn.ems.mapper.EmsSiteSettingMapper;
import com.xzzn.ems.mapper.EmsSiteWeatherDayMapper;
import com.xzzn.ems.service.IEmsWeatherSyncService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

@Service
public class EmsWeatherSyncServiceImpl implements IEmsWeatherSyncService {
    private static final Logger log = LoggerFactory.getLogger(EmsWeatherSyncServiceImpl.class);
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    @Autowired
    private WeatherApiProperties weatherApiProperties;
    @Autowired
    private EmsSiteSettingMapper emsSiteSettingMapper;
    @Autowired
    private EmsSiteWeatherDayMapper emsSiteWeatherDayMapper;

    @Override
    public WeatherSyncResultVo syncWeatherByDateRange(StatisAmmeterDateRequest requestVo) {
        if (!weatherApiProperties.isEnabled()) {
            throw new ServiceException("天气同步未启用，请先设置 weather.api.enabled=true");
        }
        if (requestVo == null || StringUtils.isEmpty(requestVo.getSiteId())
                || StringUtils.isEmpty(requestVo.getStartTime())
                || StringUtils.isEmpty(requestVo.getEndTime())) {
            throw new ServiceException("siteId、startTime、endTime 不能为空");
        }
        LocalDate startDate = parseDate(requestVo.getStartTime(), "startTime");
        LocalDate endDate = parseDate(requestVo.getEndTime(), "endTime");
        if (endDate.isBefore(startDate)) {
            throw new ServiceException("endTime 不能早于 startTime");
        }

        EmsSiteSetting siteSetting = emsSiteSettingMapper.selectEmsSiteSettingBySiteId(requestVo.getSiteId());
        if (siteSetting == null) {
            throw new ServiceException("未找到站点配置，siteId=" + requestVo.getSiteId());
        }
        BigDecimal latitude = siteSetting.getLatitude();
        BigDecimal longitude = siteSetting.getLongitude();
        if (latitude == null || longitude == null) {
            throw new ServiceException("站点缺少经纬度配置，siteId=" + requestVo.getSiteId());
        }

        String url = buildWeatherUrl(latitude, longitude, startDate, endDate);
        String response = HttpUtils.sendGet(url);
        if (StringUtils.isEmpty(response)) {
            throw new ServiceException("调用天气接口失败，返回为空");
        }
        JSONObject root = JSONObject.parseObject(response);
        JSONObject daily = root == null ? null : root.getJSONObject("daily");
        JSONArray dates = daily == null ? null : daily.getJSONArray("time");
        JSONArray codes = daily == null ? null : daily.getJSONArray("weather_code");
        if (dates == null || codes == null || dates.size() == 0 || dates.size() != codes.size()) {
            throw new ServiceException("天气接口返回格式异常，daily.time/weather_code 不可用");
        }

        WeatherSyncResultVo resultVo = new WeatherSyncResultVo();
        resultVo.setSiteId(requestVo.getSiteId());
        resultVo.setStartTime(requestVo.getStartTime());
        resultVo.setEndTime(requestVo.getEndTime());
        resultVo.setTotalDays(dates.size());

        int updated = 0;
        int inserted = 0;
        for (int i = 0; i < dates.size(); i++) {
            String dateStr = dates.getString(i);
            int weatherCode = codes.getIntValue(i);
            String weatherDesc = mapWeatherDesc(weatherCode);

            int updateRows = emsSiteWeatherDayMapper.updateWeatherDesc(
                    requestVo.getSiteId(), dateStr, weatherDesc, weatherCode);
            if (updateRows > 0) {
                updated++;
                continue;
            }

            int count = emsSiteWeatherDayMapper.selectCountBySiteAndDate(requestVo.getSiteId(), dateStr);
            if (count == 0) {
                emsSiteWeatherDayMapper.insertSiteWeatherDay(
                        requestVo.getSiteId(), dateStr, weatherDesc, weatherCode, "open-meteo");
                inserted++;
                continue;
            }

            // 理论上不会到这里，防御性兜底再更新一次
            updated += emsSiteWeatherDayMapper.updateWeatherDesc(
                    requestVo.getSiteId(), dateStr, weatherDesc, weatherCode);
        }
        resultVo.setUpdatedDays(updated);
        resultVo.setInsertedDays(inserted);
        resultVo.setSuccessDays(updated + inserted);
        resultVo.setMessage("天气同步完成");

        log.info("天气同步完成, siteId: {}, startTime: {}, endTime: {}, totalDays: {}, updatedDays: {}, insertedDays: {}",
                requestVo.getSiteId(), requestVo.getStartTime(), requestVo.getEndTime(),
                resultVo.getTotalDays(), updated, inserted);
        return resultVo;
    }

    private LocalDate parseDate(String value, String fieldName) {
        try {
            return LocalDate.parse(value, DATE_FORMATTER);
        } catch (Exception e) {
            throw new ServiceException(fieldName + " 格式错误，应为 yyyy-MM-dd");
        }
    }

    private String buildWeatherUrl(BigDecimal latitude, BigDecimal longitude, LocalDate startDate, LocalDate endDate) {
        StringBuilder builder = new StringBuilder();
        builder.append(weatherApiProperties.getBaseUrl())
                .append("?latitude=").append(latitude.stripTrailingZeros().toPlainString())
                .append("&longitude=").append(longitude.stripTrailingZeros().toPlainString())
                .append("&start_date=").append(startDate.format(DATE_FORMATTER))
                .append("&end_date=").append(endDate.format(DATE_FORMATTER))
                .append("&daily=weather_code")
                .append("&timezone=").append(urlEncode(weatherApiProperties.getTimezone()));
        if (StringUtils.isNotEmpty(weatherApiProperties.getApiKey())) {
            builder.append("&apikey=").append(urlEncode(weatherApiProperties.getApiKey()));
        }
        return builder.toString();
    }

    private String urlEncode(String value) {
        try {
            return URLEncoder.encode(value, StandardCharsets.UTF_8.name());
        } catch (UnsupportedEncodingException e) {
            throw new ServiceException("URL 编码失败");
        }
    }

    private String mapWeatherDesc(int weatherCode) {
        switch (weatherCode) {
            case 0:
                return "晴";
            case 1:
            case 2:
            case 3:
                return "多云";
            case 45:
            case 48:
                return "雾";
            case 51:
            case 53:
            case 55:
                return "毛毛雨";
            case 56:
            case 57:
                return "冻毛毛雨";
            case 61:
            case 63:
            case 65:
                return "雨";
            case 66:
            case 67:
                return "冻雨";
            case 71:
            case 73:
            case 75:
                return "雪";
            case 77:
                return "雪粒";
            case 80:
            case 81:
            case 82:
                return "阵雨";
            case 85:
            case 86:
                return "阵雪";
            case 95:
                return "雷暴";
            case 96:
            case 99:
                return "雷暴伴冰雹";
            default:
                return "未知(" + weatherCode + ")";
        }
    }
}
