package com.fs.baidu.service.impl;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.LongStream;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fs.ad.domain.AdAccount;
import com.fs.ad.service.IAdAccountService;
import com.fs.baidu.domain.BdCreative;
import com.fs.baidu.domain.BdPlan;
import com.fs.baidu.domain.BdUnit;
import com.fs.baidu.service.IBdCreativeService;
import com.fs.baidu.service.IBdPlanService;
import com.fs.baidu.service.IBdUnitService;
import com.fs.common.core.domain.R;
import com.fs.common.utils.DateUtils;
import com.fs.common.utils.PubFun;
import com.fs.common.utils.StringUtils;
import com.fs.common.utils.date.DateUtil;
import com.fs.course.domain.FsCourseWatchLog;
import com.fs.course.service.IFsCourseWatchLogService;
import com.fs.qw.param.ConversionStatisticsParam;
import com.fs.qw.result.QwExternalContactLogTableVo;
import com.fs.qw.result.QwExternalContactLogVo;
import com.fs.qw.service.IQwExternalContactService;
import com.fs.store.domain.FsStoreOrder;
import com.fs.store.service.IFsStoreOrderService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import com.fs.baidu.mapper.BdAccountMapper;
import com.fs.baidu.domain.BdAccount;
import com.fs.baidu.service.IBdAccountService;

/**
 * 百度账号Service业务层处理
 *
 * @author fs
 * @date 2025-01-20
 */
@Service
@AllArgsConstructor
public class BdAccountServiceImpl extends ServiceImpl<BdAccountMapper, BdAccount> implements IBdAccountService {

    private final IQwExternalContactService qwExternalContactService;
    private final IFsCourseWatchLogService fsCourseWatchLogService;
    private final IFsStoreOrderService fsStoreOrderService;
    private final IBdPlanService bdPlanService;
    private final IBdUnitService bdUnitService;
    private final IBdCreativeService bdCreativeService;
    private final IAdAccountService adAccountService;

    /**
     * 查询百度账号
     *
     * @param id 百度账号主键
     * @return 百度账号
     */
    @Override
    public BdAccount selectBdAccountById(Long id)
    {
        return baseMapper.selectBdAccountById(id);
    }

    /**
     * 查询百度账号列表
     *
     * @param bdAccount 百度账号
     * @return 百度账号
     */
    @Override
    public List<BdAccount> selectBdAccountList(BdAccount bdAccount){
        List<BdAccount> list = baseMapper.selectBdAccountList(bdAccount);
        List<Long> longs = PubFun.listToNewList(list, BdAccount::getAccountId);
        List<BdPlan> planList = bdPlanService.list(new QueryWrapper<BdPlan>().in("account_id", longs));
        Map<Long, Long> planMap = PubFun.listToMapByGroupCount(planList, BdPlan::getAccountId);
        List<BdUnit> unitList = bdUnitService.list(new QueryWrapper<BdUnit>().in("account_id", longs));
        Map<Long, Long> unitMap = PubFun.listToMapByGroupCount(unitList, BdUnit::getAccountId);
        List<BdCreative> creativeList = bdCreativeService.list(new QueryWrapper<BdCreative>().in("account_id", longs));
        Map<Long, Long> creativeMap = PubFun.listToMapByGroupCount(creativeList, BdCreative::getAccountId);
        list.stream().filter(e -> e.getAccountId() != null).forEach(e -> {
            e.setPlanCount(planMap.getOrDefault(e.getAccountId(), 0L));
            e.setUnitCount(unitMap.getOrDefault(e.getAccountId(), 0L));
            e.setCreativeCount(creativeMap.getOrDefault(e.getAccountId(), 0L));
        });
        return list;
    }

    /**
     * 新增百度账号
     *
     * @param bdAccount 百度账号
     * @return 结果
     */
    @Override
    public int insertBdAccount(BdAccount bdAccount)
    {
        bdAccount.setCreateTime(DateUtils.getNowDate());
        int insert = baseMapper.insert(bdAccount);
        AdAccount adAccount = new AdAccount();
        adAccount.setName(bdAccount.getAccountName());
        adAccount.setType(0);
        adAccount.setLinkId(bdAccount.getId());
        adAccountService.save(adAccount);
        return insert;
    }

