package com.yolo.keyboard.service.tenantbalance;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.yolo.keyboard.dal.dataobject.tenantbalancetransaction.TenantBalanceTransactionDO;
import com.yolo.keyboard.dal.dataobject.tenantwithdraworder.KeyboardTenantWithdrawOrderDO;
import com.yolo.keyboard.dal.mysql.tenantbalancetransaction.TenantBalanceTransactionMapper;
import com.yolo.keyboard.dal.mysql.tenantwithdraworder.KeyboardTenantWithdrawOrderMapper;
import com.yolo.keyboard.framework.common.util.collection.CollectionUtils;
import com.yolo.keyboard.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.yolo.keyboard.framework.tenant.core.context.TenantContextHolder;
import com.yolo.keyboard.module.infra.api.config.ConfigApi;
import com.yolo.keyboard.module.system.api.notify.NotifyMessageSendApi;
import com.yolo.keyboard.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import com.yolo.keyboard.utils.BizNoGenerator;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import com.yolo.keyboard.controller.admin.tenantbalance.vo.*;
import com.yolo.keyboard.dal.dataobject.tenantbalance.TenantBalanceDO;
import com.yolo.keyboard.framework.common.pojo.PageResult;
import com.yolo.keyboard.framework.common.pojo.PageParam;
import com.yolo.keyboard.framework.common.util.object.BeanUtils;

import com.yolo.keyboard.dal.mysql.tenantbalance.TenantBalanceMapper;
import com.yolo.keyboard.module.system.dal.dataobject.tenant.TenantDO;
import com.yolo.keyboard.module.system.dal.mysql.tenant.TenantMapper;

import static com.yolo.keyboard.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yolo.keyboard.framework.common.util.collection.CollectionUtils.convertList;
import static com.yolo.keyboard.framework.common.util.collection.CollectionUtils.diffList;
import static com.yolo.keyboard.module.infra.enums.ErrorCodeConstants.*;

