package cn.iocoder.yudao.module.system.service.tenantbalance;

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.system.controller.admin.tenantbalance.vo.*;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenantagencypackage.TenantAgencyPackageDO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenantbalance.TenantBalanceDO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenantpoints.TenantPointsDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper;
import cn.iocoder.yudao.module.system.dal.mysql.tenantagencypackage.TenantAgencyPackageMapper;
import cn.iocoder.yudao.module.system.dal.mysql.tenantbalance.TenantBalanceMapper;
import cn.iocoder.yudao.module.system.dal.mysql.tenantpoints.TenantPointsMapper;
import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import cn.iocoder.yudao.module.system.util.BizNoGenerator;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mzt.logapi.starter.annotation.LogRecord;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.system.enums.balance.BalanceEnum.*;

/**
 * 租户余额 Service 实现类
 *
 * @author 总后台
 */
@Service
@Validated
public class TenantBalanceServiceImpl implements TenantBalanceService {

    @Resource
    private TenantBalanceMapper tenantBalanceMapper;

    @Resource
    private TenantPointsMapper tenantPointsMapper;

    @Resource
    private AdminUserService userService;

    @Resource
    private TenantMapper tenantMapper;

    @Resource
    private TenantAgencyPackageMapper tenantAgencyPackageMapper;

