package com.ruoyi.web.modules.industryservice.support;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TimeZone;
import com.ruoyi.common.utils.StringUtils;

/**
 * IoT 数据增量同步共用工具。
 */
public final class IotSyncSupport
{
    /** 增量窗口起始时间相对库内最大事件时间的回退量（毫秒），避免边界漏数 */
    public static final long INCREMENTAL_OVERLAP_MS = 5L * 60 * 1000;

    /** 库内无历史数据时，默认回溯 24 小时 */
    public static final long DEFAULT_LOOKBACK_MS = 24L * 60 * 60 * 1000;

    /** sys_config 键：上次 IoT 增量同步成功时间（yyyy-MM-dd HH:mm:ss） */
    public static final String CONFIG_KEY_LAST_SYNC_TIME = "third_party.iot.last_sync_time";

    private static final SimpleDateFormat PARAM_TIME = createParamTimeFormat();

    private IotSyncSupport()
    {
    }

    /**
     * 根据库内最大事件时间计算增量起始时间；无历史则回溯 {@link #DEFAULT_LOOKBACK_MS}。
     * <p>仅在有本地数据时使用；表为空时应传 {@code null} 触发全量回补。</p>
     */
    public static Date resolveIncrementalStart(Date maxEventTime, Date endTime)
    {
        Date end = endTime != null ? endTime : new Date();
        if (maxEventTime == null)
        {
            return new Date(end.getTime() - DEFAULT_LOOKBACK_MS);
        }
        long startMs = maxEventTime.getTime() - INCREMENTAL_OVERLAP_MS;
        if (startMs < 0)
        {
            startMs = 0;
        }
        return new Date(startMs);
    }

    /**
     * 根据本地表行数与最大事件时间，解析全量或增量查询参数。
     * <p>
     * {@code activeCount == 0} 时不传任何时间过滤（全量分页）；否则按事件时间窗口增量拉取。
     * <p>行数统计包含全部记录，不按 {@code del_flag} 过滤。</p>
     * </p>
     */
    public static SyncQuery resolveSyncQuery(long activeCount, Date maxEventTime, Date endTime, Date configUpdatedAfter)
    {
        Date end = endTime != null ? endTime : new Date();
        if (activeCount <= 0)
        {
            return SyncQuery.fullSync();
        }
        Date start = maxEventTime != null
                ? resolveIncrementalStart(maxEventTime, end)
                : new Date(end.getTime() - DEFAULT_LOOKBACK_MS);
        Date updatedAfter = configUpdatedAfter;
        return SyncQuery.incremental(start, end, updatedAfter);
    }

    /** IoT 同步查询窗口：全量或增量 */
    public static final class SyncQuery
    {
        private final boolean fullSync;
        private final Date startTime;
        private final Date endTime;
        private final Date updatedAfter;

        private SyncQuery(boolean fullSync, Date startTime, Date endTime, Date updatedAfter)
        {
            this.fullSync = fullSync;
            this.startTime = startTime;
            this.endTime = endTime;
            this.updatedAfter = updatedAfter;
        }

        public static SyncQuery fullSync()
        {
            return new SyncQuery(true, null, null, null);
        }

        public static SyncQuery incremental(Date startTime, Date endTime, Date updatedAfter)
        {
            return new SyncQuery(false, startTime, endTime, updatedAfter);
        }

        public boolean isFullSync()
        {
            return fullSync;
        }

        public Date getStartTime()
        {
            return startTime;
        }

        public Date getEndTime()
        {
            return endTime;
        }

        public Date getUpdatedAfter()
        {
            return updatedAfter;
        }
    }

    /**
     * 库内无该表数据时不传 {@code updatedAfter}，避免表清空后仍被 sys_config 水印拦截。
     * @deprecated 使用 {@link #resolveSyncQuery(long, Date, Date, Date)}
     */
    @Deprecated
    public static Date resolveUpdatedAfter(Date maxEventTime, Date configLastSyncTime)
    {
        if (maxEventTime == null)
        {
            return null;
        }
        return configLastSyncTime;
    }

