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

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.ruoyi.web.modules.diagnosis.support.AiConsultConstants;
import com.ruoyi.web.modules.industryservice.support.BreedingIndustryRules;
import com.ruoyi.web.modules.screen.domain.dto.LivestockResourceBandCountDto;
import com.ruoyi.web.modules.screen.domain.dto.LivestockResourceCategoryCountDto;
import com.ruoyi.web.modules.screen.domain.dto.LivestockResourceDateCountDto;
import com.ruoyi.web.modules.screen.domain.dto.LivestockResourceOverviewDto;
import com.ruoyi.web.modules.screen.domain.dto.LivestockResourceUserStructureDto;
import com.ruoyi.web.modules.screen.domain.vo.LivestockResourceActivityTrendVo;
import com.ruoyi.web.modules.screen.domain.vo.LivestockResourceCategoryItemVo;
import com.ruoyi.web.modules.screen.domain.vo.LivestockResourceCategoryShareVo;
import com.ruoyi.web.modules.screen.domain.vo.LivestockResourceDailyPointVo;
import com.ruoyi.web.modules.screen.domain.vo.LivestockResourceHourlyHeatVo;
import com.ruoyi.web.modules.screen.domain.vo.LivestockResourceHourlyPointVo;
import com.ruoyi.web.modules.screen.domain.vo.LivestockResourceKeywordVo;
import com.ruoyi.web.modules.screen.domain.vo.LivestockResourceOverviewVo;
import com.ruoyi.web.modules.screen.domain.vo.LivestockResourceReplyStatusVo;
import com.ruoyi.web.modules.screen.domain.vo.LivestockResourceSessionBandVo;
import com.ruoyi.web.modules.screen.domain.vo.LivestockResourceStatusItemVo;
import com.ruoyi.web.modules.screen.domain.vo.LivestockResourceUserStructureVo;

/**
 * 大屏畜牧资源口径组装与纯计算。
 */
public final class LivestockResourceScreenSupport
{
    private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    private static final DateTimeFormatter DATE_TIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private static final DateTimeFormatter HOUR_FMT = DateTimeFormatter.ofPattern("HH:mm");

    private static final int RATIO_SCALE = 1;

    private static final Pattern CN_TOKEN = Pattern.compile("[\\u4e00-\\u9fa5]{2,}");

    private static final Pattern EN_TOKEN = Pattern.compile("[A-Za-z]{2,}");

    private static final Set<String> STOP_WORDS = new HashSet<>(Arrays.asList(
            "的", "了", "是", "在", "我", "有", "和", "就", "不", "人", "都", "一", "一个", "上", "也", "很",
            "到", "说", "要", "去", "你", "会", "着", "没有", "看", "好", "自己", "这", "他", "她", "它",
            "吗", "呢", "啊", "吧", "那", "什么", "怎么", "为什么", "可以", "已经", "还是", "如果"));

    private static final String[][] CATEGORY_ORDER = {
            {"1", "养殖技巧"},
            {"2", "疾病诊疗"},
            {"3", "用药指导"},
            {"4", "设备操作"},
            {LivestockResourceScreenRules.CATEGORY_UNCAT, "未分类"}
    };

    private static final String[][] BAND_ORDER = {
            {LivestockResourceScreenRules.BAND_LIGHT, "轻度"},
            {LivestockResourceScreenRules.BAND_NORMAL, "一般"},
            {LivestockResourceScreenRules.BAND_ACTIVE, "活跃"},
            {LivestockResourceScreenRules.BAND_DEEP, "深度"}
    };

    private LivestockResourceScreenSupport()
    {
    }

    public static String formatStatDateTime(LocalDateTime dateTime)
    {
        return dateTime.format(DATE_TIME_FMT);
    }

    public static boolean isCurrentStatYear(int statYear)
    {
        return statYear == LocalDate.now(ZoneId.of(BreedingIndustryRules.ZONE_SHANGHAI)).getYear();
    }

    public static LivestockResourceOverviewVo buildOverview(
            LivestockResourceOverviewDto yearMetrics,
            Long todayActiveUsers,
            Long todayNewUsers,
            Long todaySessions,
            Long todayQuestionCount,
            boolean includeToday)
    {
        LivestockResourceOverviewVo vo = new LivestockResourceOverviewVo();
        if (yearMetrics == null)
        {
            vo.setTotalUsers(0);
            vo.setTotalSessions(0);
            vo.setTotalQuestionCount(0);
            vo.setAvgResponseSeconds(BigDecimal.ZERO.setScale(RATIO_SCALE, RoundingMode.HALF_UP));
        }
        else
        {
            vo.setTotalUsers(safeLong(yearMetrics.getTotalUsers()));
            vo.setTotalSessions(safeLong(yearMetrics.getTotalSessions()));
            vo.setTotalQuestionCount(safeLong(yearMetrics.getTotalQuestionCount()));
            vo.setAvgResponseSeconds(toAvgSeconds(yearMetrics.getAvgCostTimeMs()));
        }
        if (includeToday)
        {
            vo.setTodayActiveUsers(safeLong(todayActiveUsers));
            vo.setTodayNewUsers(safeLong(todayNewUsers));
            vo.setTodaySessions(safeLong(todaySessions));
            vo.setTodayQuestionCount(safeLong(todayQuestionCount));
        }
        else
        {
            vo.setTodayActiveUsers(0);
            vo.setTodayNewUsers(0);
            vo.setTodaySessions(0);
            vo.setTodayQuestionCount(0);
        }
        return vo;
    }

