package com.bizzan.bitrade.service;

import com.bizzan.bitrade.constant.TransactionType;
import com.bizzan.bitrade.dao.AirdropRecordDao;
import com.bizzan.bitrade.dao.StakingConfigDao;
import com.bizzan.bitrade.dao.StakingOrderDao;
import com.bizzan.bitrade.dto.BatchAirdropDistributeResult;
import com.bizzan.bitrade.entity.*;
import com.bizzan.bitrade.service.Base.BaseService;
import com.bizzan.bitrade.util.MessageResult;
import com.bizzan.bitrade.vo.AirdropEligibilityVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;

/**
 * 空投池业务服务，处理空投池管理和用户领取空投逻辑
 *
 * @author auto
 * @date 2026-05-17
 */
@Service
@Slf4j
public class AirdropPoolService extends BaseService {

    /** 质押中 */
    private static final int ORDER_STATUS_STAKING = 0;

    @Autowired
    private AirdropRecordDao airdropRecordDao;

    @Autowired
    private StakingOrderDao stakingOrderDao;

    @Autowired
    private StakingConfigDao stakingConfigDao;

    @Autowired
    private StakingService stakingService;

    @Autowired
    @Lazy
    private AirdropPoolService self;

    @Autowired
    private MemberWalletService memberWalletService;

    @Autowired
    private MemberTransactionService memberTransactionService;

    @Autowired
    private MemberService memberService;

    /** 领取空投所需的最少邀请下级人数 */
    private static final int REQUIRED_INVITE_COUNT = 3;

    /**
     * 查询用户是否具备提交空投领取申请的资格（有活跃质押 + 邀请下级 ≥ 3 人 + 有待领取记录）
     *
     * @param memberId 用户ID
     * @return 操作结果，data 为 AirdropEligibilityVo
     */
    @Transactional(readOnly = true)
    public MessageResult checkEligibility(Long memberId) {
        int inviteCount = memberService.countByInviterId(memberId);
        boolean inviteTaskCompleted = inviteCount >= REQUIRED_INVITE_COUNT;
        boolean hasActiveStaking = stakingService.hasActiveStaking(memberId);
        // status=0：有待提交的空投
        boolean hasPendingAirdrop = airdropRecordDao.existsByMemberIdAndStatus(memberId, 0);
        // status=2：已提交、审核中
        boolean hasPendingAudit = airdropRecordDao.existsByMemberIdAndStatus(memberId, 2);
        BigDecimal claimableAmount = airdropRecordDao.sumPendingAmountByMemberId(memberId);
        if (claimableAmount == null) {
            claimableAmount = BigDecimal.ZERO;
        }
        String claimableCoinUnit = resolveClaimableCoinUnit(memberId);
        // 可提交条件：邀请达标 + 有活跃质押 + 有待领取记录（且当前无审核中记录）
        boolean eligible = inviteTaskCompleted && hasActiveStaking && hasPendingAirdrop && !hasPendingAudit;

        AirdropEligibilityVo vo = new AirdropEligibilityVo();
        vo.setInviteCount(inviteCount);
        vo.setRequiredInviteCount(REQUIRED_INVITE_COUNT);
        vo.setInviteTaskCompleted(inviteTaskCompleted);
        vo.setHasActiveStaking(hasActiveStaking);
        vo.setHasPendingAirdrop(hasPendingAirdrop);
        vo.setHasPendingAudit(hasPendingAudit);
        vo.setClaimableAmount(claimableAmount);
        vo.setClaimableCoinUnit(claimableCoinUnit);
        vo.setEligible(eligible);
        vo.setMessage(buildEligibilityMessage(inviteCount, inviteTaskCompleted, hasActiveStaking, hasPendingAirdrop, hasPendingAudit));

        return MessageResult.success("success", vo);
    }

    /** 待领取记录仅有一种币种时返回该币种，否则返回 null */
    private String resolveClaimableCoinUnit(Long memberId) {
        List<AirdropRecord> pending = airdropRecordDao.findByMemberIdAndStatus(memberId, 0);
        if (pending == null || pending.isEmpty()) {
            return null;
        }
        Set<String> units = new HashSet<>();
        for (AirdropRecord r : pending) {
            if (r.getCoinUnit() != null && !r.getCoinUnit().isEmpty()) {
                units.add(r.getCoinUnit());
            }
        }
        return units.size() == 1 ? units.iterator().next() : null;
    }

