package com.centreproject.system.service.guestExperience.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.centreproject.common.constant.GuestConstants;
import com.centreproject.common.constant.KeyConstants;
import com.centreproject.common.constant.KpiIndexConstants;
import com.centreproject.common.enums.DataSourceSystem;
import com.centreproject.common.enums.SyncStatus;
import com.centreproject.common.utils.DateUtils;
import com.centreproject.common.utils.TableNameUtils;
import com.centreproject.system.domain.kpi.digitalGx.KpiDigitalGx;
import com.centreproject.system.domain.sync.ods.OdsYiAppMetricsEngagement;
import com.centreproject.system.domain.sync.ods.OdsYiAppMetricsScreenViews;
import com.centreproject.system.domain.vo.ScreenViewSummaryVO;
import com.centreproject.system.mapper.kpi.KpiDigitalGxMapper;
import com.centreproject.system.mapper.sync.ods.OdsYiAppMetricsEngagementMapper;
import com.centreproject.system.mapper.sync.ods.OdsYiAppMetricsScreenViewsMapper;
import com.centreproject.system.service.guestExperience.IKpiDigitalGxService;
import com.centreproject.system.service.job.factory.SyncRuleHandlerFactory;
import com.centreproject.system.service.sync.IDataSyncLogService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

/**
 * @author zhonglei.xu
 * @description 数字化Gx KPI ServiceImpl
 */
