package com.jeesharp.business.modules.coupon.stats.application;

import cn.hutool.core.util.StrUtil;
import com.jeesharp.business.modules.coupon.enums.CouponTemplateStatusEnum;
import com.jeesharp.business.modules.coupon.enums.MemberCouponUseStatusEnum;
import com.jeesharp.business.modules.coupon.stats.service.CouponStatsService;
import com.jeesharp.business.modules.coupon.stats.vo.CouponStatsDailyTrendItemVO;
import com.jeesharp.business.modules.coupon.stats.vo.CouponStatsDateCountVO;
import com.jeesharp.business.modules.coupon.stats.vo.CouponStatsOverviewItemVO;
import com.jeesharp.business.modules.coupon.stats.vo.CouponStatsQueryVO;
import com.jeesharp.business.modules.coupon.stats.vo.CouponStatsResultVO;
import com.jeesharp.business.modules.coupon.stats.vo.CouponStatsTemplateRankRowVO;
import com.jeesharp.business.modules.coupon.stats.vo.CouponStatsTenantScope;
import com.jeesharp.business.modules.coupon.stats.vo.CouponStatsTopTemplateItemVO;
import com.jeesharp.framework.exception.BizErrorException;
import com.jeesharp.framework.utils.WebFrameworkUtils;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 优惠券统计应用服务：解析当前登录租户并编排聚合查询。
 *
 * @author jeesharp
 * @since 2026-07-03
 */
@Service
@AllArgsConstructor
@Transactional(readOnly = true)
public class CouponStatsApplicationService {

    private static final String GET_SOURCE_ADMIN = "ADMIN";
    private static final DateTimeFormatter DATE_LABEL = DateTimeFormatter.ofPattern("MM-dd");
    private static final DateTimeFormatter UPDATED_AT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");

    private final CouponStatsService couponStatsService;

    public CouponStatsResultVO overview(CouponStatsQueryVO query) {
        CouponStatsQueryVO q = query != null ? query : new CouponStatsQueryVO();
        CouponStatsTenantScope scope = resolveTenantScope();
        q.setTenantId(scope.getTenantId());
        q.setTenantCode(scope.getTenantCode());

        int trendDays = q.getTrendDays() != null && q.getTrendDays() > 0 ? q.getTrendDays() : 7;
        int topLimit = q.getTopLimit() != null && q.getTopLimit() > 0 ? q.getTopLimit() : 3;
        LocalDate endDate = q.getDateEnd() != null ? q.getDateEnd() : LocalDate.now();
        LocalDate startDate = q.getDateStart() != null ? q.getDateStart() : endDate.minusDays(trendDays - 1L);
        LocalDateTime trendStart = startDate.atStartOfDay();
        LocalDateTime trendEnd = endDate.atTime(LocalTime.of(23, 59, 59));

        LocalDate today = LocalDate.now();
        LocalDateTime todayStart = today.atStartOfDay();
        LocalDateTime todayEnd = today.atTime(LocalTime.of(23, 59, 59));
        LocalDateTime yesterdayStart = today.minusDays(1).atStartOfDay();
        LocalDateTime yesterdayEnd = today.minusDays(1).atTime(LocalTime.of(23, 59, 59));

        long templateTotal = couponStatsService.countCouponTemplate(scope, null);
        long templateOn = couponStatsService.countCouponTemplate(scope, CouponTemplateStatusEnum.IN_PROGRESS.getValue());
        long claimTotal = couponStatsService.countMemberCoupon(scope, null, null, null, null);
        long claimToday = couponStatsService.countMemberCoupon(scope, null, null, todayStart, todayEnd);
        long claimYesterday = couponStatsService.countMemberCoupon(scope, null, null, yesterdayStart, yesterdayEnd);
        long grantTotal = couponStatsService.countMemberCoupon(scope, null, GET_SOURCE_ADMIN, null, null);
        long grantToday = couponStatsService.countMemberCoupon(scope, null, GET_SOURCE_ADMIN, todayStart, todayEnd);
        long usedTotal = couponStatsService.countMemberCoupon(scope, MemberCouponUseStatusEnum.USED.getValue(), null, null, null);
        long unusedCount = couponStatsService.countMemberCoupon(scope, MemberCouponUseStatusEnum.UNUSED.getValue(), null, null, null);
        long expiredCount = couponStatsService.countMemberCoupon(scope, MemberCouponUseStatusEnum.EXPIRED.getValue(), null, null, null);

        CouponStatsOverviewItemVO overview = new CouponStatsOverviewItemVO();
        overview.setTemplateTotal(templateTotal);
        overview.setTemplateOn(templateOn);
        overview.setClaimTotal(claimTotal);
        overview.setClaimToday(claimToday);
        overview.setClaimTodayDeltaPct(calcDeltaPct(claimToday, claimYesterday));
        overview.setGrantTotal(grantTotal);
        overview.setGrantToday(grantToday);
        overview.setUsedTotal(usedTotal);
        overview.setUseRate(calcUseRate(usedTotal, claimTotal));
        overview.setUnusedCount(unusedCount);
        overview.setExpiredCount(expiredCount);

        CouponStatsResultVO result = new CouponStatsResultVO();
        result.setOverview(overview);
        result.setTrendDays(trendDays);
        result.setDailyTrend(buildDailyTrend(scope, startDate, endDate, trendStart, trendEnd));
        result.setTopTemplates(buildTopTemplates(scope, topLimit));
        result.setUpdatedAt(LocalDateTime.now().format(UPDATED_AT));
        return result;
    }