    private String buildEligibilityMessage(int inviteCount, boolean inviteOk,
                                           boolean hasStaking, boolean hasPending,
                                           boolean hasPendingAudit) {
        if (!inviteOk) {
            return "您当前邀请的下级人数为 " + inviteCount + " 人，需邀请至少 "
                    + REQUIRED_INVITE_COUNT + " 人才可领取空投";
        }
        if (!hasStaking) {
            return "您当前没有活跃的质押订单，无法领取空投";
        }
        if (hasPendingAudit) {
            return "您的空投申请正在审核中，请耐心等待管理员审核";
        }
        if (!hasPending) {
            return "您当前没有待领取的空投";
        }
        return "您已具备提交空投申请的资格";
    }

    /**
     * 用户提交空投领取申请：校验资格后将待领取记录置为「审核中」，等待管理员审核
     * 审核通过后再入账，拒绝则保持拒绝状态
     *
     * @param memberId 用户ID
     * @return 操作结果
     */
    @Transactional(rollbackFor = Exception.class)
    public MessageResult claim(Long memberId) {
        // 1. 校验用户是否有活跃质押
        if (!stakingService.hasActiveStaking(memberId)) {
            return MessageResult.error("您当前没有活跃的质押订单，无法领取空投");
        }

        // 2. 校验邀请下级人数是否满足要求
        int inviteCount = memberService.countByInviterId(memberId);
        if (inviteCount < REQUIRED_INVITE_COUNT) {
            return MessageResult.error("您邀请的下级人数不足，需邀请至少 " + REQUIRED_INVITE_COUNT + " 人才可领取空投");
        }

        // 3. 检查是否已有审核中的记录，避免重复提交
        if (airdropRecordDao.existsByMemberIdAndStatus(memberId, 2)) {
            return MessageResult.error("您的空投申请正在审核中，请耐心等待管理员审核");
        }

        // 4. 查询用户名下待领取的空投记录（status=0）
        List<AirdropRecord> pendingRecords = airdropRecordDao.findByMemberIdAndStatus(memberId, 0);
        if (pendingRecords == null || pendingRecords.isEmpty()) {
            return MessageResult.error("您当前没有可领取的空投");
        }

        // 5. 将所有待领取记录置为「审核中」，记录提交时间
        BigDecimal totalApplied = BigDecimal.ZERO;
        String coinUnit = null;
        Date now = new Date();
        for (AirdropRecord record : pendingRecords) {
            record.setStatus(2);
            record.setClaimTime(now);
            airdropRecordDao.save(record);
            totalApplied = totalApplied.add(record.getAmount());
            coinUnit = record.getCoinUnit();
        }

        log.info("[空投申请] 用户ID={} 申请金额={} 币种={} 共{}笔，等待审核",
                memberId, totalApplied, coinUnit, pendingRecords.size());
        return MessageResult.success("申请已提交，共 " + totalApplied.toPlainString()
                + " " + coinUnit + "，等待管理员审核");
    }

    /**
     * 管理员审核通过：入账质押钱包并将记录置为「已发放」
     *
     * @param recordId 空投记录ID
     * @return 操作结果
     */
    @Transactional(rollbackFor = Exception.class)
    public MessageResult approveAudit(Long recordId) {
        // 1. 查询并校验记录状态
        AirdropRecord record = airdropRecordDao.findOne(recordId);
        if (record == null) {
            return MessageResult.error("空投记录不存在");
        }
        if (record.getStatus() == null || record.getStatus() != 2) {
            return MessageResult.error("该记录不处于审核中状态，无法审核（当前状态=" + record.getStatus() + "）");
        }

        // 2. 入账资金账户
        memberWalletService.creditAirdropToFund(record.getMemberId(), record.getCoinUnit(), record.getAmount());

        // 3. 记录资金流水
        MemberTransaction transaction = buildMemberTransaction(
                record.getMemberId(), record.getAmount(), record.getCoinUnit(), TransactionType.AIRDROP_REWARD);
        memberTransactionService.save(transaction);

        // 4. 更新记录状态为已发放
        record.setStatus(1);
        record.setAuditTime(new Date());
        airdropRecordDao.save(record);

        log.info("[空投审核通过] 记录ID={} 用户ID={} 金额={} 币种={}",
                recordId, record.getMemberId(), record.getAmount(), record.getCoinUnit());
        MessageResult result = MessageResult.success("审核通过，已发放 " + record.getAmount().toPlainString()
                + " " + record.getCoinUnit() + " 至资金账户");
        result.setData(record);
        return result;
    }