    @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) {
        // 校验存在
        validateTenantBalanceExists(ids);
        // 删除
        tenantBalanceMapper.deleteByIds(ids);
        }

    private void validateTenantBalanceExists(List<Long> ids) {
        List<TenantBalanceDO> list = tenantBalanceMapper.selectByIds(ids);
        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
            throw exception(TENANT_BALANCE_NOT_EXISTS);
        }
    }

    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<TenantBalanceRespVO> getTenantBalancePage(TenantBalancePageReqVO pageReqVO) {
        IPage<TenantBalanceRespVO> iPage = new Page<>(pageReqVO.getPageNo(),pageReqVO.getPageSize());
        IPage<TenantBalanceRespVO> tenantBalanceRespVOIPage = tenantBalanceMapper.selectPageWithTenantName(iPage, pageReqVO);
        return new  PageResult<>(tenantBalanceRespVOIPage.getRecords(),tenantBalanceRespVOIPage.getTotal());
    }

     @Override
     @Transactional(rollbackFor = Exception.class)
    public void addTenantBalance(TenantBalanceAddReqVO addReqVO) {

        // 校验存在
         if (tenantBalanceMapper.selectById(addReqVO.getId()) == null) {
                 // 初始化余额为 0
                 TenantBalanceDO tenantBalance = new TenantBalanceDO();
                 tenantBalance.setId(addReqVO.getId());
                 tenantBalance.setBalance(0);
                 tenantBalance.setVersion(0);
                 tenantBalanceMapper.insert(tenantBalance);
         }
         String recharge = BizNoGenerator.generate("RECHARGE");
         // 获取初始化查询结果

        TenantBalanceDO tenantBalance = tenantBalanceMapper.selectById(addReqVO.getId());
         Integer newBalance = tenantBalance.getBalance()+addReqVO.getAmount();
         Integer oldVersion = tenantBalance.getVersion();

         Integer updateCount = tenantBalanceMapper.updateBalanceWithVersion(addReqVO.getId(), addReqVO.getAmount(), oldVersion);
         if (updateCount == 0) {
             throw  exception(TENANT_BALANCE_RECHARGE_ERROR);
         }
         LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
         if (loginUser == null) {
             throw exception(USER_NOT_EXISTS);
         }

         TenantPointsDO tenantPointsDO = new TenantPointsDO();
         tenantPointsDO.setTenantId(addReqVO.getId());
         tenantPointsDO.setPoints(addReqVO.getAmount());
         tenantPointsDO.setBalance(newBalance);
         tenantPointsDO.setOperatorId(loginUser.getId());
         tenantPointsDO.setType(RECHARGE.getDesc());               // RECHARGE / BONUS / ...
         tenantPointsDO.setDescription("总后台充值,金额："+addReqVO.getAmount());
         tenantPointsDO.setRemark(addReqVO.getRemark());
         tenantPointsDO.setBizNo(recharge);
         tenantPointsMapper.insert(tenantPointsDO);
     }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void transferToTenant(TenantBalanceTransferReqVO transferReqVO) {

        /**
         * 执行租户余额转账操作
         * 注：这部分代码应该位于某个转账相关的方法中
         */
        // 获取当前登录用户ID，用于操作记录和权限验证
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        
        // 生成转账业务流水号，用于唯一标识这笔转账交易
        String transfer = BizNoGenerator.generate("TRANSFER");
        
        // 验证转账密码是否为空
        if (transferReqVO.getPassword().isEmpty()) {
            throw exception(TENANT_BALANCE_TRANSFER_PASSWORD_ERROR_IS_NULL);
        }
        TenantDO targetTenantInfo = tenantMapper.selectById(transferReqVO.getTargetTenantId());


        // 获取当前登录用户信息
        AdminUserDO user = userService.getUser(loginUserId);
        
        // 验证转账密码是否正确
        if (!userService.isPasswordMatch(transferReqVO.getPassword(), user.getPassword())) {
            throw exception(TENANT_BALANCE_TRANSFER_PASSWORD_ERROR);
        }
        
        // 获取当前租户ID（转出方）
        Long tenantId = TenantContextHolder.getTenantId();

        if (!Objects.equals(targetTenantInfo.getParentId(), tenantId)) {
            throw exception(TENANT_BALANCE_TRANSFER_ERROR_TARGET_NOT_SUBORDINATE);
        }

        // 校验当前租户钱包是否存在
        TenantBalanceDO tenantBalanceDO = tenantBalanceMapper.selectById(tenantId);
        if (tenantBalanceDO == null) {
            throw exception(TENANT_WALLET_NOT_EXISTS);
        }
        
        // 获取目标租户（转入方）的余额信息
        TenantBalanceDO targetTenantBalance = tenantBalanceMapper.selectById(transferReqVO.getTargetTenantId());
        
        // 校验目标租户钱包是否存在
        if (targetTenantBalance == null) {
            throw exception(TENANT_BALANCE_TRANSFER_TARGET_NOT_EXISTS);
        }
        
        // 校验当前租户余额是否足够支付转账金额
        if (tenantBalanceDO.getBalance() < transferReqVO.getTransferAmount()) {
            throw exception(TENANT_BALANCE_NOT_ENOUGH);
        }
        
        // 校验转账金额是否有效（必须大于0）
        if (transferReqVO.getTransferAmount() <= 0) {
            throw exception(TENANT_BALANCE_TRANSFER_ERROR);
        }
        
        // 计算转账后转出方的新余额
        Integer tenantNewBalance = tenantBalanceDO.getBalance() - transferReqVO.getTransferAmount();
        
        // 计算转账后转入方的新余额
        Integer targetTenantNewBalance = targetTenantBalance.getBalance() + transferReqVO.getTransferAmount();
        
        // 获取转出方当前的版本号（用于乐观锁控制）
        Integer tenantBalanceDOVersion = tenantBalanceDO.getVersion();
        
        // 获取转入方当前的版本号（用于乐观锁控制）
        Integer targetTenantBalanceVersion = targetTenantBalance.getVersion();
        
        // 先更新转入方的余额（使用乐观锁）
        Integer updateTargetBalanceCount = tenantBalanceMapper
        .updateBalanceWithVersion(transferReqVO.getTargetTenantId(), transferReqVO.getTransferAmount(), targetTenantBalanceVersion);
        
        // 如果更新转入方失败，抛出异常
        if (updateTargetBalanceCount == 0) {
            throw exception(TENANT_BALANCE_TRANSFER_OPERATION_ERROR);
        }
        
        // 再更新转出方的余额（使用乐观锁，金额为负数表示减少）
        Integer updateCount = tenantBalanceMapper
        .updateBalanceWithVersion(tenantId, -transferReqVO.getTransferAmount(), tenantBalanceDOVersion);
        
        // 如果更新转出方失败，抛出异常
        if (updateCount == 0) {
            throw exception(TENANT_BALANCE_TRANSFER_OPERATION_ERROR);
        }
        
        // 创建转出方的积分变动记录
        TenantPointsDO tenantPointsDO = new TenantPointsDO();
        tenantPointsDO.setTenantId(tenantId); // 设置租户ID
        tenantPointsDO.setPoints(-transferReqVO.getTransferAmount()); // 设置变动积分（负数表示减少）
        tenantPointsDO.setBalance(tenantNewBalance); // 设置变动后的余额
        tenantPointsDO.setOperatorId(loginUserId); // 设置操作人ID
        tenantPointsDO.setType(TRANSFER_OUT.getDesc()); // 设置交易类型为转账
        tenantPointsDO.setDescription("转账给租户" + transferReqVO.getTargetTenantId() + "，金额：" + transferReqVO.getTransferAmount()); // 设置交易描述
        tenantPointsDO.setTargetTenantId(transferReqVO.getTargetTenantId()); // 设置目标租户ID
        tenantPointsDO.setBizNo(transfer); // 设置业务流水号
        tenantPointsDO.setRemark(transferReqVO.getRemark());
        int tenantInsert = tenantPointsMapper.insert(tenantPointsDO); // 插入记录
        
        // 创建转入方的积分变动记录
        TenantPointsDO targetPointsDO = new TenantPointsDO();
        targetPointsDO.setTenantId(transferReqVO.getTargetTenantId()); // 设置目标租户ID
        targetPointsDO.setPoints(transferReqVO.getTransferAmount()); // 设置变动积分（正数表示增加）
        targetPointsDO.setBalance(targetTenantNewBalance); // 设置变动后的余额
        targetPointsDO.setOperatorId(loginUserId); // 设置操作人ID
        targetPointsDO.setType(TRANSFER_IN.getDesc()); // 设置交易类型为转账
        targetPointsDO.setTargetTenantId(tenantId);// 设置目标租户ID
        targetPointsDO.setDescription("转账来自租户" + tenantId + "，金额：" + transferReqVO.getTransferAmount()); // 设置交易描述
        targetPointsDO.setBizNo(transfer); // 设置业务流水号（与转出方使用同一个流水号）
        int targetInsert = tenantPointsMapper.insert(targetPointsDO); // 插入记录
        
        // 验证交易记录是否都插入成功
        if (tenantInsert == 0 || targetInsert == 0) {
            throw exception(TENANT_BALANCE_TRANSFER_OPERATION_ERROR);
        }

    }

    @Override
    public TenantBalanceDO getSelfBalance() {
        Long tenantId = TenantContextHolder.getTenantId();
        TenantBalanceDO tenantBalance = tenantBalanceMapper.selectById(tenantId);
        if (tenantBalance == null) {
            throw exception(TENANT_WALLET_NOT_EXISTS);
        }
       return tenantBalance;
    }

    @Override
    public PageResult<TenantBalanceRespVO> getSelfSubordinateTenantBalancePage(TenantBalancePageReqVO pageReqVO) {
        Long tenantId = TenantContextHolder.getTenantId();
        IPage<TenantBalanceRespVO> iPage = new Page<>(pageReqVO.getPageNo(),pageReqVO.getPageSize());
        IPage<TenantBalanceRespVO> tenantBalanceRespVOIPage = tenantBalanceMapper.selectPageWithSelfSubordinateTenant(iPage, pageReqVO,tenantId);
        return new  PageResult<>(tenantBalanceRespVOIPage.getRecords(),tenantBalanceRespVOIPage.getTotal());
    }

    @Override
    public Boolean consumption(Long PackageId, Long targetTenantId,String remark) {

        Long tenantId = TenantContextHolder.getTenantId();
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();

        String consumption = BizNoGenerator.generate("CONSUMPTION");
        String order = BizNoGenerator.generate("ORDER");

        // 从数据库中查询租户余额
        TenantBalanceDO tenantBalance = tenantBalanceMapper.selectById(tenantId);
        if (tenantBalance == null) {
            throw exception(TENANT_WALLET_NOT_EXISTS);
        }
        // 从数据库中查询套餐信息
        TenantAgencyPackageDO tenantAgencyPackageDO = tenantAgencyPackageMapper.selectById(PackageId);
        if (tenantAgencyPackageDO == null) {
            throw exception(TENANT_AGENCY_PACKAGE_NOT_EXISTS);
        }
        // 检查租户余额是否足够
        if (tenantBalance.getBalance() < tenantAgencyPackageDO.getPrice()) {
            throw exception(TENANT_BALANCE_BALANCE_NOT_ENOUGH);
        }

        Integer tenantBalanceDOVersion = tenantBalance.getVersion();

        Integer oldBalance = tenantBalance.getBalance();

        Integer newBalance = oldBalance - tenantAgencyPackageDO.getPrice();
        // 更新租户余额
        Integer updateCount = tenantBalanceMapper.updateBalanceWithVersion(tenantId, -tenantAgencyPackageDO.getPrice(), tenantBalanceDOVersion);

        if (updateCount == 0) {
            throw exception(TENANT_BALANCE_CONSUMPTION_OPERATION_ERROR);
        }

        // 消费积分变动记录
        TenantPointsDO tenantPointsDO = new TenantPointsDO();
        tenantPointsDO.setTenantId(tenantId); // 设置租户ID
        tenantPointsDO.setPoints(-tenantAgencyPackageDO.getPrice()); // 设置变动积分（负数表示减少）
        tenantPointsDO.setBalance(newBalance); // 设置变动后的余额
        tenantPointsDO.setOperatorId(loginUserId); // 设置操作人ID
        tenantPointsDO.setType(CONSUMPTION.getDesc()); // 设置交易类型为转账
        tenantPointsDO.setDescription("给租户" + targetTenantId + "开通套餐" + tenantAgencyPackageDO.getName() + "，金额：" + tenantAgencyPackageDO.getPrice()); // 设置交易描述
        tenantPointsDO.setTargetTenantId(targetTenantId); // 设置目标租户ID
        tenantPointsDO.setBizNo(consumption); // 设置业务流水号
        tenantPointsDO.setRemark(remark);
        tenantPointsDO.setOrderId(order);
        int tenantInsert = tenantPointsMapper.insert(tenantPointsDO); // 插入记录
        if (tenantInsert == 0) {
            throw exception(TENANT_BALANCE_CONSUMPTION_OPERATION_ERROR);
        }

        return true;



    }

    @Override
    public Boolean reduceTestAccountNum(Long currentTenantId,String remark) {
        TenantBalanceDO tenantBalanceDO = tenantBalanceMapper.selectById(currentTenantId);
        if (tenantBalanceDO.getTestAccountNum()<=0) {
            throw exception(TENANT_BALANCE_TEST_ACCOUNT_NUM_NOT_ENOUGH);
        }
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        Integer oldVersion = tenantBalanceDO.getVersion();
        Integer newTestAccountNum = tenantBalanceDO.getTestAccountNum() - 1;

        Integer i = tenantBalanceMapper.updateTestAccountNumWithVersion(currentTenantId, -1, oldVersion);
        if (i == 0) {
            throw exception(TENANT_BALANCE_TEST_ACCOUNT_NUM_OPERATION_ERROR);
        }
        String consumption = BizNoGenerator.generate("CONSUMPTION");

        // 消费积分变动记录
        TenantPointsDO tenantPointsDO = new TenantPointsDO();
        tenantPointsDO.setTenantId(currentTenantId); // 设置租户ID
        tenantPointsDO.setPoints(-1); // 设置变动积分（负数表示减少）
        tenantPointsDO.setBalance(tenantPointsDO.getBalance());
        tenantPointsDO.setTestAccountNum(newTestAccountNum); // 设置变动后的余额
        tenantPointsDO.setOperatorId(loginUserId); // 设置操作人ID
        tenantPointsDO.setType(CONSUMPTION.getDesc()); // 设置交易类型为转账
        tenantPointsDO.setDescription("开通测试账户"); // 设置交易描述
        tenantPointsDO.setRemark(remark);
        tenantPointsDO.setBizNo(consumption); // 设置业务流水号
        int tenantInsert = tenantPointsMapper.insert(tenantPointsDO); // 插入记录
        if (tenantInsert == 0) {
            throw exception(TENANT_BALANCE_CONSUMPTION_OPERATION_ERROR);
        }
        return true;
    }

    @Override
    public Boolean addTestAccountNum(Long currentTenantId,String remark) {
        TenantBalanceDO tenantBalanceDO = tenantBalanceMapper.selectById(currentTenantId);

        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();

        Integer oldVersion = tenantBalanceDO.getVersion();

        Integer newTestAccountNum = tenantBalanceDO.getTestAccountNum() + 5;

        Integer i = tenantBalanceMapper.updateTestAccountNumWithVersion(currentTenantId, 5, oldVersion);
        if (i == 0) {
            throw exception(TENANT_BALANCE_TEST_ACCOUNT_NUM_OPERATION_ERROR);
        }
        String bonus = BizNoGenerator.generate("BONUS");

        // 消费积分变动记录
        TenantPointsDO tenantPointsDO = new TenantPointsDO();
        tenantPointsDO.setTenantId(currentTenantId); // 设置租户ID
        tenantPointsDO.setPoints(5); // 设置变动积分（负数表示减少）
        tenantPointsDO.setTestAccountNum(newTestAccountNum); // 设置变动后的余额
        tenantPointsDO.setBalance(tenantPointsDO.getBalance());
        tenantPointsDO.setOperatorId(loginUserId); // 设置操作人ID
        tenantPointsDO.setType(BONUS.getDesc()); // 设置交易类型为转账
        tenantPointsDO.setDescription("开通测试账户,增加测试账号数量"); // 设置交易描述
        tenantPointsDO.setBizNo(bonus); // 设置业务流水号
        tenantPointsDO.setRemark(remark);
        int tenantInsert = tenantPointsMapper.insert(tenantPointsDO); // 插入记录
        if (tenantInsert == 0) {
            throw exception(TENANT_BALANCE_CONSUMPTION_OPERATION_ERROR);
        }

        return true;
    }

}