    public static LivestockResourceActivityTrendVo buildActivityTrend(
            List<LivestockResourceDateCountDto> sessionRows,
            List<LivestockResourceDateCountDto> questionRows,
            LocalDate statDate)
    {
        Map<String, Long> sessions = toCountMap(sessionRows);
        Map<String, Long> questions = toCountMap(questionRows);
        List<LivestockResourceDailyPointVo> series = new ArrayList<>();
        for (int i = LivestockResourceScreenRules.ACTIVITY_DAYS - 1; i >= 0; i--)
        {
            LocalDate day = statDate.minusDays(i);
            String key = day.format(DATE_FMT);
            LivestockResourceDailyPointVo point = new LivestockResourceDailyPointVo();
            point.setDate(key);
            point.setSessionCount(sessions.getOrDefault(key, 0L));
            point.setQuestionCount(questions.getOrDefault(key, 0L));
            series.add(point);
        }
        LivestockResourceActivityTrendVo vo = new LivestockResourceActivityTrendVo();
        vo.setDailySeries(series);
        return vo;
    }

    public static LivestockResourceUserStructureVo buildUserStructure(LivestockResourceUserStructureDto raw)
    {
        long newCount = raw != null && raw.getNewUserCount() != null ? raw.getNewUserCount() : 0L;
        long oldCount = raw != null && raw.getOldUserCount() != null ? raw.getOldUserCount() : 0L;
        LivestockResourceUserStructureVo vo = new LivestockResourceUserStructureVo();
        vo.setNewUserCount(newCount);
        vo.setOldUserCount(oldCount);
        vo.setNewUserRatio(ratioPercent(newCount, newCount + oldCount));
        vo.setOldUserRatio(ratioPercent(oldCount, newCount + oldCount));
        return vo;
    }

    public static LivestockResourceCategoryShareVo buildCategoryShare(
            List<LivestockResourceCategoryCountDto> rows, long totalAskerMessages)
    {
        Map<String, Long> countByCode = new HashMap<>();
        if (rows != null)
        {
            for (LivestockResourceCategoryCountDto row : rows)
            {
                if (row != null && row.getCategoryCode() != null)
                {
                    countByCode.put(normalizeCategoryCode(row.getCategoryCode()),
                            safeLong(row.getCount()));
                }
            }
        }
        List<LivestockResourceCategoryItemVo> items = buildFixedCategoryItems(countByCode, totalAskerMessages);
        LivestockResourceCategoryShareVo vo = new LivestockResourceCategoryShareVo();
        vo.setTotalAskerMessages(totalAskerMessages);
        vo.setItems(items);
        return vo;
    }

    public static List<LivestockResourceCategoryItemVo> buildModelCallAnalysis(
            List<LivestockResourceCategoryItemVo> items)
    {
        if (items == null)
        {
            return Collections.emptyList();
        }
        List<LivestockResourceCategoryItemVo> copy = new ArrayList<>();
        for (LivestockResourceCategoryItemVo item : items)
        {
            LivestockResourceCategoryItemVo row = new LivestockResourceCategoryItemVo();
            row.setCategoryCode(item.getCategoryCode());
            row.setCategoryName(item.getCategoryName());
            row.setCount(item.getCount());
            copy.add(row);
        }
        return copy;
    }

    public static List<LivestockResourceCategoryItemVo> buildHotTopics(
            List<LivestockResourceCategoryCountDto> rows, int topN)
    {
        if (rows == null || rows.isEmpty())
        {
            return Collections.emptyList();
        }
        List<LivestockResourceCategoryCountDto> sorted = new ArrayList<>(rows);
        sorted.sort(Comparator.comparingLong((LivestockResourceCategoryCountDto r) -> safeLong(r.getCount())).reversed()
                .thenComparing(LivestockResourceCategoryCountDto::getLastEventTime,
                        Comparator.nullsLast(Comparator.reverseOrder()))
                .thenComparing(r -> normalizeCategoryCode(r.getCategoryCode())));
        int limit = Math.min(topN, sorted.size());
        List<LivestockResourceCategoryItemVo> result = new ArrayList<>();
        for (int i = 0; i < limit; i++)
        {
            LivestockResourceCategoryCountDto row = sorted.get(i);
            LivestockResourceCategoryItemVo item = new LivestockResourceCategoryItemVo();
            String code = normalizeCategoryCode(row.getCategoryCode());
            item.setCategoryCode(code);
            item.setCategoryName(resolveCategoryName(code));
            item.setCount(safeLong(row.getCount()));
            result.add(item);
        }
        return result;
    }