    /**
     * 库内无该表数据时不传 {@code endTime}，与 {@code startTime=null} 组合为全量分页拉取。
     * @deprecated 使用 {@link #resolveSyncQuery(long, Date, Date, Date)}
     */
    @Deprecated
    public static Date resolveIncrementalEnd(Date maxEventTime, Date endTime)
    {
        return maxEventTime == null ? null : endTime;
    }

    /**
     * 构建开放接口增量查询参数：{@code updatedAfter}、{@code startTime}、{@code endTime}。
     */
    public static Map<String, String> buildIncrementalQueryParams(Date startTime, Date endTime, Date updatedAfter)
    {
        Map<String, String> params = new LinkedHashMap<>();
        if (updatedAfter != null)
        {
            params.put("updatedAfter", formatParamTime(updatedAfter));
        }
        appendTimeRangeParams(params, startTime, endTime);
        return params;
    }

    /**
     * 耳标温度步数查询参数：{@code earTagNumber} 必传；可选 {@code startTime}/{@code endTime} 监测时间窗口。
     */
    public static Map<String, String> buildEarTagQueryParams(String earTagNumber, Date startTime, Date endTime)
    {
        Map<String, String> params = new LinkedHashMap<>();
        if (StringUtils.isNotEmpty(earTagNumber))
        {
            params.put("earTagNumber", earTagNumber.trim());
        }
        appendTimeRangeParams(params, startTime, endTime);
        return params;
    }

    /**
     * 环境监测查询参数：{@code farmId}/{@code enclosureId}；可选 {@code startTime}/{@code endTime} 监测时间窗口。
     */
    public static Map<String, String> buildEnvMonitoringQueryParams(Long farmId, Long enclosureId,
            Date startTime, Date endTime)
    {
        Map<String, String> params = new LinkedHashMap<>();
        if (farmId != null)
        {
            params.put("farmId", String.valueOf(farmId));
        }
        if (enclosureId != null)
        {
            params.put("enclosureId", String.valueOf(enclosureId));
        }
        appendTimeRangeParams(params, startTime, endTime);
        return params;
    }

    /**
     * 牛只称重查询参数：{@code cattleId} 必传（档案 {@code external_id}）；
     * 可选 {@code startTime}/{@code endTime} 称重时间窗口。
     */
    public static Map<String, String> buildWeighingQueryParams(Long cattleId, Date startTime, Date endTime)
    {
        Map<String, String> params = new LinkedHashMap<>();
        if (cattleId != null)
        {
            params.put("cattleId", String.valueOf(cattleId));
        }
        appendTimeRangeParams(params, startTime, endTime);
        return params;
    }

    private static void appendTimeRangeParams(Map<String, String> params, Date startTime, Date endTime)
    {
        if (startTime != null)
        {
            params.put("startTime", formatParamTime(startTime));
        }
        if (endTime != null)
        {
            params.put("endTime", formatParamTime(endTime));
        }
    }

    /** 格式化为第三方接口时间参数 */
    public static String formatParamTime(Date time)
    {
        if (time == null)
        {
            return null;
        }
        synchronized (PARAM_TIME)
        {
            return PARAM_TIME.format(time);
        }
    }

    /** 解析 sys_config 中保存的上次同步时间 */
    public static Date parseConfigSyncTime(String value)
    {
        if (StringUtils.isEmpty(value))
        {
            return null;
        }
        return FarmingSyncSupport.parseThirdPartyTime(value.trim(), null);
    }

    /** 判断事件时间是否落在增量窗口内（闭区间） */
    public static boolean withinIncrementalWindow(Date eventTime, Date startTime, Date endTime)
    {
        if (eventTime == null)
        {
            return false;
        }
        if (startTime != null && eventTime.before(startTime))
        {
            return false;
        }
        if (endTime != null && eventTime.after(endTime))
        {
            return false;
        }
        return true;
    }

    private static SimpleDateFormat createParamTimeFormat()
    {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        df.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        df.setLenient(false);
        return df;
    }
}