@Service
@RequiredArgsConstructor
public class KpiDigitalGxServiceImpl extends ServiceImpl<KpiDigitalGxMapper, KpiDigitalGx>
        implements IKpiDigitalGxService {
    private final Logger logger = LoggerFactory.getLogger(IKpiDigitalGxService.class);
    // 规则工厂
    private final SyncRuleHandlerFactory ruleHandlerFactory;
    // 同步日志
    private final IDataSyncLogService dataSyncService;
    // 事物模板
    private final TransactionTemplate transactionTemplate;
    private final Executor virtualThreadExecutor;

    private final OdsYiAppMetricsEngagementMapper odsYiAppMetricsEngagementMapper;
    private final OdsYiAppMetricsScreenViewsMapper odsYiAppMetricsScreenViewsMapper;

    @Autowired
    private KpiDigitalGxMapper mapper;
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;
    /**
     * 获取数字化Gx KPI
     *
     * @return KpiDigitalGx
     */
    @Override
    public KpiDigitalGx getGx() {
        String redisKey = KeyConstants.DIGITAL_GX + DateUtils.getRiDate() + GuestConstants.LATEST_60_DAYS;

        // 1. 查缓存
        KpiDigitalGx cached =  (KpiDigitalGx)redisTemplate.opsForValue().get(redisKey);
        if (null!=cached) {
            return cached;
        }

        // 2. 根据类型处理数据
        KpiDigitalGx result= mapper.getKpiDigitalGxLast60Days();

        // 3. 写缓存（仅60天数据需要缓存）
        if (result != null ) {
            this.ruleProcess(result);
            redisTemplate.opsForValue().set(redisKey, result, 1, TimeUnit.HOURS);
        }
        return result;
    }

    /**
     * 规则处理
     * @param result result
     */
    private void ruleProcess(KpiDigitalGx result) {
        // 规则处理
        Integer currentAppSessions = ruleHandlerFactory.applyDefaultValue(KeyConstants.DIGITAL_GX, KpiIndexConstants.CURRENT_APP_SESSIONS, result.getCurrentAppSessions(), Integer.class);
        ruleHandlerFactory.checkThreshold(KeyConstants.DIGITAL_GX, KpiIndexConstants.CURRENT_APP_SESSIONS, currentAppSessions);
        result.setCurrentAppSessions(currentAppSessions);
        BigDecimal avgTimeSpend = ruleHandlerFactory.applyDefaultValue(KeyConstants.DIGITAL_GX, KpiIndexConstants.AVG_TIME_SPEND, result.getAvgTimespend(), BigDecimal.class);
        ruleHandlerFactory.checkThreshold(KeyConstants.DIGITAL_GX, KpiIndexConstants.AVG_TIME_SPEND, avgTimeSpend);
        result.setAvgTimespend(avgTimeSpend);
        BigDecimal appRevenue = ruleHandlerFactory.applyDefaultValue(KeyConstants.DIGITAL_GX, KpiIndexConstants.APP_REVENUE, result.getAppRevenue(), BigDecimal.class);
        ruleHandlerFactory.checkThreshold(KeyConstants.DIGITAL_GX, KpiIndexConstants.APP_REVENUE, appRevenue);
        result.setAppRevenue(appRevenue);
        // 刷新规则缓存 避免长时间缓存，规则不更新
        ruleHandlerFactory.refreshRuleCache(KeyConstants.DIGITAL_GX);
    }

    /**
     * Digital Gx KPI 同步数据接口
     *
     * @param traceId traceId
     * @param jobId   jobId
     */
    @Override
    public void startSync(String traceId, long jobId) {
        // 使用虚拟线程执行同步任务
        virtualThreadExecutor.execute(() -> {
            MDC.put("traceId", traceId);
            try {
                // 同步出勤数据
                syncDataAll(traceId, jobId);

            } finally {
                MDC.clear();
            }
        });
    }

    /**
     * 1.查询ods的两张数据源表
     * 2.根据规则处理数据
     * 3.更新数据库
     * Digital Gx KPI 同步数据
     * @param traceId traceId
     * @param jobId jobId
     */
    private void syncDataAll(String traceId, long jobId) {
        try {
            // 日志开始
            logger.info("【Digital GX】开始同步数据");
            String sourceTable = TableNameUtils.getTableNames(KeyConstants.SPLIT, OdsYiAppMetricsScreenViews.class, OdsYiAppMetricsEngagement.class);
            String targetTable = TableNameUtils.getTableNames("", KpiDigitalGx.class);
            dataSyncService.startSync(jobId, traceId, DataSourceSystem.YA_BACKEND.getCode(), sourceTable, targetTable);

            // 1. 查询源数据
            // 1.1 查询使用量最高的screens
            LocalDate startDate = LocalDate.now().minusDays(60);
            String startDateStr = startDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
            OdsYiAppMetricsScreenViews screenViewSummaryVO = this.selectGxScreen(startDateStr);
            String screenName = screenViewSummaryVO.getScreenName()==null? "" : screenViewSummaryVO.getScreenName();
            logger.info("【Digital Gx】近60天使用量最高的screens:{},使用量:{}", screenName, screenViewSummaryVO.getPageViewsCount());
            // 1.2 查询app uptime
            LocalDateTime appUpTime = this.getMaxReportDateFormatted();
            logger.info("【Digital Gx】近60天app uptime:{},",appUpTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            // 1.3 查询其他相关数据
            KpiDigitalGx gx = this.getEngagementSummary(startDateStr);
            // todo 1.4 查询 Downtime notification 和Yas app Cast数据
            // 数据聚合
            gx.setMostUsedScreens(screenName);
            gx.setAppUptime(appUpTime);

            // 3. 执行事务操作
            Integer affectedRows = transactionTemplate.execute(status -> {
                try {
                    int count = syncToTarget(gx, traceId);
                    status.flush();
                    return count;
                } catch (Exception e) {
                    // 设置回滚标记
                    status.setRollbackOnly();
                    throw e;
                }
            });

            // 日志结束
            dataSyncService.endSync(traceId, affectedRows, SyncStatus.SUCCESS, "Digital GX data synchronization completed successfully");


        } catch (DataAccessException e) {
            String errorMsg = String.format("数据库操作失败[表:%s]，错误: %s",
                    TableNameUtils.getTableNames(KeyConstants.SPLIT, KpiDigitalGx.class),
                    e.getCause() != null ? e.getCause().getMessage() : e.getMessage());
            logger.error(errorMsg, e);
            dataSyncService.endSync(traceId, 0, SyncStatus.FAILED, errorMsg);
            throw e;
        } catch (Exception e) {
            logger.error("【系统异常】", e);
            dataSyncService.endSync(traceId, 0, SyncStatus.FAILED, "系统异常");
            throw e;
        }
    }

    public OdsYiAppMetricsScreenViews selectGxScreen(String startDateStr) {
        QueryWrapper<OdsYiAppMetricsScreenViews> wrapper = new QueryWrapper<>();
        wrapper.select("screen_name", "SUM(page_views_count) AS page_views_count")
                .apply("LEFT(report_hour, 8) >= {0}", startDateStr)
                .groupBy("screen_name")
                .orderByDesc("SUM(page_views_count)")
                .last("LIMIT 1");

        return odsYiAppMetricsScreenViewsMapper.selectOne(wrapper);
    }

    /**
     * 保存至业务表
     * @param gx
     * @param traceId
     * @return
     */
    private int syncToTarget(KpiDigitalGx gx, String traceId) {
       if(ObjectUtils.isNotEmpty(gx)) {
           gx.setSynchTime(LocalDateTime.now());
           gx.setSyncTraceId(traceId);
           mapper.batchInsertOrUpdate(gx);
       }
        return 1;
    }



    /**
     * 查询最大的report_date，并转换为"yyyy-MM-dd"格式
     * @return 格式化日期（如"2025-10-01"），若无数据返回null
     */
    public LocalDateTime  getMaxReportDateFormatted() {
        // 1. 查询最大的report_date（原始格式：如"2025100101"）
        QueryWrapper<OdsYiAppMetricsEngagement> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("report_date")
                .orderByDesc("report_date")
                .last("LIMIT 1");
        OdsYiAppMetricsEngagement result = odsYiAppMetricsEngagementMapper.selectOne(queryWrapper);
        if (result == null || result.getReportDate() == null) {
            // 无数据时返回null
            return null;
        }

        // 2. 提取日期部分并格式化（假设report_date前8位为"yyyyMMdd"）
        String rawReportDate = result.getReportDate();
        try {
            // 2. 解析为LocalDateTime（默认分钟/秒为00:00）
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHH");
            // 解析逻辑："2025100101" → 年=2025，月=10，日=01，时=01，分=00，秒=00
            return LocalDateTime.parse(rawReportDate, formatter);
        } catch (StringIndexOutOfBoundsException | DateTimeParseException e) {
            throw new RuntimeException("解析失败，report_date格式应为'yyyyMMddHH'（如2025100101）：" + rawReportDate, e);
        }
    }


    /**
     * 查询近60天的总会话数、平均参与时间、总 revenue
     * @return EngagementSummaryDTO：包含三个聚合指标
     */
    public KpiDigitalGx getEngagementSummary(String startDateStr) {
        // 1. 构建查询条件：筛选近60天数据（report_date前8位 >= 起始日期）
        QueryWrapper<OdsYiAppMetricsEngagement> queryWrapper = new QueryWrapper<>();
        queryWrapper.apply("LEFT(report_date, 8) >= {0}", startDateStr);

        // 2. 聚合查询：sum(sessions_count)、avg(avg_engagement_time_sec)、sum(total_revenue)
        queryWrapper.select(
                "SUM(sessions_count) AS totalSessions",
                "AVG(avg_engagement_time_sec) AS avgEngagementTime",
                "SUM(total_revenue) AS totalRevenue"
        );

        // 4. 执行查询，返回Map结果
        List<Map<String, Object>> resultList = odsYiAppMetricsEngagementMapper.selectMaps(queryWrapper);
        if (resultList.isEmpty() || resultList.getFirst() == null) {
            return new KpiDigitalGx();
        }
        Map<String, Object> resultMap = resultList.getFirst();

        // 5. 将Map结果转换为DTO实体
        KpiDigitalGx dto = new KpiDigitalGx();
        // 总会话数（整数）
        if(null!=resultMap.get("totalSessions")) {
            dto.setCurrentAppSessions(Integer.parseInt(resultMap.get("totalSessions").toString()));
        }
        // 平均参与时间（可能为小数）
        if(null!=resultMap.get("avgEngagementTime")) {
            dto.setAvgTimespend((BigDecimal) resultMap.get("avgEngagementTime"));
        }
        // 总revenue（BigDecimal）
        if(null!=resultMap.get("totalRevenue")) {
            dto.setAppRevenue((BigDecimal) resultMap.get("totalRevenue"));
        }
        return dto;
    }
}
