package com.yolo.keyboard.service.tenantwithdraworder;

import com.yolo.keyboard.controller.admin.tenantwithdraworder.vo.KeyboardTenantWithdrawOrderPageReqVO;
import com.yolo.keyboard.controller.admin.tenantwithdraworder.vo.KeyboardTenantWithdrawOrderRespVO;
import com.yolo.keyboard.controller.admin.tenantwithdraworder.vo.KeyboardTenantWithdrawOrderSaveReqVO;
import com.yolo.keyboard.dal.dataobject.tenantbalance.TenantBalanceDO;
import com.yolo.keyboard.dal.dataobject.tenantbalancetransaction.TenantBalanceTransactionDO;
import com.yolo.keyboard.dal.dataobject.tenantwithdraworder.KeyboardTenantWithdrawOrderDO;
import com.yolo.keyboard.dal.mysql.tenantbalance.TenantBalanceMapper;
import com.yolo.keyboard.dal.mysql.tenantbalancetransaction.TenantBalanceTransactionMapper;
import com.yolo.keyboard.dal.mysql.tenantwithdraworder.KeyboardTenantWithdrawOrderMapper;
import com.yolo.keyboard.framework.common.pojo.PageResult;
import com.yolo.keyboard.framework.common.util.object.BeanUtils;
import com.yolo.keyboard.framework.tenant.core.context.TenantContextHolder;
import com.yolo.keyboard.module.system.dal.dataobject.tenant.TenantDO;
import com.yolo.keyboard.module.system.dal.mysql.tenant.TenantMapper;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.yolo.keyboard.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yolo.keyboard.module.infra.enums.ErrorCodeConstants.TENANT_WITHDRAW_ORDER_NOT_EXISTS;

/**
 * 租户提现订单表（申请-审核-打款-完成/失败） Service 实现类
 *
 * @author ziin
 */
@Service
@Validated
public class KeyboardTenantWithdrawOrderServiceImpl implements KeyboardTenantWithdrawOrderService {

    @Resource
    private KeyboardTenantWithdrawOrderMapper tenantWithdrawOrderMapper;

    @Resource
    private TenantMapper tenantMapper;

    @Resource
    private TenantBalanceMapper tenantBalanceMapper;

    @Resource
    private TenantBalanceTransactionMapper tenantBalanceTransactionMapper;