    /**
     * 管理员审核拒绝：记录拒绝理由并将记录置为「已拒绝」
     *
     * @param recordId 空投记录ID
     * @param remark   拒绝理由（可为空）
     * @return 操作结果
     */
    @Transactional(rollbackFor = Exception.class)
    public MessageResult rejectAudit(Long recordId, String remark) {
        // 1. 查询并校验记录状态
        AirdropRecord record = airdropRecordDao.findOne(recordId);
        if (record == null) {
            return MessageResult.error("空投记录不存在");
        }
        if (record.getStatus() == null || record.getStatus() != 2) {
            return MessageResult.error("该记录不处于审核中状态，无法拒绝（当前状态=" + record.getStatus() + "）");
        }

        // 2. 更新记录状态为已拒绝，写入拒绝理由和审核时间
        record.setStatus(3);
        record.setAuditTime(new Date());
        record.setAuditRemark(remark != null ? remark.trim() : "");
        airdropRecordDao.save(record);

        log.info("[空投审核拒绝] 记录ID={} 用户ID={} 理由={}", recordId, record.getMemberId(), remark);
        return MessageResult.success("已拒绝该空投申请");
    }

    /**
     * 查询指定状态的空投记录（管理员审核列表）
     *
     * @param status   状态：2=审核中，1=已发放，3=已拒绝，传 null 则查全部
     * @param pageNo   页码（从1开始）
     * @param pageSize 每页条数
     * @return 记录分页
     */
    @Transactional(readOnly = true)
    public Page<AirdropRecord> getAuditList(Integer status, int pageNo, int pageSize) {
        PageRequest pageable = new PageRequest(pageNo - 1, pageSize);
        if (status != null) {
            return airdropRecordDao.findByStatusOrderByClaimTimeDesc(status, pageable);
        }
        return airdropRecordDao.findAll(
                new PageRequest(pageNo - 1, pageSize, Sort.Direction.DESC, "createTime"));
    }

    /**
     * 查询用户空投记录（分页）
     *
     * @param memberId 用户ID
     * @param pageNo   页码
     * @param pageSize 每页条数
     * @return 记录分页
     */
    @Transactional(readOnly = true)
    public Page<AirdropRecord> getUserAirdropRecords(Long memberId, int pageNo, int pageSize) {
        PageRequest pageable = new PageRequest(pageNo - 1, pageSize);
        return airdropRecordDao.findByMemberIdOrderByCreateTimeDesc(memberId, pageable);
    }

    /**
     * 管理员向指定用户分配空投（创建待领取记录）
     * 同一质押订单当日只能发放一次
     *
     * @param record 分配信息（memberId、coinUnit、amount、stakingOrderId 必填）
     * @return 操作结果
     */
    @Transactional(rollbackFor = Exception.class)
    public MessageResult distribute(AirdropRecord record) {
        if (record.getStakingOrderId() == null) {
            return MessageResult.error("质押订单ID不能为空");
        }
        StakingOrder order = stakingOrderDao.findOne(record.getStakingOrderId());
        if (order == null) {
            return MessageResult.error("质押订单不存在");
        }
        if (order.getStatus() == null || order.getStatus() != ORDER_STATUS_STAKING) {
            return MessageResult.error("仅质押中的订单可发放空投");
        }
        if (record.getMemberId() == null || !record.getMemberId().equals(order.getMemberId())) {
            return MessageResult.error("用户ID与质押订单不匹配");
        }
        if (record.getCoinUnit() == null || !record.getCoinUnit().equals(order.getCoinUnit())) {
            return MessageResult.error("币种与质押订单不匹配");
        }

        Date[] todayRange = getTodayRangeGmt8();
        if (airdropRecordDao.existsByStakingOrderIdAndCreateTimeBetween(
                record.getStakingOrderId(), todayRange[0], todayRange[1])) {
            return MessageResult.error("该质押订单今日已发放过空投，不可重复发放");
        }

        // 初始化为待领取状态并保存
        record.setStatus(0);
        record.setClaimTime(null);
        airdropRecordDao.save(record);
        log.info("[空投分配] 用户ID={} 质押订单ID={} 金额={} 币种={}",
                record.getMemberId(), record.getStakingOrderId(), record.getAmount(), record.getCoinUnit());
        return MessageResult.success("分配成功");
    }

