package com.starry.admin.modules.withdraw.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
import com.starry.admin.modules.withdraw.entity.EarningsLineEntity;
import com.starry.admin.modules.withdraw.enums.EarningsType;
import com.starry.admin.modules.withdraw.mapper.EarningsLineMapper;
import com.starry.admin.modules.withdraw.service.IEarningsService;
import com.starry.admin.modules.withdraw.service.IFreezePolicyService;
import com.starry.common.utils.IdUtils;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;

@Service
public class EarningsServiceImpl extends ServiceImpl<EarningsLineMapper, EarningsLineEntity>
        implements IEarningsService {

    @Resource
    private IFreezePolicyService freezePolicyService;

    @Override
    public void createFromOrder(PlayOrderInfoEntity orderInfo) {
        if (orderInfo == null || orderInfo.getAcceptBy() == null) return;
        // amount from estimatedRevenue; fallback to finalAmount if null
        BigDecimal amount = orderInfo.getEstimatedRevenue() != null ? orderInfo.getEstimatedRevenue()
                : (orderInfo.getFinalAmount() != null ? orderInfo.getFinalAmount() : BigDecimal.ZERO);
        if (amount.compareTo(BigDecimal.ZERO) <= 0) return;

        int freezeHours = freezePolicyService
                .resolveFreezeHours(orderInfo.getTenantId(), orderInfo.getAcceptBy());
        LocalDateTime endTime = orderInfo.getOrderEndTime();
        if (endTime == null) return;
        LocalDateTime unlockTime = endTime.plusHours(freezeHours);
        LocalDateTime now = LocalDateTime.now();

        EarningsLineEntity line = new EarningsLineEntity();
        line.setId(IdUtils.getUuid());
        line.setTenantId(orderInfo.getTenantId());
        line.setClerkId(orderInfo.getAcceptBy());
        line.setOrderId(orderInfo.getId());
        line.setAmount(amount);
        line.setEarningType(EarningsType.ORDER);
        line.setUnlockTime(unlockTime);
        line.setStatus(unlockTime.isAfter(now) ? "frozen" : "available");
        this.save(line);
    }

    @Override
    public BigDecimal getAvailableAmount(String clerkId, LocalDateTime now) {
        // available = sum(frozen where unlock<=now) + sum(available)
        BigDecimal sum = this.baseMapper.sumWithdrawableAmount(clerkId, now);
        return sum == null ? BigDecimal.ZERO : sum;
    }

    @Override
    public BigDecimal getPendingAmount(String clerkId, LocalDateTime now) {
        // pending = sum(frozen where unlock>now)
        BigDecimal sum = this.baseMapper.sumPendingAmount(clerkId, now);
        return sum == null ? BigDecimal.ZERO : sum;
    }

    @Override
    public LocalDateTime getNextUnlockTime(String clerkId, LocalDateTime now) {
        LambdaQueryWrapper<EarningsLineEntity> q = new LambdaQueryWrapper<>();
        q.eq(EarningsLineEntity::getClerkId, clerkId)
                .eq(EarningsLineEntity::getStatus, "frozen")
                .gt(EarningsLineEntity::getUnlockTime, now)
                .orderByAsc(EarningsLineEntity::getUnlockTime)
                .last("limit 1");
        EarningsLineEntity line = this.baseMapper.selectOne(q);
        return line == null ? null : line.getUnlockTime();
    }

    @Override
    public List<EarningsLineEntity> findWithdrawable(String clerkId, BigDecimal amount, LocalDateTime now) {
        // pick oldest unlocked first (status in available or frozen with unlock<=now)
        List<EarningsLineEntity> list = this.baseMapper.selectWithdrawableLines(clerkId, now);
        BigDecimal acc = BigDecimal.ZERO;
        List<EarningsLineEntity> picked = new ArrayList<>();
        for (EarningsLineEntity e : list) {
            picked.add(e);
            acc = acc.add(e.getAmount());
            if (acc.compareTo(amount) >= 0) break;
        }
        if (acc.compareTo(amount) < 0) return new ArrayList<>();
        return picked;
    }
}