    /**
     * 修改百度账号
     *
     * @param bdAccount 百度账号
     * @return 结果
     */
    @Override
    public int updateBdAccount(BdAccount bdAccount)
    {
        bdAccount.setUpdateTime(DateUtils.getNowDate());
        AdAccount adAccount = adAccountService.selectByLinkId(bdAccount.getId());
        adAccount.setName(bdAccount.getAccountName());
        adAccountService.updateById(adAccount);
        return baseMapper.updateById(bdAccount);
    }

    /**
     * 批量删除百度账号
     *
     * @param ids 需要删除的百度账号主键
     * @return 结果
     */
    @Override
    public int deleteBdAccountByIds(Long[] ids)
    {
        adAccountService.deleteByLinkIds(0, ids);
        return baseMapper.deleteBdAccountByIds(ids);
    }

    /**
     * 删除百度账号信息
     *
     * @param id 百度账号主键
     * @return 结果
     */
    @Override
    public int deleteBdAccountById(Long id)
    {
        adAccountService.deleteByLinkId(0, id);
        return baseMapper.deleteBdAccountById(id);
    }

    @Override
    public List<BdAccount> listByApiId(Long apiId) {
        return list(new QueryWrapper<BdAccount>().eq("api_id", apiId));
    }

    @Override
    // 加账号名称，并且可以选择账户，创意，单元，计划筛选
    public R conversionStatistics(ConversionStatisticsParam param) {
        // 根据type获取开始和结束时间
        LocalDateTime[] times = typeToTime(param.getType());
        LocalDateTime startTime = times[0];
        LocalDateTime endTime = times[1];
        param.setStart(DateUtil.formatLocalDateTime(startTime));
        param.setEnd(DateUtil.formatLocalDateTime(endTime));
        // 查询数据，list总数据
        List<QwExternalContactLogVo> list = qwExternalContactService.selectAllFormAd(param);
        if(param.getType() == null) param.setType(0);
        // 注册数据
        List<QwExternalContactLogVo> regList = list.stream().filter(e -> e.getFsUserId() != null).collect(Collectors.toList());
        // 完课数据
        List<QwExternalContactLogVo> finisList = list.stream().filter(e -> e.getUploadRegisterStatus() == 1 && e.getUploadFinishedStatus() == 0).collect(Collectors.toList());
        // 成功下单数据
        if(!finisList.isEmpty()){
            List<Long> exIdList = PubFun.listToNewList(finisList, QwExternalContactLogVo::getId);
            List<FsCourseWatchLog> watchLogList = fsCourseWatchLogService.list(new QueryWrapper<FsCourseWatchLog>().in("qw_external_contact_id", exIdList).eq("log_type", 2));
            Map<Long, Long> watchList = PubFun.listToMapByGroupCount(watchLogList, FsCourseWatchLog::getQwExternalContactId);
            finisList = finisList.stream().filter(e -> watchList.containsKey(e.getId())).collect(Collectors.toList());

        }
        Map<Long, FsStoreOrder> orderMap;
        if(!list.isEmpty()){
            List<Long> fsUserIdList = PubFun.listToNewList(list, QwExternalContactLogVo::getFsUserId);
            List<FsStoreOrder> fsOrderList = fsStoreOrderService.selectFsStoreOrderStatisticsByUserId(fsUserIdList);
            orderMap = PubFun.listToMapByGroupObject(fsOrderList, FsStoreOrder::getUserId);
        }else{
            orderMap = new HashMap<>();
        }
        // 是否完课
        List<QwExternalContactLogVo> orderList = list.stream().filter(e -> orderMap.containsKey(e.getFsUserId())).collect(Collectors.toList());
        R result = R.ok();
        switch (param.getType()) {
            case 0: // 默认12个月
                result = dateToR(startTime, endTime, "YYYY-MM", (i, time) -> time.plusMonths(i), ChronoUnit.MONTHS, Arrays.asList(list, regList, finisList, orderList));
                break;
            case 1: // 今天
            case 2: // 昨天
                result = dateToR(startTime, endTime, "YYYY-MM-dd HH", (i, time) -> time.plusHours(i), ChronoUnit.HOURS, Arrays.asList(list, regList, finisList, orderList));
                break;
            case 3: // 本周
            case 4: // 上周
                result = dateToR(startTime, endTime, "YYYY-MM-dd", (i, time) -> time.plusDays(i), ChronoUnit.DAYS, Arrays.asList(list, regList, finisList, orderList));
                break;
            case 5: // 本月
            case 6: // 上月
                result = dateToR(startTime, endTime, "YYYY-MM-dd", (i, time) -> time.plusDays(i), ChronoUnit.DAYS, Arrays.asList(list, regList, finisList, orderList));
                break;
            case 7: // 本季度
            case 8: // 上季度
                result = dateToR(startTime, endTime, "YYYY-MM", (i, time) -> time.plusMonths(i), ChronoUnit.MONTHS, Arrays.asList(list, regList, finisList, orderList));
                break;
            case 9: // 本年
            case 10: // 去年
                result = dateToR(startTime, endTime, "YYYY-MM", (i, time) -> time.plusMonths(i), ChronoUnit.MONTHS, Arrays.asList(list, regList, finisList, orderList));
                break;
        }
        if(param.getTableType() == null) param.setTableType(0);
        Function<QwExternalContactLogVo, String> key = null;
        Function<QwExternalContactLogVo, String> nameFun = null;
        Function<QwExternalContactLogVo, String> directionFun = null;
        if(param.getTableType() == 1){
            key = QwExternalContactLogVo::getAccountId;
            nameFun = QwExternalContactLogVo::getAccountName;
        }
        if(param.getTableType() == 2){
            key = QwExternalContactLogVo::getPid;
            nameFun = QwExternalContactLogVo::getCampaignFeedName;
        }
        if(param.getTableType() == 3){
            key = QwExternalContactLogVo::getUid;
            nameFun = QwExternalContactLogVo::getAdgroupFeedName;
        }
        if(param.getTableType() == 4){
            key = QwExternalContactLogVo::getAid;
            nameFun = QwExternalContactLogVo::getCreativeFeedName;
        }
        if (param.getStaticType() == 1) {
            directionFun = e -> StringUtils.isEmpty(e.getAddress()) ? "未知" : e.getAddress();
        }
        if (param.getStaticType() == 2) {
            directionFun = e -> StringUtils.isEmpty(e.getSex()) ? "未知" : e.getSex();
        }
        if (param.getStaticType() == 3) {
            directionFun = e -> StringUtils.isEmpty(e.getAge()) ? "未知" : e.getAge();
        }
        // 列表数据组装
        List<QwExternalContactLogTableVo> tableList = getTableList(list, regList, finisList, orderList, key, nameFun, directionFun);
        BigDecimal ten = BigDecimal.valueOf(100);
        tableList.forEach(e -> {
            BigDecimal total = BigDecimal.valueOf(e.getTotal());
            BigDecimal reg = BigDecimal.valueOf(e.getReg());
            BigDecimal finis = BigDecimal.valueOf(e.getFinis());
            BigDecimal order = BigDecimal.valueOf(e.getOrder());
            e.setRegPri(reg.doubleValue() == 0 ? BigDecimal.ZERO : reg.divide(total, 4, RoundingMode.HALF_UP).multiply(ten));
            e.setFinisPri(finis.doubleValue() == 0 ? BigDecimal.ZERO : finis.divide(reg, 4, RoundingMode.HALF_UP).multiply(ten));
            e.setOrderPri(order.doubleValue() == 0 ? BigDecimal.ZERO : order.divide(reg, 4, RoundingMode.HALF_UP).multiply(ten));
        });
        return result.put("tableList", tableList);
    }