    /**
     * 批量对质押中订单发放空投（每笔统一金额，部分失败不影响其他订单）
     */
    public MessageResult batchDistribute(List<Long> stakingOrderIds, BigDecimal amount) {
        if (stakingOrderIds == null || stakingOrderIds.isEmpty()) {
            return MessageResult.error("请选择至少一条质押订单");
        }
        if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
            return MessageResult.error("发放金额必须大于0");
        }
        BatchAirdropDistributeResult result = new BatchAirdropDistributeResult();
        for (Long orderId : stakingOrderIds) {
            StakingOrder order = stakingOrderDao.findOne(orderId);
            if (order == null) {
                result.setFailCount(result.getFailCount() + 1);
                result.getFailMessages().add("订单" + orderId + ": 质押订单不存在");
                continue;
            }
            AirdropRecord record = new AirdropRecord();
            record.setStakingOrderId(orderId);
            record.setMemberId(order.getMemberId());
            record.setCoinUnit(order.getCoinUnit());
            record.setAmount(amount);
            MessageResult single = self.distribute(record);
            if (single.getCode() == 0) {
                result.setSuccessCount(result.getSuccessCount() + 1);
            } else {
                result.setFailCount(result.getFailCount() + 1);
                result.getFailMessages().add("订单" + orderId + ": " + single.getMessage());
            }
        }
        String msg = String.format("批量发放完成：成功 %d 笔，失败 %d 笔",
                result.getSuccessCount(), result.getFailCount());
        MessageResult response = MessageResult.success(msg);
        response.setData(result);
        return response;
    }

    /**
     * 定时任务：对启用自动空投的质押配置，向当日未发放的质押中订单创建待领取记录
     *
     * @return 成功发放笔数
     */
    public int runAutoDistribute() {
        List<StakingOrder> orders = stakingOrderDao.findByStatus(ORDER_STATUS_STAKING);
        if (orders == null || orders.isEmpty()) {
            return 0;
        }
        Date[] todayRange = getTodayRangeGmt8();
        int success = 0;
        for (StakingOrder order : orders) {
            StakingConfig config = stakingConfigDao.findOne(order.getConfigId());
            if (!isAutoAirdropConfig(config)) {
                continue;
            }
            if (airdropRecordDao.existsByStakingOrderIdAndCreateTimeBetween(
                    order.getId(), todayRange[0], todayRange[1])) {
                continue;
            }
            BigDecimal amount = StakingService.calcAirdropAmount(order, config);
            if (amount.compareTo(BigDecimal.ZERO) <= 0) {
                log.warn("[自动空投] 订单ID={} 计算金额<=0，跳过", order.getId());
                continue;
            }
            AirdropRecord record = new AirdropRecord();
            record.setStakingOrderId(order.getId());
            record.setMemberId(order.getMemberId());
            record.setCoinUnit(order.getCoinUnit());
            record.setAmount(amount);
            MessageResult result = self.distribute(record);
            if (result.getCode() == 0) {
                success++;
            } else {
                log.warn("[自动空投] 订单ID={} 发放失败: {}", order.getId(), result.getMessage());
            }
        }
        return success;
    }

    private boolean isAutoAirdropConfig(StakingConfig config) {
        return config != null
                && config.getStatus() != null && config.getStatus() == 1
                && config.getAutoAirdropEnabled() != null && config.getAutoAirdropEnabled() == 1;
    }

    /**
     * 查询所有空投记录（admin 分页）
     *
     * @param pageNo   页码
     * @param pageSize 每页条数
     * @return 记录分页
     */
    @Transactional(readOnly = true)
    public Page<AirdropRecord> getAllAirdropRecords(int pageNo, int pageSize) {
        PageRequest pageable = new PageRequest(pageNo - 1, pageSize, Sort.Direction.DESC, "createTime");
        return airdropRecordDao.findAll(pageable);
    }

    // ========================= 私有辅助方法 =========================

    /** 返回当日 [00:00:00, 23:59:59.999] 时间范围（GMT+8），用于重复发放校验 */
    private Date[] getTodayRangeGmt8() {
        Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT+8"));
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        Date start = cal.getTime();
        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        cal.set(Calendar.MILLISECOND, 999);
        Date end = cal.getTime();
        return new Date[]{start, end};
    }

    /** 构建资金流水（member_transaction） */
    private MemberTransaction buildMemberTransaction(Long memberId, BigDecimal amount,
                                                     String coinUnit, TransactionType type) {
        MemberTransaction transaction = new MemberTransaction();
        transaction.setMemberId(memberId);
        transaction.setAmount(amount);
        transaction.setSymbol(coinUnit);
        transaction.setType(type);
        transaction.setFee(BigDecimal.ZERO);
        transaction.setDiscountFee("0");
        transaction.setRealFee("0");
        transaction.setCreateTime(new Date());
        return transaction;
    }
}
