package com.jeesharp.business.modules.member.application;

import com.jeesharp.business.modules.member.api.MemberPointsApi;
import com.jeesharp.business.modules.member.dto.MemberPointsGrantDTO;
import com.jeesharp.business.modules.member.dto.MemberPointsGrantResultDTO;
import com.jeesharp.business.modules.member.dto.MemberPointsTrialDTO;
import com.jeesharp.business.modules.member.dto.MemberPointsTrialResultDTO;
import com.jeesharp.business.modules.member.entity.MemberSignInRecord;
import com.jeesharp.business.modules.member.enums.MemberPointsSceneEnum;
import com.jeesharp.business.modules.member.service.MemberService;
import com.jeesharp.business.modules.member.service.MemberSignInRecordService;
import com.jeesharp.business.modules.member.vo.MemberSignInRecordItemVO;
import com.jeesharp.business.modules.member.vo.MemberSignInRecordQueryVO;
import com.jeesharp.business.modules.member.vo.MemberSignInResultVO;
import com.jeesharp.business.modules.member.vo.MemberSignInRuleVO;
import com.jeesharp.business.modules.member.vo.MemberSignInStatusVO;
import com.jeesharp.framework.pojo.Page;
import com.jeesharp.framework.pojo.query.PageRespVO;
import com.jeesharp.framework.utils.base.ExceptionUtil;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.YearMonth;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 会员签到应用服务：编排签到记录与积分发放（每日一次、连续天数里程碑额外分）。
 *
 * @author jeesharp
 * @since 2026-05-13
 */
@Service
@AllArgsConstructor
@Transactional(readOnly = true)
public class MemberSignInApplicationService {

    private final MemberSignInRecordService memberSignInRecordService;
    private final MemberService memberService;
    private final MemberPointsApi memberPointsApi;

    /**
     * 执行签到并发放积分
     */
    @Transactional(readOnly = false)
    public MemberSignInResultVO signIn(String memberId) {
        if (memberService.get(memberId) == null) {
            ExceptionUtil.error("会员不存在");
        }
        LocalDate today = LocalDate.now();
        if (memberSignInRecordService.existsOnDate(memberId, today)) {
            ExceptionUtil.error("今日已签到");
        }
        int streakBeforeToday = countStreakEndingOn(memberId, today.minusDays(1));
        int continuous = streakBeforeToday + 1;
        int basePoints = trialSignInPoints(memberId, 1);

        MemberSignInRecord record = new MemberSignInRecord();
        record.setMemberId(memberId);
        record.setSignDate(today);
        record.setContinuousDays(continuous);
        record.setBasePoints(basePoints);
        record.setBonusPoints(0);
        record.setTotalPoints(0);
        memberSignInRecordService.saveRecord(record);

        Map<String, Object> context = new HashMap<>();
        context.put("continuousDays", continuous);
        String bizDesc = continuous > 1 && trialSignInPoints(memberId, continuous) > basePoints
                ? String.format("每日签到（连续%d天，含额外奖励）", continuous)
                : "每日签到";
        MemberPointsGrantResultDTO grantResult = memberPointsApi.grant(MemberPointsGrantDTO.builder()
                .memberId(memberId)
                .sceneCode(MemberPointsSceneEnum.SIGN_IN.getCode())
                .bizId(record.getId())
                .bizDesc(bizDesc)
                .operateSource("MINI")
                .context(context)
                .build());
        int total = grantResult.getPoints();
        int bonus = Math.max(0, total - basePoints);
        record.setTotalPoints(total);
        record.setBonusPoints(bonus);
        memberSignInRecordService.updateRecord(record);

        MemberSignInResultVO vo = new MemberSignInResultVO();
        vo.setPoints(total);
        vo.setContinuousDays(continuous);
        vo.setTotalSignDays((int) memberSignInRecordService.countByMemberId(memberId));
        vo.setTodaySigned(true);
        MemberSignInResultVO.ExtraReward extra = new MemberSignInResultVO.ExtraReward();
        extra.setHasExtra(bonus > 0);
        extra.setExtraPoints(bonus);
        if (bonus > 0) {
            extra.setReason(String.format("连续签到%d天额外奖励", continuous));
        }
        vo.setExtraReward(extra);
        return vo;
    }