    /**
     * 列表数据处理
     * @param list         所有统计数据
     * @param regList      注册数据
     * @param finisList    完课数据
     * @param orderList    下单数据
     * @param key          分组key
     * @param nameFun      名称方法
     * @param directionFun 统计方向方法（地区，性别，年龄）
     * @return 组装完成数据
     */
    private List<QwExternalContactLogTableVo> getTableList(List<QwExternalContactLogVo> list, List<QwExternalContactLogVo> regList, List<QwExternalContactLogVo> finisList, List<QwExternalContactLogVo> orderList, Function<QwExternalContactLogVo, String> key, Function<QwExternalContactLogVo, String> nameFun, Function<QwExternalContactLogVo, String> directionFun){
        if(key == null || nameFun == null || directionFun == null){
            return new ArrayList<>();
        }
        Map<String, Map<String, Optional<QwExternalContactLogVo>>> map = list.stream().collect(Collectors.groupingBy(directionFun, Collectors.groupingBy(key, Collectors.reducing((e1, e2) -> e1))));
        Function<List<QwExternalContactLogVo>, Map<String, Map<String, Long>>> group = data -> data.stream().collect(Collectors.groupingBy(directionFun, Collectors.groupingBy(key, Collectors.counting())));
        Map<String, Map<String, Long>> totalMap = group.apply(list);
        Map<String, Map<String, Long>> regMap = group.apply(regList);
        Map<String, Map<String, Long>> finisMap = group.apply(finisList);
        Map<String, Map<String, Long>> orderMap = group.apply(orderList);
        return map.entrySet().stream().flatMap(mapData -> mapData.getValue().entrySet().stream().map(e -> {
            String key1 = e.getKey();
            Optional<QwExternalContactLogVo> value = e.getValue();
            long total = totalMap.getOrDefault(mapData.getKey(), new HashMap<>()).getOrDefault(key1, 0L);
            long reg = regMap.getOrDefault(mapData.getKey(), new HashMap<>()).getOrDefault(key1, 0L);
            long finis = finisMap.getOrDefault(mapData.getKey(), new HashMap<>()).getOrDefault(key1, 0L);
            long order = orderMap.getOrDefault(mapData.getKey(), new HashMap<>()).getOrDefault(key1, 0L);
            QwExternalContactLogVo vo = value.orElse(new QwExternalContactLogVo());
            String name = nameFun.apply(vo);
            return QwExternalContactLogTableVo.builder().direction(mapData.getKey()).name(name).nickName(vo.getNickName()).total(total).order(order).reg(reg).finis(finis).build();
        })).collect(Collectors.toList());
    }

