package com.datacenter.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.datacenter.entity.stat.StatPlatformDailyPnl;
import com.datacenter.mapper.StatPlatformDailyPnlMapper;
import com.datacenter.service.StatPlatformDailyPnlService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;

@Slf4j
@Service
public class StatPlatformDailyPnlServiceImpl
        extends ServiceImpl<StatPlatformDailyPnlMapper, StatPlatformDailyPnl>
        implements StatPlatformDailyPnlService {

    // member_transaction type ordinals
    private static final int TYPE_CONTRACT_FEE      = 19;
    private static final int TYPE_FOLLOW_FEE        = 37;
    private static final int TYPE_CONTRACT_FUNDING   = 26;
    private static final int TYPE_FOLLOW_FUNDING     = 42;

    @Override
    public void computeAndSaveBoth(LocalDate date) {
        computeAndSave(date, false);
        computeAndSave(date, true);
    }

    @Override
    public StatPlatformDailyPnl computeAndSave(LocalDate date, boolean includeInternal) {
        int ii = includeInternal ? 1 : 0;
        log.info("[PlatformDailyPnl] 开始统计 date={} includeInternal={}", date, includeInternal);

        LocalDateTime dayStart = LocalDateTime.of(date, LocalTime.MIDNIGHT);
        LocalDateTime dayEnd   = dayStart.plusDays(1);
        long startMs = dayStart.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        long endMs   = dayEnd.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();

        // ── 充值 / 提现 ────────────────────────────────────────────────────────
        BigDecimal depositAmount  = baseMapper.sumDeposit(dayStart, dayEnd, ii);
        BigDecimal withdrawAmount = baseMapper.sumWithdraw(dayStart, dayEnd, ii);

        // ── 自主合约（follow=0）────────────────────────────────────────────────
        int        selfOpenCount  = baseMapper.sumOpenCount(0, startMs, endMs, ii);
        BigDecimal selfOpenAmount = baseMapper.sumOpenAmount(0, startMs, endMs, ii);
        BigDecimal selfCloseProfit = baseMapper.sumCloseProfit(0, startMs, endMs, ii);
        BigDecimal selfCloseLoss   = baseMapper.sumCloseLoss(0, startMs, endMs, ii);
        BigDecimal selfCloseDiff   = selfCloseLoss.subtract(selfCloseProfit);
        BigDecimal selfFundingFee  = baseMapper.sumFundingFee(TYPE_CONTRACT_FUNDING, dayStart, dayEnd, ii);

        // ── 跟单（follow=1）───────────────────────────────────────────────────
        int        copyOpenCount  = baseMapper.sumOpenCount(1, startMs, endMs, ii);
        BigDecimal copyOpenAmount = baseMapper.sumOpenAmount(1, startMs, endMs, ii);
        BigDecimal copyCloseProfit = baseMapper.sumCloseProfit(1, startMs, endMs, ii);
        BigDecimal copyCloseLoss   = baseMapper.sumCloseLoss(1, startMs, endMs, ii);
        BigDecimal copyCloseDiff   = copyCloseLoss.subtract(copyCloseProfit);
        BigDecimal copyFundingFee  = baseMapper.sumFundingFee(TYPE_FOLLOW_FUNDING, dayStart, dayEnd, ii);

        // ── 手续费（毛收入按比例扣总返佣）─────────────────────────────────────────
        BigDecimal grossSelfFee = baseMapper.sumGrossFee(TYPE_CONTRACT_FEE, dayStart, dayEnd, ii);
        BigDecimal grossCopyFee = baseMapper.sumGrossFee(TYPE_FOLLOW_FEE,   dayStart, dayEnd, ii);
        BigDecimal totalRebates = baseMapper.sumTotalRebates(dayStart, dayEnd, ii);

        BigDecimal totalGross = grossSelfFee.add(grossCopyFee);
        BigDecimal selfFee;
        BigDecimal copyFee;
        if (totalGross.compareTo(BigDecimal.ZERO) == 0) {
            selfFee = BigDecimal.ZERO;
            copyFee = BigDecimal.ZERO;
        } else {
            BigDecimal selfRebate = totalRebates
                    .multiply(grossSelfFee)
                    .divide(totalGross, 8, RoundingMode.HALF_UP);
            selfFee = grossSelfFee.subtract(selfRebate);
            copyFee = grossCopyFee.subtract(totalRebates.subtract(selfRebate));
        }

        log.info("[PlatformDailyPnl] date={} ii={} " +
                 "deposit={} withdraw={} " +
                 "selfOpen={}/{} selfPnl={}/{} selfFee={} selfFunding={} " +
                 "copyOpen={}/{} copyPnl={}/{} copyFee={} copyFunding={}",
                 date, ii,
                 depositAmount, withdrawAmount,
                 selfOpenCount, selfOpenAmount, selfCloseProfit, selfCloseLoss, selfFee, selfFundingFee,
                 copyOpenCount, copyOpenAmount, copyCloseProfit, copyCloseLoss, copyFee, copyFundingFee);

        StatPlatformDailyPnl entity = getByDate(date, includeInternal);
        if (entity == null) {
            entity = new StatPlatformDailyPnl();
            entity.setStatDate(date);
            entity.setIncludeInternal(ii);
        }
        entity.setDepositAmount(depositAmount);
        entity.setWithdrawAmount(withdrawAmount);
        entity.setSelfOpenCount(selfOpenCount);
        entity.setSelfOpenAmount(selfOpenAmount);
        entity.setSelfCloseProfit(selfCloseProfit);
        entity.setSelfCloseLoss(selfCloseLoss);
        entity.setSelfCloseDiff(selfCloseDiff);
        entity.setSelfFee(selfFee);
        entity.setSelfFundingFee(selfFundingFee);
        entity.setCopyOpenCount(copyOpenCount);
        entity.setCopyOpenAmount(copyOpenAmount);
        entity.setCopyCloseProfit(copyCloseProfit);
        entity.setCopyCloseLoss(copyCloseLoss);
        entity.setCopyCloseDiff(copyCloseDiff);
        entity.setCopyFee(copyFee);
        entity.setCopyFundingFee(copyFundingFee);
        entity.setUpdatedTime(LocalDateTime.now());

        saveOrUpdate(entity);
        log.info("[PlatformDailyPnl] 写入完成 id={}", entity.getId());
        return entity;
    }

    @Override
    public StatPlatformDailyPnl getByDate(LocalDate date, boolean includeInternal) {
        return getOne(new LambdaQueryWrapper<StatPlatformDailyPnl>()
                .eq(StatPlatformDailyPnl::getStatDate, date)
                .eq(StatPlatformDailyPnl::getIncludeInternal, includeInternal ? 1 : 0));
    }

    @Override
    public void backfill(int days) {
        int n = Math.min(Math.max(days, 1), 90);
        log.info("[PlatformDailyPnl] 开始回填历史数据 days={}", n);
        for (int i = 1; i <= n; i++) {
            LocalDate date = LocalDate.now().minusDays(i);
            try {
                computeAndSaveBoth(date);
            } catch (Exception e) {
                log.error("[PlatformDailyPnl] 回填失败 date={}", date, e);
            }
        }
        log.info("[PlatformDailyPnl] 历史数据回填完成 days={}", n);
    }
}