    /**
     * 当前签到状态（含本月日历打点与规则说明）
     */
    public MemberSignInStatusVO getSignInStatus(String memberId) {
        if (memberService.get(memberId) == null) {
            ExceptionUtil.error("会员不存在");
        }
        LocalDate today = LocalDate.now();
        YearMonth ym = YearMonth.from(today);
        boolean todaySigned = memberSignInRecordService.existsOnDate(memberId, today);
        int continuous = todaySigned
                ? countStreakEndingOn(memberId, today)
                : countStreakEndingOn(memberId, today.minusDays(1));

        MemberSignInStatusVO vo = new MemberSignInStatusVO();
        vo.setTodaySigned(todaySigned);
        vo.setContinuousDays(continuous);
        vo.setTotalSignDays((int) memberSignInRecordService.countByMemberId(memberId));
        vo.setMonthSignDays(memberSignInRecordService.listSignedDaysOfMonth(memberId, ym));
        vo.setSignRules(buildSignRules(memberId));
        return vo;
    }

    /**
     * 签到记录分页（仅当前登录会员）
     */
    public PageRespVO<MemberSignInRecordItemVO> pageSignInRecords(String memberId, MemberSignInRecordQueryVO query) {
        if (memberService.get(memberId) == null) {
            ExceptionUtil.error("会员不存在");
        }
        if (query == null) {
            query = new MemberSignInRecordQueryVO();
        }
        Page<MemberSignInRecord> page = new Page<>(query);
        Page<MemberSignInRecord> result = memberSignInRecordService.findPageByMember(page, memberId);
        PageRespVO<MemberSignInRecordItemVO> resp = new PageRespVO<>();
        resp.setTotal(result.getTotal());
        resp.setPageNo(result.getPageNo());
        resp.setPageSize(result.getPageSize());
        List<MemberSignInRecordItemVO> rows =
                result.getRecords().stream().map(this::toSignInRecordItem).collect(Collectors.toList());
        resp.setRecords(rows);
        return resp;
    }

    private int trialSignInPoints(String memberId, int continuousDays) {
        MemberPointsTrialResultDTO trial = memberPointsApi.trial(MemberPointsTrialDTO.builder()
                .memberId(memberId)
                .sceneCode(MemberPointsSceneEnum.SIGN_IN.getCode())
                .context(Map.of("continuousDays", continuousDays))
                .build());
        return trial.getPoints();
    }

    private MemberSignInRecordItemVO toSignInRecordItem(MemberSignInRecord e) {
        MemberSignInRecordItemVO vo = new MemberSignInRecordItemVO();
        vo.setId(e.getId());
        vo.setSignDate(e.getSignDate());
        vo.setContinuousDays(e.getContinuousDays());
        vo.setBasePoints(e.getBasePoints());
        vo.setBonusPoints(e.getBonusPoints());
        vo.setTotalPoints(e.getTotalPoints());
        vo.setCreateDate(e.getCreateDate());
        return vo;
    }

    private List<MemberSignInRuleVO> buildSignRules(String memberId) {
        return List.of(
                new MemberSignInRuleVO(1, trialSignInPoints(memberId, 1)),
                new MemberSignInRuleVO(3, trialSignInPoints(memberId, 3)),
                new MemberSignInRuleVO(5, trialSignInPoints(memberId, 5)),
                new MemberSignInRuleVO(7, trialSignInPoints(memberId, 7))
        );
    }

    /**
     * 从 end 日期起向前连续有签到记录的天数（end 当日必须已签）
     */
    private int countStreakEndingOn(String memberId, LocalDate end) {
        int streak = 0;
        LocalDate d = end;
        while (memberSignInRecordService.existsOnDate(memberId, d)) {
            streak++;
            d = d.minusDays(1);
        }
        return streak;
    }
}