    private List<CouponStatsDailyTrendItemVO> buildDailyTrend(CouponStatsTenantScope scope,
                                                              LocalDate startDate,
                                                              LocalDate endDate,
                                                              LocalDateTime trendStart,
                                                              LocalDateTime trendEnd) {
        Map<LocalDate, Long> claimMap = toDateCountMap(
                couponStatsService.dailyClaimByGetDate(scope, trendStart, trendEnd));
        Map<LocalDate, Long> grantMap = toDateCountMap(
                couponStatsService.dailyGrantByGetDate(scope, GET_SOURCE_ADMIN, trendStart, trendEnd));
        Map<LocalDate, Long> useMap = toDateCountMap(
                couponStatsService.dailyUseByUseDate(scope, trendStart, trendEnd));

        List<CouponStatsDailyTrendItemVO> items = new ArrayList<>();
        for (LocalDate d = startDate; !d.isAfter(endDate); d = d.plusDays(1)) {
            CouponStatsDailyTrendItemVO item = new CouponStatsDailyTrendItemVO();
            item.setDate(d.format(DATE_LABEL));
            item.setWeekday(weekdayLabel(d.getDayOfWeek()));
            item.setClaimCount(claimMap.getOrDefault(d, 0L));
            item.setGrantCount(grantMap.getOrDefault(d, 0L));
            item.setUseCount(useMap.getOrDefault(d, 0L));
            items.add(item);
        }
        return items;
    }

    private List<CouponStatsTopTemplateItemVO> buildTopTemplates(CouponStatsTenantScope scope, int limit) {
        List<CouponStatsTopTemplateItemVO> list = new ArrayList<>();
        for (CouponStatsTemplateRankRowVO row : couponStatsService.topClaimTemplates(scope, limit)) {
            CouponStatsTopTemplateItemVO item = new CouponStatsTopTemplateItemVO();
            item.setId(row.getTemplateId());
            item.setName(StrUtil.blankToDefault(row.getTemplateName(), "未命名券"));
            long claim = row.getClaimCount() != null ? row.getClaimCount() : 0L;
            long used = row.getUsedCount() != null ? row.getUsedCount() : 0L;
            item.setClaimCount(claim);
            item.setUseRate(calcUseRate(used, claim));
            list.add(item);
        }
        return list;
    }

    private Map<LocalDate, Long> toDateCountMap(List<CouponStatsDateCountVO> rows) {
        Map<LocalDate, Long> map = new HashMap<>();
        if (rows == null) {
            return map;
        }
        for (CouponStatsDateCountVO row : rows) {
            if (row.getStatDate() != null) {
                map.put(row.getStatDate(), row.getCount() != null ? row.getCount() : 0L);
            }
        }
        return map;
    }

    private int calcDeltaPct(long today, long yesterday) {
        if (yesterday <= 0) {
            return 0;
        }
        return (int) Math.round(((double) (today - yesterday) / yesterday) * 100);
    }

    private int calcUseRate(long used, long claim) {
        if (claim <= 0) {
            return 0;
        }
        return (int) Math.round((double) used * 100 / claim);
    }

    private String weekdayLabel(DayOfWeek dayOfWeek) {
        return switch (dayOfWeek) {
            case MONDAY -> "周一";
            case TUESDAY -> "周二";
            case WEDNESDAY -> "周三";
            case THURSDAY -> "周四";
            case FRIDAY -> "周五";
            case SATURDAY -> "周六";
            case SUNDAY -> "周日";
        };
    }

    /**
     * 管理端统计：优先 {@code tenantId}，缺失时按 {@code tenantCode} 过滤（与列表 dataScope 一致）。
     */
    private CouponStatsTenantScope resolveTenantScope() {
        String tenantId = StrUtil.trimToNull(WebFrameworkUtils.getLoginUserTenantId());
        String tenantCode = StrUtil.trimToNull(WebFrameworkUtils.getLoginUserTenantCode());
        if (StrUtil.isBlank(tenantId) && StrUtil.isBlank(tenantCode)) {
            throw new BizErrorException("无法识别当前租户，请重新登录");
        }
        CouponStatsTenantScope scope = new CouponStatsTenantScope();
        scope.setTenantId(tenantId);
        scope.setTenantCode(tenantCode);
        return scope;
    }
}