    public static LivestockResourceHourlyHeatVo buildHourlyHeat(
            List<LivestockResourceDateCountDto> sessionRows,
            List<LivestockResourceDateCountDto> questionRows,
            LocalDate statDate)
    {
        Map<Integer, Long> sessions = toHourBucketMap(sessionRows);
        Map<Integer, Long> questions = toHourBucketMap(questionRows);
        List<LivestockResourceHourlyPointVo> series = new ArrayList<>();
        for (int h = 0; h < LivestockResourceScreenRules.HOURLY_BUCKETS; h++)
        {
            LivestockResourceHourlyPointVo point = new LivestockResourceHourlyPointVo();
            point.setBucketStart(String.format("%02d:00", h));
            point.setSessionCount(sessions.getOrDefault(h, 0L));
            point.setQuestionCount(questions.getOrDefault(h, 0L));
            series.add(point);
        }
        LivestockResourceHourlyHeatVo vo = new LivestockResourceHourlyHeatVo();
        vo.setWindowEnd(statDate.format(DATE_FMT));
        vo.setHourlySeries(series);
        return vo;
    }

    public static List<LivestockResourceSessionBandVo> buildSessionReplyDistribution(
            List<LivestockResourceBandCountDto> rows, long yearSessionTotal)
    {
        Map<String, Long> byBand = new HashMap<>();
        if (rows != null)
        {
            for (LivestockResourceBandCountDto row : rows)
            {
                if (row != null && row.getBandCode() != null)
                {
                    byBand.put(row.getBandCode(), safeLong(row.getSessionCount()));
                }
            }
        }
        List<LivestockResourceSessionBandVo> list = new ArrayList<>();
        for (String[] band : BAND_ORDER)
        {
            long count = byBand.getOrDefault(band[0], 0L);
            LivestockResourceSessionBandVo vo = new LivestockResourceSessionBandVo();
            vo.setBandCode(band[0]);
            vo.setBandName(band[1]);
            vo.setSessionCount(count);
            vo.setRatio(ratioPercent(count, yearSessionTotal));
            list.add(vo);
        }
        return list;
    }

    public static LivestockResourceReplyStatusVo buildReplyStatus(long okCount, long failCount)
    {
        long total = okCount + failCount;
        LivestockResourceStatusItemVo ok = statusItem(
                LivestockResourceScreenRules.STATUS_OK, "正常应答", okCount, total);
        LivestockResourceStatusItemVo fail = statusItem(
                LivestockResourceScreenRules.STATUS_FAIL, "调用失败", failCount, total);
        LivestockResourceReplyStatusVo vo = new LivestockResourceReplyStatusVo();
        vo.setTotalAiReplies(total);
        vo.setItems(Arrays.asList(ok, fail));
        return vo;
    }

    public static List<LivestockResourceKeywordVo> extractTopKeywords(List<String> texts, int topN)
    {
        if (texts == null || texts.isEmpty())
        {
            return Collections.emptyList();
        }
        Map<String, Integer> freq = new LinkedHashMap<>();
        for (String text : texts)
        {
            if (text == null || text.trim().isEmpty())
            {
                continue;
            }
            collectTokens(text, CN_TOKEN, freq);
            collectTokens(text, EN_TOKEN, freq);
        }
        List<Map.Entry<String, Integer>> entries = new ArrayList<>(freq.entrySet());
        entries.sort(Comparator.<Map.Entry<String, Integer>>comparingInt(Map.Entry::getValue).reversed()
                .thenComparing(Map.Entry::getKey));
        List<LivestockResourceKeywordVo> result = new ArrayList<>();
        int limit = Math.min(topN, entries.size());
        for (int i = 0; i < limit; i++)
        {
            Map.Entry<String, Integer> e = entries.get(i);
            LivestockResourceKeywordVo vo = new LivestockResourceKeywordVo();
            vo.setKeyword(e.getKey());
            vo.setCount(e.getValue().longValue());
            result.add(vo);
        }
        return result;
    }