/**
 * 租户余额 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
public class TenantBalanceServiceImpl implements TenantBalanceService {

    @Resource
    private TenantBalanceMapper tenantBalanceMapper;

    @Resource
    private TenantBalanceTransactionMapper tenantBalanceTransactionMapper;

    @Resource
    private TenantMapper tenantMapper;

    @Resource
    private ConfigApi configApi;

    @Resource
    private KeyboardTenantWithdrawOrderMapper tenantWithdrawOrderMapper;

    @Resource
    private NotifyMessageSendApi notifyMessageSendApi;

    private static final String WITHDRAW_DAYS_CONFIG_KEY = "WITHDRAW-DAYS";
    private static final String WITHDRAW_NOTIFY_TEMPLATE_CODE = "tx-001";

    @Override
    public Long createTenantBalance(TenantBalanceSaveReqVO createReqVO) {
        // 插入
        TenantBalanceDO tenantBalance = BeanUtils.toBean(createReqVO, TenantBalanceDO.class);
        tenantBalanceMapper.insert(tenantBalance);

        // 返回
        return tenantBalance.getId();
    }

    @Override
    public void updateTenantBalance(TenantBalanceSaveReqVO updateReqVO) {
        // 校验存在
        validateTenantBalanceExists(updateReqVO.getId());
        // 更新
        TenantBalanceDO updateObj = BeanUtils.toBean(updateReqVO, TenantBalanceDO.class);
        tenantBalanceMapper.updateById(updateObj);
    }

    @Override
    public void deleteTenantBalance(Long id) {
        // 校验存在
        validateTenantBalanceExists(id);
        // 删除
        tenantBalanceMapper.deleteById(id);
    }

    @Override
        public void deleteTenantBalanceListByIds(List<Long> ids) {
        // 删除
        tenantBalanceMapper.deleteByIds(ids);
        }


    private void validateTenantBalanceExists(Long id) {
        if (tenantBalanceMapper.selectById(id) == null) {
            throw exception(TENANT_BALANCE_NOT_EXISTS);
        }
    }

    @Override
    public TenantBalanceDO getTenantBalance(Long id) {
        return tenantBalanceMapper.selectById(id);
    }

    @Override
    public PageResult<TenantBalanceDO> getTenantBalancePage(TenantBalancePageReqVO pageReqVO) {
        return tenantBalanceMapper.selectPage(pageReqVO);
    }

    @Override
    @Transactional
    public void addTenantBalance(TenantBalanceAddReqVO addReqVO) {
        // 1. 根据ID查询租户余额记录，校验是否存在
        TenantBalanceDO balance = tenantBalanceMapper.selectById(addReqVO.getId());
        if (balance == null) {
            throw exception(TENANT_BALANCE_NOT_EXISTS);
        }

        // 2. 计算新的余额（当前余额 + 充值金额）
        BigDecimal newBalance = balance.getBalance().add(new BigDecimal(String.valueOf(addReqVO.getAmount())));
        // 3. 更新租户余额（使用乐观锁）
        balance.setBalance(newBalance);
        int updateCount = tenantBalanceMapper.updateById(balance);
        if (updateCount == 0) {
            throw exception(TENANT_BALANCE_NOT_EXISTS);
        }

        // 4. 创建余额交易记录
        TenantBalanceTransactionDO transaction = TenantBalanceTransactionDO.builder()
                .bizNo(BizNoGenerator.generate("RECHARGE")) // 生成充值业务编号
                .points(new BigDecimal(String.valueOf(addReqVO.getAmount()))) // 充值金额
                .balance(newBalance) // 充值后余额
                .frozenAmt(balance.getFrozenAmt() != null ? balance.getFrozenAmt() : BigDecimal.ZERO) // 当前冻结金额
                .withdrawableBalance(balance.getWithdrawableBalance() != null ? balance.getWithdrawableBalance() : BigDecimal.ZERO) // 当前可提现金额
                .tenantId(addReqVO.getId())
                .type("RECHARGE") // 交易类型：充值
                .description("余额充值") // 交易描述
                .remark(addReqVO.getRemark()) // 备注信息
                .operatorId(TenantContextHolder.getTenantId()) // 操作人ID（当前租户ID）
                .build();
        // 5. 插入交易记录到数据库
        tenantBalanceTransactionMapper.insert(transaction);
    }

    @Override
    public TenantBalanceDO getSelfBalance() {
        Long tenantId = TenantContextHolder.getRequiredTenantId();
        return tenantBalanceMapper.selectById(tenantId);
    }

    @Override
    public PageResult<TenantBalanceRespVO> getSelfSubordinateTenantBalancePage(TenantBalancePageReqVO pageReqVO) {
        // 1. 获取当前租户ID
        Long currentTenantId = TenantContextHolder.getRequiredTenantId();
        
        // 2. 查询当前租户的下级租户列表
        List<TenantDO> subordinateTenants = tenantMapper.selectList(TenantDO::getParentId, currentTenantId);
        
        // 3. 提取下级租户的ID列表
        List<Long> tenantIds = CollectionUtils.convertList(subordinateTenants, TenantDO::getId);
        
        // 4. 如果没有下级租户，则直接返回空的分页结果
        if (CollUtil.isEmpty(tenantIds)) {
            return PageResult.empty();
        }
        
        // 5. 根据下级租户ID列表查询对应的余额信息，并进行分页
        PageResult<TenantBalanceDO> pageResult = tenantBalanceMapper.selectPage(pageReqVO,
            new LambdaQueryWrapperX<TenantBalanceDO>().in(TenantBalanceDO::getId, tenantIds));
            
        // 6. 将查询结果转换为响应VO并返回
        return BeanUtils.toBean(pageResult, TenantBalanceRespVO.class);
    }

    @Override
    public PageResult<TenantBalanceTransactionDO> getTenantBalanceTransactionPage(PageParam pageReqVO, Long tenantId) {
        return tenantBalanceTransactionMapper.selectPage(pageReqVO, tenantId);
    }

    @Override
    public Long createTenantBalanceTransaction(TenantBalanceTransactionDO tenantBalanceTransaction) {
        tenantBalanceTransactionMapper.insert(tenantBalanceTransaction);
        return tenantBalanceTransaction.getId();
    }

    @Override
    public void updateTenantBalanceTransaction(TenantBalanceTransactionDO tenantBalanceTransaction) {
        // 校验存在
        validateTenantBalanceTransactionExists(tenantBalanceTransaction.getId());
        // 更新
        tenantBalanceTransactionMapper.updateById(tenantBalanceTransaction);
    }

    @Override
    public void deleteTenantBalanceTransaction(Long id) {
        // 删除
        tenantBalanceTransactionMapper.deleteById(id);
    }

    @Override
    public void deleteTenantBalanceTransactionListByIds(List<Long> ids) {
        // 删除
        tenantBalanceTransactionMapper.deleteByIds(ids);
    }

    @Override
    public TenantBalanceTransactionDO getTenantBalanceTransaction(Long id) {
        return tenantBalanceTransactionMapper.selectById(id);
    }

    private void validateTenantBalanceTransactionExists(Long id) {
        if (tenantBalanceTransactionMapper.selectById(id) == null) {
            throw exception(TENANT_BALANCE_TRANSACTION_NOT_EXISTS);
        }
    }

    private void deleteTenantBalanceTransactionByTenantId(Long tenantId) {
        tenantBalanceTransactionMapper.deleteByTenantId(tenantId);
    }

    private void deleteTenantBalanceTransactionByTenantIds(List<Long> tenantIds) {
        tenantBalanceTransactionMapper.deleteByTenantIds(tenantIds);
    }

    @Override
    @Transactional
    public void withdraw(TenantBalanceWithdrawReqVO withdrawReqVO) {
        // 1. 校验是否在提现日期范围内
        validateWithdrawDate();

        // 2. 获取当前租户的余额记录
        Long tenantId = TenantContextHolder.getRequiredTenantId();
        TenantBalanceDO balance = tenantBalanceMapper.selectById(tenantId);
        if (balance == null) {
            throw exception(TENANT_BALANCE_NOT_EXISTS);
        }

        // 3. 校验可提现金额是否充足
        BigDecimal withdrawAmount = withdrawReqVO.getAmount();
        BigDecimal withdrawableBalance = balance.getWithdrawableBalance() != null ? balance.getWithdrawableBalance() : BigDecimal.ZERO;
        if (withdrawableBalance.compareTo(withdrawAmount) < 0) {
            throw exception(TENANT_BALANCE_WITHDRAW_INSUFFICIENT);
        }

        // 4. 从可提现金额中扣减并增加冻结金额
        BigDecimal frozenAmt = balance.getFrozenAmt() != null ? balance.getFrozenAmt() : BigDecimal.ZERO;
        BigDecimal newWithdrawableBalance = withdrawableBalance.subtract(withdrawAmount);
        BigDecimal newFrozenAmt = frozenAmt.add(withdrawAmount);
        balance.setWithdrawableBalance(newWithdrawableBalance);
        balance.setFrozenAmt(newFrozenAmt);
        int updateCount = tenantBalanceMapper.updateById(balance);
        if (updateCount == 0) {
            throw exception(TENANT_BALANCE_NOT_EXISTS);
        }

        // 5. 生成提现单号
        String withdrawNo = BizNoGenerator.generate("WD");
        String bizNo = BizNoGenerator.generate("BIZ");

        // 6. 创建冻结交易记录
        TenantBalanceTransactionDO transaction = TenantBalanceTransactionDO.builder()
                .bizNo(bizNo)
                .points(withdrawAmount.negate()) // 冻结金额（负数表示冻结扣减）
                .balance(balance.getBalance()) // 当前总余额
                .frozenAmt(newFrozenAmt) // 冻结后的冻结金额
                .withdrawableBalance(newWithdrawableBalance) // 扣减后的可提现余额
                .tenantId(tenantId)
                .type("FREEZE")
                .description("提现冻结")
                .remark(withdrawReqVO.getRemark())
                .operatorId(tenantId)
                .build();
        tenantBalanceTransactionMapper.insert(transaction);

        // 7. 创建提现申请订单
        LocalDateTime now = LocalDateTime.now();
        KeyboardTenantWithdrawOrderDO withdrawOrder = KeyboardTenantWithdrawOrderDO.builder()
                .withdrawNo(withdrawNo)
                .bizNo(bizNo)
                .tenantId(tenantId)
                .currency("CNY")
                .amount(withdrawAmount) // 直接存储 BigDecimal
                .feeAmount(BigDecimal.ZERO) // 手续费暂时为0
                .actualAmount(withdrawAmount) // 实际到账金额
                // 收款信息
                .payChannel(withdrawReqVO.getPayChannel())
                .payeeType(withdrawReqVO.getPayeeType())
                .payeeName(withdrawReqVO.getPayeeName())
                .payeeAccount(withdrawReqVO.getPayeeAccount())
                .payeeBankName(withdrawReqVO.getPayeeBankName())
                .payeeBankCode(withdrawReqVO.getPayeeBankCode())
                .payeeBankBranch(withdrawReqVO.getPayeeBankBranch())
                // 状态
                .status("APPLIED") // 申请中
                .auditStatus("PENDING") // 待审核
                .applyTime(now)
                .balanceTxnId(transaction.getId()) // 关联冻结交易记录
                .creatorId(tenantId)
                .createdAt(now)
                .updatedAt(now)
                .build();
        tenantWithdrawOrderMapper.insert(withdrawOrder);

        // 8. 发送站内信通知
        sendWithdrawNotify(tenantId, withdrawAmount);
    }

    /**
     * 发送提现站内信通知
     *
     * @param tenantId       租户ID
     * @param withdrawAmount 提现金额
     */
    private void sendWithdrawNotify(Long tenantId, BigDecimal withdrawAmount) {
        // 获取租户信息，找到联系人用户ID
        TenantDO tenant = tenantMapper.selectById(tenantId);
        if (tenant == null || tenant.getContactUserId() == null) {
            return;
        }

        // 构建站内信请求
        NotifySendSingleToUserReqDTO reqDTO = new NotifySendSingleToUserReqDTO();
        reqDTO.setUserId(tenant.getContactUserId());
        reqDTO.setTemplateCode(WITHDRAW_NOTIFY_TEMPLATE_CODE);
        reqDTO.setTemplateParams(Map.of("amount", withdrawAmount.toPlainString()));

        // 发送站内信
        notifyMessageSendApi.sendSingleMessageToAdmin(reqDTO);
    }

    /**
     * 校验是否在提现日期范围内
     * 配置格式：{"start": 25, "days": 6}
     * 表示每月从第 start 天开始，连续 days 天可以提现（不允许跨月）
     */
    private void validateWithdrawDate() {
        // 获取提现日期配置
        String configValue = configApi.getConfigValueByKey(WITHDRAW_DAYS_CONFIG_KEY);
        if (configValue == null || configValue.isEmpty()) {
            throw exception(TENANT_BALANCE_WITHDRAW_CONFIG_NOT_EXISTS);
        }

        // 解析配置
        cn.hutool.json.JSONObject config = JSONUtil.parseObj(configValue);
        int startDay = config.getInt("start");
        int days = config.getInt("days");

        // 获取当前日期
        LocalDate today = LocalDate.now();
        int currentDay = today.getDayOfMonth();
        int lastDayOfMonth = today.lengthOfMonth();

        // 计算提现日期范围（不允许跨月，endDay 最大为当月最后一天）
        int endDay = Math.min(startDay + days - 1, lastDayOfMonth);

        // 判断当前日期是否在 [startDay, endDay] 范围内
        boolean isInRange = currentDay >= startDay && currentDay <= endDay;

        if (!isInRange) {
            throw exception(TENANT_BALANCE_WITHDRAW_NOT_IN_DATE);
        }
    }


}