    @Override
    public Long createTenantWithdrawOrder(KeyboardTenantWithdrawOrderSaveReqVO createReqVO) {
        // 插入
        KeyboardTenantWithdrawOrderDO tenantWithdrawOrder = BeanUtils.toBean(createReqVO, KeyboardTenantWithdrawOrderDO.class);
        tenantWithdrawOrderMapper.insert(tenantWithdrawOrder);

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

    @Override
    @Transactional
    public void updateTenantWithdrawOrder(KeyboardTenantWithdrawOrderSaveReqVO updateReqVO) {
        // 校验存在
        KeyboardTenantWithdrawOrderDO existingOrder = tenantWithdrawOrderMapper.selectById(updateReqVO.getId());
        if (existingOrder == null) {
            throw exception(TENANT_WITHDRAW_ORDER_NOT_EXISTS);
        }

        // 更新订单
        KeyboardTenantWithdrawOrderDO updateObj = BeanUtils.toBean(updateReqVO, KeyboardTenantWithdrawOrderDO.class);
        tenantWithdrawOrderMapper.updateById(updateObj);

        String newStatus = updateReqVO.getStatus();
        String oldStatus = existingOrder.getStatus();

        // 如果提现状态更新为成功（PAID），则扣除提现用户的冻结金额并创建流水记录
        if ("PAID".equals(newStatus) && !"PAID".equals(oldStatus)) {
            handleWithdrawSuccess(existingOrder);
        }
        // 如果提现状态更新为已拒绝、已取消、打款失败，则返还冻结金额到可提现余额并创建流水记录
        else if (isRefundStatus(newStatus) && !isRefundStatus(oldStatus)) {
            handleWithdrawRefund(existingOrder, newStatus, updateReqVO.getReason());
        }
    }

    /**
     * 判断是否为需要退还冻结金额的状态
     */
    private boolean isRefundStatus(String status) {
        return "REJECTED".equals(status) || "CANCELED".equals(status) || "FAILED".equals(status);
    }

    /**
     * 处理提现成功：扣除冻结金额并记录流水
     */
    private void handleWithdrawSuccess(KeyboardTenantWithdrawOrderDO order) {
        TenantBalanceDO balance = tenantBalanceMapper.selectById(order.getTenantId());
        if (balance == null) {
            return;
        }

        // 扣除冻结金额
        BigDecimal frozenAmt = balance.getFrozenAmt() != null ? balance.getFrozenAmt() : BigDecimal.ZERO;
        BigDecimal withdrawAmount = order.getAmount();
        BigDecimal newFrozenAmt = frozenAmt.subtract(withdrawAmount);
        if (newFrozenAmt.compareTo(BigDecimal.ZERO) < 0) {
            newFrozenAmt = BigDecimal.ZERO;
        }
        balance.setFrozenAmt(newFrozenAmt);
        tenantBalanceMapper.updateById(balance);

        // 创建提现成功的流水记录
        BigDecimal currentWithdrawableBalance = balance.getWithdrawableBalance() != null ? balance.getWithdrawableBalance() : BigDecimal.ZERO;
        TenantBalanceTransactionDO transaction = TenantBalanceTransactionDO.builder()
                .bizNo(order.getBizNo())
                .points(withdrawAmount.negate()) // 提现金额（负数表示支出）
                .balance(balance.getBalance()) // 当前总余额
                .frozenAmt(newFrozenAmt) // 扣除后的冻结金额
                .withdrawableBalance(currentWithdrawableBalance) // 当前可提现余额
                .tenantId(order.getTenantId())
                .type("WITHDRAW_SUCCESS")
                .description("提现成功")
                .orderId(order.getWithdrawNo())
                .operatorId(TenantContextHolder.getTenantId())
                .createdAt(LocalDateTime.now())
                .build();
        tenantBalanceTransactionMapper.insert(transaction);
    }

    /**
     * 处理提现退还：返还冻结金额到可提现余额并记录流水
     *
     * @param order     提现订单
     * @param newStatus 新状态
     * @param reason    拒绝/失败/取消原因
     */
    private void handleWithdrawRefund(KeyboardTenantWithdrawOrderDO order, String newStatus, String reason) {
        TenantBalanceDO balance = tenantBalanceMapper.selectById(order.getTenantId());
        if (balance == null) {
            return;
        }

        BigDecimal withdrawAmount = order.getAmount();

        // 扣除冻结金额
        BigDecimal frozenAmt = balance.getFrozenAmt() != null ? balance.getFrozenAmt() : BigDecimal.ZERO;
        BigDecimal newFrozenAmt = frozenAmt.subtract(withdrawAmount);
        if (newFrozenAmt.compareTo(BigDecimal.ZERO) < 0) {
            newFrozenAmt = BigDecimal.ZERO;
        }
        balance.setFrozenAmt(newFrozenAmt);

        // 返还到可提现余额
        BigDecimal withdrawableBalance = balance.getWithdrawableBalance() != null ? balance.getWithdrawableBalance() : BigDecimal.ZERO;
        BigDecimal newWithdrawableBalance = withdrawableBalance.add(withdrawAmount);
        balance.setWithdrawableBalance(newWithdrawableBalance);

        tenantBalanceMapper.updateById(balance);

        // 根据状态确定流水类型和描述
        String type;
        String description;
        switch (newStatus) {
            case "REJECTED":
                type = "WITHDRAW_REJECTED";
                description = "提现被拒绝，金额已退还";
                break;
            case "CANCELED":
                type = "WITHDRAW_CANCELED";
                description = "提现已取消，金额已退还";
                break;
            case "FAILED":
                type = "WITHDRAW_FAILED";
                description = "提现打款失败，金额已退还";
                break;
            default:
                type = "WITHDRAW_REFUND";
                description = "提现退还";
        }

        // 备注：优先使用传入的原因，如果没有则使用默认描述
        String remark = (reason != null && !reason.trim().isEmpty()) ? reason : description;

        // 创建退还流水记录
        TenantBalanceTransactionDO transaction = TenantBalanceTransactionDO.builder()
                .bizNo(order.getBizNo())
                .points(withdrawAmount) // 退还金额（正数表示收入）
                .balance(balance.getBalance()) // 当前总余额
                .frozenAmt(newFrozenAmt) // 扣除后的冻结金额
                .withdrawableBalance(newWithdrawableBalance) // 退还后的可提现余额
                .tenantId(order.getTenantId())
                .type(type)
                .description(description)
                .remark(remark)
                .orderId(order.getWithdrawNo())
                .operatorId(TenantContextHolder.getTenantId())
                .createdAt(LocalDateTime.now())
                .build();
        tenantBalanceTransactionMapper.insert(transaction);
    }

    @Override
    public void deleteTenantWithdrawOrder(Long id) {
        // 校验存在
        validateTenantWithdrawOrderExists(id);
        // 删除
        tenantWithdrawOrderMapper.deleteById(id);
    }

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


    private void validateTenantWithdrawOrderExists(Long id) {
        if (tenantWithdrawOrderMapper.selectById(id) == null) {
            throw exception(TENANT_WITHDRAW_ORDER_NOT_EXISTS);
        }
    }

    @Override
    public KeyboardTenantWithdrawOrderDO getTenantWithdrawOrder(Long id) {
        return tenantWithdrawOrderMapper.selectById(id);
    }

    @Override
    public PageResult<KeyboardTenantWithdrawOrderRespVO> getTenantWithdrawOrderPage(KeyboardTenantWithdrawOrderPageReqVO pageReqVO) {
        // 根据当前租户级别过滤下级租户的提现申请
        Long currentTenantId = TenantContextHolder.getTenantId();
        if (currentTenantId != null) {
            TenantDO currentTenant = tenantMapper.selectById(currentTenantId);
            if (currentTenant != null && currentTenant.getTenantLevel() != null) {
                if (currentTenant.getTenantLevel() == 0) {
                    // 系统管理员：只能查看1级代理的提现申请
                    List<TenantDO> firstLevelAgents = tenantMapper.selectList(
                            new LambdaQueryWrapper<TenantDO>().eq(TenantDO::getTenantLevel, 1));
                    List<Long> firstLevelAgentIds = firstLevelAgents.stream()
                            .map(TenantDO::getId)
                            .collect(Collectors.toList());
                    if (CollUtil.isEmpty(firstLevelAgentIds)) {
                        // 没有1级代理，返回空结果
                        return PageResult.empty(0L);
                    }
                    pageReqVO.setTenantIds(firstLevelAgentIds);
                } else {
                    // 非系统管理员：只能查看直属下级租户的提现申请
                    List<TenantDO> subordinateTenants = tenantMapper.selectList(
                            new LambdaQueryWrapper<TenantDO>().eq(TenantDO::getParentId, currentTenantId));
                    List<Long> subordinateTenantIds = subordinateTenants.stream()
                            .map(TenantDO::getId)
                            .collect(Collectors.toList());
                    if (CollUtil.isEmpty(subordinateTenantIds)) {
                        // 没有下级租户，返回空结果
                        return PageResult.empty(0L);
                    }
                    pageReqVO.setTenantIds(subordinateTenantIds);
                }
            }
        }

        // 分页查询租户提现订单数据
        PageResult<KeyboardTenantWithdrawOrderDO> pageResult = tenantWithdrawOrderMapper.selectPage(pageReqVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return PageResult.empty(pageResult.getTotal());
        }

        // 批量获取租户名称 - 提升性能，避免N+1查询
        List<Long> tenantIds = pageResult.getList().stream()
                .map(KeyboardTenantWithdrawOrderDO::getTenantId)
                .distinct()
                .collect(Collectors.toList());
        List<TenantDO> tenants = tenantMapper.selectBatchIds(tenantIds);
        Map<Long, String> tenantNameMap = CollUtil.isEmpty(tenants)
                ? new HashMap<>()  // 如果没有查询到租户数据，返回空map
                : tenants.stream().collect(Collectors.toMap(TenantDO::getId, TenantDO::getName, (a, b) -> a)); // 构建租户ID到名称的映射

        // 转换为 VO 并填充租户名称
        List<KeyboardTenantWithdrawOrderRespVO> voList = pageResult.getList().stream().map(order -> {
            // 将DO转换为VO
            KeyboardTenantWithdrawOrderRespVO vo = BeanUtils.toBean(order, KeyboardTenantWithdrawOrderRespVO.class);
            // 根据租户ID获取并设置租户名称
            vo.setTenantName(tenantNameMap.get(order.getTenantId()));
            return vo;
        }).collect(Collectors.toList());
        
        // 返回包含VO列表和总数的分页结果
        return new PageResult<>(voList, pageResult.getTotal());
    }

}