    /**
     * 根据时间分组，统计返回数据
     * @param start       开始时间
     * @param end         结束时间
     * @param pattern     时间格式化字符串
     * @param nextTimeFun 计算每次时间间隔方法
     * @param unit        时间计算单位
     * @param dataList    需要分组统计的数据
     */
    private static R dateToR(LocalDateTime start, LocalDateTime end, String pattern, BiFunction<Long, LocalDateTime, LocalDateTime> nextTimeFun, ChronoUnit unit, List<List<QwExternalContactLogVo>> dataList) {
        Function<LocalDateTime, String> timePattern = time -> DateUtil.formatLocalDateTime(time, pattern);
        Function<QwExternalContactLogVo, String> key = e -> timePattern.apply(e.getTime());
        List<String> dateList = LongStream.range(0, start.until(end, unit) + 1).mapToObj(e -> nextTimeFun.apply(e, start)).map(timePattern).collect(Collectors.toList());
        return R.ok().put("dataList", dateList).put("data", dataList.stream().map(list -> {
            Map<String, Long> map = PubFun.listToMapByGroupCount(list, key);
            return dateList.stream().map(e -> map.getOrDefault(e, 0L)).collect(Collectors.toList());
        }).collect(Collectors.toList()));
    }

    /**
     * 根据类型计算开始和结束时间
     * @param type 类型
     */
    private static LocalDateTime[] typeToTime(Integer type) {
        LocalTime beginTime = LocalTime.of(0, 0, 0);
        LocalTime lastTime = LocalTime.of(23, 59, 59);
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime startTime = LocalDateTime.of(now.minusYears(1).toLocalDate(), beginTime);
        LocalDateTime endTime = LocalDateTime.of(now.toLocalDate(), lastTime);
        if(type != null && type != 0) {{
            LocalDate date;
            switch (type) {
                case 1: // 今天
                    date = now.toLocalDate();
                    startTime = LocalDateTime.of(date, beginTime);
                    endTime = LocalDateTime.of(date, lastTime);
                    break;
                case 2: // 昨天
                    date = now.minusDays(1).toLocalDate();
                    startTime = LocalDateTime.of(date, beginTime);
                    endTime = LocalDateTime.of(date, lastTime);
                    break;
                case 3: // 本周
                    startTime = LocalDateTime.of(now.with(DayOfWeek.MONDAY).toLocalDate(), beginTime);
                    endTime = LocalDateTime.of(now.with(DayOfWeek.SUNDAY).toLocalDate(), lastTime);
                    break;
                case 4: // 上周
                    LocalDateTime time = now.with(DayOfWeek.MONDAY).minusDays(1);
                    startTime = LocalDateTime.of(time.with(DayOfWeek.MONDAY).toLocalDate(), beginTime);
                    endTime = LocalDateTime.of(time.with(DayOfWeek.SUNDAY).toLocalDate(), lastTime);
                    break;
                case 5: // 本月
                    startTime = LocalDateTime.of(now.toLocalDate().with(TemporalAdjusters.firstDayOfMonth()), beginTime);
                    endTime = LocalDateTime.of(now.toLocalDate().with(TemporalAdjusters.lastDayOfMonth()), lastTime);
                    break;
                case 6: // 上月
                    LocalDate localDate = now.toLocalDate().minusMonths(1);
                    startTime = LocalDateTime.of(localDate.with(TemporalAdjusters.firstDayOfMonth()), beginTime);
                    endTime = LocalDateTime.of(localDate.with(TemporalAdjusters.lastDayOfMonth()), lastTime);
                    break;
                case 7: // 本季度

                    startTime = LocalDateTime.of(getQuarterStart(now), beginTime);
                    endTime = LocalDateTime.of(getQuarterEnd(now), lastTime);
                    break;
                case 8: // 上季度
                    startTime = LocalDateTime.of(getQuarterStart(now.minusMonths(3)), beginTime);
                    endTime = LocalDateTime.of(getQuarterEnd(now.minusMonths(3)), lastTime);
                    break;
                case 9: // 本年
                    startTime = LocalDateTime.of(now.toLocalDate().with(TemporalAdjusters.firstDayOfYear()), beginTime);
                    endTime = LocalDateTime.of(now.toLocalDate().with(TemporalAdjusters.lastDayOfYear()), lastTime);
                    break;
                case 10: // 去年
                    startTime = LocalDateTime.of(now.toLocalDate().minusYears(1).with(TemporalAdjusters.firstDayOfYear()), beginTime);
                    endTime = LocalDateTime.of(now.toLocalDate().minusYears(1).with(TemporalAdjusters.lastDayOfYear()), lastTime);
                    break;
            }
        }}
        return new LocalDateTime[]{startTime, endTime};
    }

    // 获取指定时间所在季度的开始时间
    private static LocalDate getQuarterStart(LocalDateTime dateTime) {
        int month = dateTime.getMonthValue();
        int startMonth = ((month - 1) / 3) * 3 + 1; // 计算季度开始月份
        return LocalDate.of(dateTime.getYear(), startMonth, 1);
    }

    // 获取指定时间所在季度的结束时间
    private static LocalDate getQuarterEnd(LocalDateTime dateTime) {
        int month = dateTime.getMonthValue();
        int endMonth = ((month - 1) / 3) * 3 + 3; // 计算季度结束月份
        LocalDate endOfMonth = LocalDate.of(dateTime.getYear(), endMonth, 1);
        return endOfMonth.with(TemporalAdjusters.lastDayOfMonth());
    }



}
