package com.jeesharp.business.modules.finance.merchantsettlement.service.impl;

import cn.hutool.core.util.StrUtil;
import com.jeesharp.business.modules.finance.merchantsettlement.entity.MerchantSettlementBill;
import com.jeesharp.business.modules.finance.merchantsettlement.enums.MerchantSettlementBillStatusEnum;
import com.jeesharp.business.modules.finance.merchantsettlement.mapper.MerchantSettlementBillMapper;
import com.jeesharp.business.modules.finance.merchantsettlement.service.MerchantSettlementBillService;
import com.jeesharp.business.modules.finance.merchantsettlement.vo.MerchantSettlementBillQueryVO;
import com.jeesharp.framework.exception.BizErrorException;
import com.jeesharp.framework.mybatis.core.service.CrudService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 商户结算单领域服务实现。
 *
 * @author jeesharp
 * @since 2026-06-29
 */
@Service
@Transactional(readOnly = true)
public class MerchantSettlementBillServiceImpl
        extends CrudService<MerchantSettlementBillMapper, MerchantSettlementBill, MerchantSettlementBillQueryVO>
        implements MerchantSettlementBillService {

    @Override
    @Transactional(readOnly = false)
    public MerchantSettlementBill createBill(MerchantSettlementBill bill) {
        if (bill == null) {
            throw new BizErrorException("结算单不能为空");
        }
        if (StrUtil.isNotBlank(bill.getId())) {
            throw new BizErrorException("新增结算单不应预置主键");
        }
        if (bill.getPaidAmount() == null) {
            bill.setPaidAmount(BigDecimal.ZERO);
        }
        if (bill.getStatus() == null) {
            bill.setStatus(MerchantSettlementBillStatusEnum.PENDING_PAYOUT.getValue());
        }
        bill.preInsert();
        if (StrUtil.isBlank(bill.getTenantId())) {
            bill.setTenantId(bill.getMerchantTenantId());
        }
        getBaseMapper().insert(bill);
        return bill;
    }

    @Override
    @Transactional(readOnly = false)
    public void updateBill(MerchantSettlementBill bill) {
        if (bill == null || StrUtil.isBlank(bill.getId())) {
            throw new BizErrorException("结算单主键不能为空");
        }
        if (get(bill.getId()) == null) {
            throw new BizErrorException("结算单不存在");
        }
        bill.preUpdate();
        getBaseMapper().update(bill);
    }

    @Override
    public MerchantSettlementBill findByMerchantAndPeriod(String merchantTenantId, LocalDateTime periodStart,
            LocalDateTime periodEnd) {
        if (StrUtil.isBlank(merchantTenantId) || periodStart == null || periodEnd == null) {
            return null;
        }
        return getBaseMapper().findByMerchantAndPeriod(merchantTenantId.trim(), periodStart, periodEnd);
    }

    @Override
    public BigDecimal remainingMerchantAmount(MerchantSettlementBill bill) {
        BigDecimal net = bill.getNetMerchantAmount() != null ? bill.getNetMerchantAmount() : BigDecimal.ZERO;
        BigDecimal paid = bill.getPaidAmount() != null ? bill.getPaidAmount() : BigDecimal.ZERO;
        BigDecimal remaining = net.subtract(paid);
        return remaining.compareTo(BigDecimal.ZERO) > 0 ? remaining : BigDecimal.ZERO;
    }

    @Override
    public void validateRecordPayout(MerchantSettlementBill bill, BigDecimal payoutAmount) {
        if (MerchantSettlementBillStatusEnum.CANCELLED.getValue().equals(bill.getStatus())
                || MerchantSettlementBillStatusEnum.MERCHANT_CONFIRMED.getValue().equals(bill.getStatus())) {
            throw new BizErrorException("当前状态不可登记打款");
        }
        if (payoutAmount == null || payoutAmount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new BizErrorException("打款金额必须大于0");
        }
        BigDecimal remaining = remainingMerchantAmount(bill);
        if (payoutAmount.compareTo(remaining) > 0) {
            throw new BizErrorException("打款金额不能超过待付余额：" + remaining);
        }
    }

    @Override
    public MerchantSettlementBill applyPayout(MerchantSettlementBill bill, BigDecimal payoutAmount) {
        BigDecimal newPaid = (bill.getPaidAmount() != null ? bill.getPaidAmount() : BigDecimal.ZERO).add(payoutAmount);
        MerchantSettlementBill update = new MerchantSettlementBill();
        update.setId(bill.getId());
        update.setPaidAmount(newPaid);
        BigDecimal net = bill.getNetMerchantAmount() != null ? bill.getNetMerchantAmount() : BigDecimal.ZERO;
        if (newPaid.compareTo(net) >= 0) {
            update.setStatus(MerchantSettlementBillStatusEnum.PAID.getValue());
        } else {
            update.setStatus(MerchantSettlementBillStatusEnum.PENDING_PAYOUT.getValue());
        }
        return update;
    }

    @Override
    public void validateMerchantConfirm(MerchantSettlementBill bill, String merchantTenantId) {
        if (!MerchantSettlementBillStatusEnum.PAID.getValue().equals(bill.getStatus())) {
            throw new BizErrorException("仅「已打款」状态可确认收款");
        }
        if (StrUtil.isBlank(merchantTenantId) || !merchantTenantId.trim().equals(bill.getMerchantTenantId())) {
            throw new BizErrorException("仅本商户可确认收款");
        }
    }

    @Override
    public MerchantSettlementBill applyMerchantConfirm(MerchantSettlementBill bill, String confirmBy) {
        MerchantSettlementBill update = new MerchantSettlementBill();
        update.setId(bill.getId());
        update.setStatus(MerchantSettlementBillStatusEnum.MERCHANT_CONFIRMED.getValue());
        update.setMerchantConfirmBy(confirmBy);
        update.setMerchantConfirmDate(LocalDateTime.now());
        return update;
    }

    @Override
    public void validateCancel(MerchantSettlementBill bill) {
        if (!MerchantSettlementBillStatusEnum.PENDING_PAYOUT.getValue().equals(bill.getStatus())) {
            throw new BizErrorException("仅「待打款」状态可作废");
        }
        BigDecimal paid = bill.getPaidAmount() != null ? bill.getPaidAmount() : BigDecimal.ZERO;
        if (paid.compareTo(BigDecimal.ZERO) > 0) {
            throw new BizErrorException("已有打款记录，不可作废");
        }
    }

    @Override
    public MerchantSettlementBill applyCancel(MerchantSettlementBill bill) {
        MerchantSettlementBill update = new MerchantSettlementBill();
        update.setId(bill.getId());
        update.setStatus(MerchantSettlementBillStatusEnum.CANCELLED.getValue());
        return update;
    }
}