    public static String resolveCategoryName(String categoryCode)
    {
        String code = normalizeCategoryCode(categoryCode);
        for (String[] row : CATEGORY_ORDER)
        {
            if (row[0].equals(code))
            {
                return row[1];
            }
        }
        if ("1".equals(code))
        {
            return "养殖技巧";
        }
        if ("2".equals(code))
        {
            return "疾病诊疗";
        }
        if ("3".equals(code))
        {
            return "用药指导";
        }
        if ("4".equals(code))
        {
            return "设备操作";
        }
        return "未分类";
    }

    private static List<LivestockResourceCategoryItemVo> buildFixedCategoryItems(
            Map<String, Long> countByCode, long total)
    {
        List<LivestockResourceCategoryItemVo> items = new ArrayList<>();
        for (String[] row : CATEGORY_ORDER)
        {
            long count = countByCode.getOrDefault(row[0], 0L);
            LivestockResourceCategoryItemVo item = new LivestockResourceCategoryItemVo();
            item.setCategoryCode(row[0]);
            item.setCategoryName(row[1]);
            item.setCount(count);
            item.setRatio(ratioPercent(count, total));
            items.add(item);
        }
        return items;
    }

    private static String normalizeCategoryCode(String raw)
    {
        if (raw == null || raw.trim().isEmpty())
        {
            return LivestockResourceScreenRules.CATEGORY_UNCAT;
        }
        String code = raw.trim();
        if (LivestockResourceScreenRules.CATEGORY_UNCAT.equalsIgnoreCase(code))
        {
            return LivestockResourceScreenRules.CATEGORY_UNCAT;
        }
        if ("1".equals(code) || String.valueOf(AiConsultConstants.CATEGORY_BREEDING).equals(code))
        {
            return "1";
        }
        if ("2".equals(code) || String.valueOf(AiConsultConstants.CATEGORY_DISEASE).equals(code))
        {
            return "2";
        }
        if ("3".equals(code) || String.valueOf(AiConsultConstants.CATEGORY_MEDICINE).equals(code))
        {
            return "3";
        }
        if ("4".equals(code) || String.valueOf(AiConsultConstants.CATEGORY_EQUIPMENT).equals(code))
        {
            return "4";
        }
        return LivestockResourceScreenRules.CATEGORY_UNCAT;
    }

    private static void collectTokens(String text, Pattern pattern, Map<String, Integer> freq)
    {
        Matcher matcher = pattern.matcher(text);
        while (matcher.find())
        {
            String token = matcher.group().toLowerCase();
            if (token.length() < 2 || STOP_WORDS.contains(token))
            {
                continue;
            }
            freq.merge(token, 1, Integer::sum);
        }
    }

    private static Map<String, Long> toCountMap(List<LivestockResourceDateCountDto> rows)
    {
        Map<String, Long> map = new HashMap<>();
        if (rows != null)
        {
            for (LivestockResourceDateCountDto row : rows)
            {
                if (row != null && row.getBucketKey() != null)
                {
                    map.put(row.getBucketKey(), safeLong(row.getCount()));
                }
            }
        }
        return map;
    }

    private static Map<Integer, Long> toHourBucketMap(List<LivestockResourceDateCountDto> rows)
    {
        Map<Integer, Long> map = new HashMap<>();
        if (rows != null)
        {
            for (LivestockResourceDateCountDto row : rows)
            {
                if (row != null && row.getBucketKey() != null)
                {
                    try
                    {
                        int idx = Integer.parseInt(row.getBucketKey().trim());
                        if (idx >= 0 && idx < LivestockResourceScreenRules.HOURLY_BUCKETS)
                        {
                            map.put(idx, safeLong(row.getCount()));
                        }
                    }
                    catch (NumberFormatException ignored)
                    {
                        // skip invalid bucket
                    }
                }
            }
        }
        return map;
    }

    private static LivestockResourceStatusItemVo statusItem(String code, String name, long count, long total)
    {
        LivestockResourceStatusItemVo vo = new LivestockResourceStatusItemVo();
        vo.setStatusCode(code);
        vo.setStatusName(name);
        vo.setCount(count);
        vo.setRatio(ratioPercent(count, total));
        return vo;
    }

    private static BigDecimal ratioPercent(long part, long total)
    {
        if (total <= 0)
        {
            return BigDecimal.ZERO.setScale(RATIO_SCALE, RoundingMode.HALF_UP);
        }
        return BigDecimal.valueOf(part * 100.0 / total).setScale(RATIO_SCALE, RoundingMode.HALF_UP);
    }

    private static BigDecimal toAvgSeconds(Double avgMs)
    {
        if (avgMs == null)
        {
            return BigDecimal.ZERO.setScale(RATIO_SCALE, RoundingMode.HALF_UP);
        }
        return BigDecimal.valueOf(avgMs / 1000.0).setScale(RATIO_SCALE, RoundingMode.HALF_UP);
    }

    private static long safeLong(Long value)
    {
        return value != null ? value : 0L;
    }
}
