package com.zhyc.module.dairyProducts.service.impl;

import com.zhyc.module.dairyProducts.domain.NpSheepMilkAnalysis;
import com.zhyc.module.dairyProducts.mapper.NpSheepMilkAnalysisMapper;
import com.zhyc.module.dairyProducts.service.INpSheepMilkAnalysisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class NpSheepMilkAnalysisServiceImpl implements INpSheepMilkAnalysisService {

    @Autowired
    private NpSheepMilkAnalysisMapper npSheepMilkAnalysisMapper;

    // 将Rec定义为静态内部类
    private static class Rec {
        LocalDate date;
        double systemMilk;
        double correctedMilk;
        int parity;
    }

    // 将ParityStat定义为静态内部类
    private static class ParityStat {
        int parity;
        double sumCorrected;
        double sumSystem;
        LocalDate first;
        LocalDate last;
        long days;
    }

    @Override
    public NpSheepMilkAnalysis selectNpSheepMilkAnalysisBySheepId(String sheepId) {
        Map<String, Object> map = npSheepMilkAnalysisMapper.selectNpSheepMilkAnalysisBySheepId(sheepId);
        if (map == null) return null;
        NpSheepMilkAnalysis ana = new NpSheepMilkAnalysis();
        ana.setSheepId(sheepId);
        ana.setManageEarTag((String) map.get("manageEarTag"));
        return ana;
    }

    // 修改处：新增模糊查询实现
    @Override
    public List<String> searchEarNumbers(String query) {
        return npSheepMilkAnalysisMapper.searchEarNumbers(query);
    }

    @Override
    public List<NpSheepMilkAnalysis> selectNpSheepMilkAnalysisList(NpSheepMilkAnalysis analysis) {
        int screenDays = (analysis != null && analysis.getScreenDays() != null) ? analysis.getScreenDays() : 100;

        // 修改处：这里改为直接传 analysis 对象，以便 mapper 可以处理 allEarNumbers
        List<String> sheepIds = npSheepMilkAnalysisMapper.selectDistinctSheepIds(analysis);

        if (CollectionUtils.isEmpty(sheepIds)) return Collections.emptyList();

        List<NpSheepMilkAnalysis> resultList = new ArrayList<>();
        Map<String, NpSheepMilkAnalysis> mapByManageTag = new HashMap<>();

        for (String sheepId : sheepIds) {
            Map<String, Object> sf = npSheepMilkAnalysisMapper.selectSheepFileBySheepId(sheepId);
            List<Map<String, Object>> records = npSheepMilkAnalysisMapper.selectMilkRecordsBySheepId(sheepId);

            if (records == null || records.isEmpty()) {
                NpSheepMilkAnalysis emptyAna = new NpSheepMilkAnalysis();
                emptyAna.setSheepId(sheepId);
                if (sf != null) {
                    emptyAna.setManageEarTag((String) sf.get("manageEarTag"));
                    emptyAna.setVariety((String) sf.get("variety"));
                    emptyAna.setLactationDays(toInteger(sf.get("lactationDay")));
                    emptyAna.setSheepCategory((String) sf.get("sheepType"));
                    emptyAna.setBirthday(convertToDate(sf.get("birthday")));
                    emptyAna.setParity(toInteger(sf.get("currentParity")));
                    emptyAna.setMonthAge(toInteger(sf.get("monthAge")));
                    emptyAna.setCurrentWeight(toDouble(sf.get("currentWeight")));
                    emptyAna.setBreedStatus((String) sf.get("breedStatus"));
                    emptyAna.setFatherManageTags((String) sf.get("fatherManageTags"));
                    emptyAna.setMotherManageTags((String) sf.get("motherManageTags"));
                    emptyAna.setRanchName((String) sf.get("ranchName"));
                    emptyAna.setFamily((String) sf.get("family"));
                }
                resultList.add(emptyAna);
                mapByManageTag.put(emptyAna.getManageEarTag(), emptyAna);
                continue;
            }

            // 使用静态内部类Rec
            List<Rec> recs = new ArrayList<>();
            for (Map<String, Object> r : records) {
                Rec rr = new Rec();
                rr.date = toLocalDate(r.get("classDate"));
                rr.systemMilk = toDouble(r.get("systemMilk"));
                rr.correctedMilk = toDouble(r.get("correctedMilk"));
                rr.parity = (r.get("parity") == null) ? 0 : Integer.parseInt(String.valueOf(r.get("parity")));
                recs.add(rr);
            }

            Map<Integer, List<Rec>> byParity = recs.stream().collect(Collectors.groupingBy(r -> r.parity));

            // 使用静态内部类ParityStat
            List<ParityStat> parityStats = new ArrayList<>();
            for (Map.Entry<Integer, List<Rec>> e : byParity.entrySet()) {
                List<Rec> list = e.getValue();
                double sumCorr = list.stream().mapToDouble(x -> x.correctedMilk).sum();
                double sumSys = list.stream().mapToDouble(x -> x.systemMilk).sum();
                LocalDate first = list.stream().map(x -> x.date).min(LocalDate::compareTo).get();
                LocalDate last = list.stream().map(x -> x.date).max(LocalDate::compareTo).get();
                long days = ChronoUnit.DAYS.between(first, last) + 1;
                ParityStat ps = new ParityStat();
                ps.parity = e.getKey();
                ps.sumCorrected = sumCorr;
                ps.sumSystem = sumSys;
                ps.first = first;
                ps.last = last;
                ps.days = Math.max(days, 1);
                parityStats.add(ps);
            }

            parityStats.sort(Comparator.comparingDouble((ParityStat p) -> p.sumCorrected).reversed()
                    .thenComparingInt(p -> p.parity));
            ParityStat maxParityStat = parityStats.get(0);

            LocalDate windowStart = maxParityStat.first;
            LocalDate windowEndByDays = windowStart.plusDays(screenDays - 1);
            LocalDate actualWindowEnd = (maxParityStat.last.isBefore(windowEndByDays)) ? maxParityStat.last : windowEndByDays;

            double sumSystemWindow = recs.stream()
                    .filter(r -> r.parity == maxParityStat.parity && !r.date.isBefore(windowStart) && !r.date.isAfter(actualWindowEnd))
                    .mapToDouble(r -> r.systemMilk).sum();

            double sumCorrectedWindow = recs.stream()
                    .filter(r -> r.parity == maxParityStat.parity && !r.date.isBefore(windowStart) && !r.date.isAfter(actualWindowEnd))
                    .mapToDouble(r -> r.correctedMilk).sum();

            long milkingDays = maxParityStat.days;
            long denominator = Math.min(milkingDays, screenDays);
            double avgCorrectedDaily = (denominator > 0) ? (sumCorrectedWindow / (double) denominator) : 0.0;

            double sumParity1 = parityStats.stream().filter(p -> p.parity == 1).mapToDouble(p -> p.sumCorrected).sum();
            double sumParity2 = parityStats.stream().filter(p -> p.parity == 2).mapToDouble(p -> p.sumCorrected).sum();
            double sumParity3 = parityStats.stream().filter(p -> p.parity == 3).mapToDouble(p -> p.sumCorrected).sum();
            double sumParity4 = parityStats.stream().filter(p -> p.parity == 4).mapToDouble(p -> p.sumCorrected).sum();

            // 你之前的computeParityAvg方法里没实现，保留调用0
            double avgP1 = computeParityAvg(parityStats, 1, screenDays);
            double avgP2 = computeParityAvg(parityStats, 2, screenDays);
            double avgP3 = computeParityAvg(parityStats, 3, screenDays);
            double avgP4 = computeParityAvg(parityStats, 4, screenDays);

            LocalDate lastDate = recs.stream().map(r -> r.date).max(LocalDate::compareTo).get();
            double avgLast7 = computeLastNDaysAvg(recs, lastDate, 7);
            double avgLast14 = computeLastNDaysAvg(recs, lastDate, 14);
            double avgLast30 = computeLastNDaysAvg(recs, lastDate, 30);

            double weightCoefficient = 1.0;
            if (sf != null && sf.get("weighCoefficient") != null) {
                weightCoefficient = toDouble(sf.get("weighCoefficient"));
            }
            double avgLast7Corrected = avgLast7 * weightCoefficient;

            NpSheepMilkAnalysis ana = new NpSheepMilkAnalysis();
            ana.setSheepId(sheepId);
            ana.setManageEarTag(sf == null ? null : (String) sf.get("manageEarTag"));
            ana.setVariety(sf == null ? null : (String) sf.get("variety"));
            ana.setMilkingStartTime(toDate(windowStart));
            ana.setDryEndTime(toDate(maxParityStat.last));
            ana.setMilkingDays((int) milkingDays);
            ana.setScreenDays(screenDays);
            ana.setMaxParity(maxParityStat.parity);
            ana.setSumSystemMilk(sumSystemWindow);
            ana.setSumCorrectedMilk(sumCorrectedWindow);
            ana.setAvgCorrectedDaily(avgCorrectedDaily);
            ana.setSumParity1Milk(sumParity1);
            ana.setSumParity2Milk(sumParity2);
            ana.setSumParity3Milk(sumParity3);
            ana.setSumParity4Milk(sumParity4);
            ana.setAvgParity1Daily(avgP1);
            ana.setAvgParity2Daily(avgP2);
            ana.setAvgParity3Daily(avgP3);
            ana.setAvgParity4Daily(avgP4);
            ana.setLactationDays(toInteger(sf == null ? null : sf.get("lactationDay")));
            ana.setAvgLast7Milk(avgLast7);
            ana.setAvgLast7Corrected(avgLast7Corrected);
            ana.setAvgLast14Milk(avgLast14);
            ana.setAvgLast30Milk(avgLast30);

            if (sf != null) {
                ana.setSheepCategory((String) sf.get("sheepType"));
                ana.setBirthday(convertToDate(sf.get("birthday")));
                ana.setParity(toInteger(sf.get("currentParity")));
                ana.setMonthAge(toInteger(sf.get("monthAge")));
                ana.setCurrentWeight(toDouble(sf.get("currentWeight")));
                ana.setBreedStatus((String) sf.get("breedStatus"));
                ana.setFatherManageTags((String) sf.get("fatherManageTags"));
                ana.setMotherManageTags((String) sf.get("motherManageTags"));
                ana.setRanchName((String) sf.get("ranchName"));
                ana.setFamily((String) sf.get("family"));
            }

            ana.setLastUpdate(toDate(lastDate));

            resultList.add(ana);
            mapByManageTag.put(ana.getManageEarTag(), ana);
        }

        // 填充母亲相关字段
        for (NpSheepMilkAnalysis a : resultList) {
            String motherTag = a.getMotherManageTags();
            if (motherTag != null && mapByManageTag.containsKey(motherTag)) {
                NpSheepMilkAnalysis mom = mapByManageTag.get(motherTag);
                a.setMotherMilkingDays(mom.getMilkingDays());
                a.setMotherSumCorrected(mom.getSumCorrectedMilk());
                a.setMotherMaxParity(mom.getMaxParity());
                a.setMotherAvgCorrectedDaily(mom.getAvgCorrectedDaily());
            }
        }

        return resultList;
    }

    @Override
    public List<NpSheepMilkAnalysis> selectNpSheepMilkAnalysisForExport(NpSheepMilkAnalysis analysis) {
        // 直接调用Mapper的导出方法，避免复杂的业务逻辑
        return npSheepMilkAnalysisMapper.selectNpSheepMilkAnalysisForExport(analysis);
    }

    private static Date convertToDate(Object obj) {
        if (obj == null) return null;
        if (obj instanceof Date) {
            return (Date) obj;
        }
        if (obj instanceof java.sql.Date) {
            return new Date(((java.sql.Date) obj).getTime());
        }
        return null;
    }

    private static LocalDate toLocalDate(Object obj) {
        if (obj == null) return null;
        if (obj instanceof java.sql.Date) {
            return ((java.sql.Date) obj).toLocalDate();
        }
        if (obj instanceof Date) {
            return ((Date) obj).toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        }
        return null;
    }

    private static Date toDate(LocalDate ld) {
        if (ld == null) return null;
        return Date.from(ld.atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

    private static Integer toInteger(Object obj) {
        if (obj == null) return null;
        if (obj instanceof Number) return ((Number) obj).intValue();
        try {
            return Integer.parseInt(obj.toString());
        } catch (Exception e) {
            return null;
        }
    }

    private static Double toDouble(Object obj) {
        if (obj == null) return 0.0;
        if (obj instanceof Number) return ((Number) obj).doubleValue();
        try {
            return Double.parseDouble(obj.toString());
        } catch (Exception e) {
            return 0.0;
        }
    }

    private double computeParityAvg(List<ParityStat> stats, int parity, int screenDays) {
        // 这里你之前实现是直接返回0，可按需完善
        return 0.0;
    }

    private double computeLastNDaysAvg(List<Rec> recs, LocalDate lastDate, int days) {
        LocalDate startDate = lastDate.minusDays(days - 1);
        double sum = 0;
        int count = 0;
        for (Rec r : recs) {
            if (r.date != null && !r.date.isBefore(startDate) && !r.date.isAfter(lastDate)) {
                sum += r.correctedMilk;
                count++;
            }
        }
        return (count > 0) ? (sum / count) : 0.0;
    }
}