package com.yc.ship.module.ota.service.tradelog;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yc.ship.framework.common.exception.ServiceException;
import com.yc.ship.framework.common.pojo.PageResult;
import com.yc.ship.framework.common.util.object.BeanUtils;
import com.yc.ship.framework.security.core.LoginUser;
import com.yc.ship.framework.tenant.core.util.TenantUtils;
import com.yc.ship.module.ota.controller.admin.distributor.vo.LoginTypeRespVO;
import com.yc.ship.module.ota.controller.admin.tradelog.vo.TradeLogPageReqVO;
import com.yc.ship.module.ota.controller.admin.tradelog.vo.TradeLogSaveReqVO;
import com.yc.ship.module.ota.dal.dataobject.distributor.DistributorDO;
import com.yc.ship.module.ota.dal.dataobject.tradelog.TradeLogDO;
import com.yc.ship.module.ota.dal.mysql.tradelog.TradeLogMapper;
import com.yc.ship.module.ota.enums.DistributorTradeTypeEnum;
import com.yc.ship.module.ota.service.distributor.DistributorService;
import com.yc.ship.module.ota.util.OtaAuthUtils;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;

import static com.yc.ship.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yc.ship.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static com.yc.ship.module.ota.enums.ErrorCodeConstants.*;

/**
 * 分销商交易日志 Service 实现类
 *
 * @author 管理员
 */
@Service
@Validated
@Slf4j
public class TradeLogServiceImpl implements TradeLogService {


    @Resource
    private RedissonClient redissonClient;

    @Resource
    private TradeLogMapper tradeLogMapper;

    @Resource
    private DistributorService distributorService;

    @Resource
    private OtaAuthUtils otaAuthUtils;

    @Override
    public Long createTradeLog(TradeLogSaveReqVO createReqVO) {
        // 插入
        TradeLogDO tradeLog = BeanUtils.toBean(createReqVO, TradeLogDO.class);
        Long id = IdWorker.getId(tradeLog);
        tradeLog.setId(id);
        tradeLog.setDeleted(false);
        tradeLogMapper.insert(tradeLog);
        // 返回
        return tradeLog.getId();
    }

    @Override
    public void updateTradeLog(TradeLogSaveReqVO updateReqVO) {
        // 校验存在
        validateTradeLogExists(updateReqVO.getId());
        // 更新
        TradeLogDO updateObj = BeanUtils.toBean(updateReqVO, TradeLogDO.class);
        tradeLogMapper.updateById(updateObj);
    }

    @Override
    public void deleteTradeLog(Long id) {
        // 校验存在
        validateTradeLogExists(id);
        // 删除
        tradeLogMapper.deleteById(id);
    }

    private void validateTradeLogExists(Long id) {
        if (tradeLogMapper.selectById(id) == null) {
            throw exception(TRADE_LOG_NOT_EXISTS);
        }
    }

    @Override
    public TradeLogDO getTradeLog(Long id) {
        LoginTypeRespVO getLoginType = distributorService.getLoginType();
        if (getLoginType.getType() != 3) {
            AtomicReference<TradeLogDO> tradeLogReference = new AtomicReference<>();
            TenantUtils.executeIgnore(() -> tradeLogReference.set(tradeLogMapper.selectById(id)));
            TradeLogDO vo = tradeLogReference.get();
            if(vo.getTradeType()==2 || vo.getTradeType()==7 || vo.getTradeType()==8){
                vo.setAfterBalance(vo.getAfterBalance().negate());
                vo.setBeforeBalance(vo.getBeforeBalance().negate());
            }
            return vo;
        }
        TradeLogDO vo = tradeLogMapper.selectById(id);
        if(vo.getTradeType()==2 || vo.getTradeType()==7 || vo.getTradeType()==8){
            vo.setAfterBalance(vo.getAfterBalance().negate());
            vo.setBeforeBalance(vo.getBeforeBalance().negate());
        }
        return vo;

    }

    @Override
    public PageResult<TradeLogDO> getTradeLogPage(TradeLogPageReqVO pageReqVO) {
        AtomicReference<PageResult<TradeLogDO>> pageResult = new AtomicReference<>();
//        LoginTypeRespVO getLoginType = distributorService.getLoginType();
//        if (getLoginType.getType() != 3) {
            // 旅行社端 或者 OTA端
//            if(pageReqVO.getDistributorId() == null) {
//                pageReqVO.setDistributorId(getLoginType.getDistributorId());
//            }
            LoginUser loginUser = otaAuthUtils.getAgencyLoginUser();
            if(loginUser!=null && loginUser.getDistributorId()!=null){
                pageReqVO.setDistributorId(loginUser.getDistributorId());
            }
            if(loginUser!=null && loginUser.getStoreId()!=null){
                pageReqVO.setStoreId(loginUser.getStoreId());
            }
            TenantUtils.executeIgnore(() -> pageResult.set(getTradeLogPageData(pageReqVO)));
            return pageResult.get();
//        }
//        return getTradeLogPageData(pageReqVO);
    }

    @Override
    public Long saveTradeLog(TradeLogDO tradeLogDO) {
        Long id = IdWorker.getId(tradeLogDO);
        tradeLogDO.setId(id);
        tradeLogDO.setDeleted(false);
        tradeLogMapper.insert(tradeLogDO);
        // 返回
        return tradeLogDO.getId();
    }

    private PageResult<TradeLogDO> getTradeLogPageData(TradeLogPageReqVO pageReqVO) {
        IPage<TradeLogDO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        if (pageReqVO.getTradeTime() != null) {
            LocalDateTime startTime = pageReqVO.getTradeTime()[0];
            LocalDateTime endTime = pageReqVO.getTradeTime()[1];
            pageReqVO.setTradeStartTime(DateUtil.format(startTime, "yyyy-MM-dd HH:mm:ss"));
            pageReqVO.setTradeEndTime(DateUtil.format(endTime, "yyyy-MM-dd HH:mm:ss"));
        }
        IPage<TradeLogDO> iPage = tradeLogMapper.getTradeLogPage(page, pageReqVO);
        iPage.getRecords().forEach(vo -> {
            if(vo.getTradeType()==2 || vo.getTradeType()==7 || vo.getTradeType()==8){
                vo.setAfterBalance(vo.getAfterBalance().negate());
                vo.setBeforeBalance(vo.getBeforeBalance().negate());
            }
        });
        return new PageResult<>(iPage.getRecords(), iPage.getTotal());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long distributorRecharge(Long id, Long selfRechargeId, Integer type, Long userId, String userName, BigDecimal money) {
        //充值金额
        String key = "ota_" + id;
        switch (type) {
            case 1:
                // 管理员充值
            case 11:
                // 自助充值
            case 12:
                // 定向库存购买
            case 13:
                // 定向库存回收
                key += "_balance";
                break;
            case 2:
                key += "_credit";
                break;
            case 3:
                key += "_inventory";
                break;
            default:
                break;
        }
        RLock lock = redissonClient.getLock(key);
        try {
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                DistributorDO distributorDO = distributorService.getDistributor(id);
                if (distributorDO == null) {
                    throw exception(DISTRIBUTOR_NOT_EXISTS);
                }
                boolean flag = false;
                BigDecimal beforeBalance = BigDecimal.ZERO;
                BigDecimal afterBalance = BigDecimal.ZERO;

                String tradeName = "";
                int tradeMode = 1;
                String payType = distributorDO.getPayType();
                String[] payTypeArray = payType.split(",");
                Stream<String> typeStream = Arrays.stream(payTypeArray);
                switch (type) {
                    case 1:
                    case 11:
                        beforeBalance = distributorDO.getBalance();
                        afterBalance = beforeBalance.add(money);
                        if (type == 1) {
                            tradeName = "管理员充值金额：" + money + "元,充值后余额为" + afterBalance + "元。";
                        } else {
                            tradeName = "余额自助充值金额：" + money + "元,充值后余额为" + afterBalance + "元。";
                        }
                        flag = distributorService.addBalance(distributorDO.getId(), money);
                        break;
                    case 2:
                        if (typeStream.noneMatch(element -> Integer.parseInt(element) == DistributorTradeTypeEnum.CREDIT_RECHARGE.getType())) {
                            throw exception(CREDIT_NOT_ENABLED);
                        }
                        // 授信额度 充值
                        if (distributorDO.getPayCredit() != null) {
                            beforeBalance = distributorDO.getSurplus();
                            afterBalance = distributorDO.getSurplus().subtract(money);
                            BigDecimal creditRemained = distributorDO.getPayCredit().subtract(afterBalance);
                            tradeName = "授信充值金额：" + money + "元,授信总额度" + distributorDO.getPayCredit() + "元,充值后剩余额度" + creditRemained + "元;已用额度" + afterBalance + "元";
                        } else {
                            tradeName = "授信充值金额：" + money + "元,授信总额度不限,充值后剩余额度不限;已用额度" + afterBalance + "元";
                        }
                        flag = distributorService.subSurplus(distributorDO.getId(), money);
                        break;
                    case 3:
                        if (typeStream.noneMatch(element -> Integer.parseInt(element) == DistributorTradeTypeEnum.STOCK_DIRECT_RECHARGE.getType())) {
                            throw exception(CREDIT_NOT_INVENTORY);
                        }
                        beforeBalance = distributorDO.getInventory();
                        afterBalance = beforeBalance.add(money);
                        tradeName = "定向库存充值金额：" + money + "元,充值后余额为" + afterBalance + "元。";
                        flag = distributorService.addInventory(distributorDO.getId(), money);
                        break;
                    case 12:
                        beforeBalance = distributorDO.getBalance();
                        afterBalance = beforeBalance.add(money);
                        tradeName = DistributorTradeTypeEnum.BALANCE_STOCK_DIRECT_RECHARGE.getName() + "：" + money + "元,购买后余额为" + afterBalance + "元。";
                        flag = distributorService.addBalance(distributorDO.getId(), money);
                        break;
                    case 13:
                        beforeBalance = distributorDO.getBalance();
                        afterBalance = beforeBalance.subtract(money);
                        tradeName = DistributorTradeTypeEnum.BALANCE_STOCK_DIRECT_BACK.getName() + "：" + money + "元,回收后余额为" + afterBalance + "元。";
                        flag = distributorService.subBalance(distributorDO.getId(), money);
                        tradeMode = 2;
                        break;
                    default:
                        break;
                }
                Long loginUserId = getLoginUserId();
                if (flag) {
                    //添加交易记录数据
                    TradeLogSaveReqVO logVO = new TradeLogSaveReqVO();
                    if (loginUserId != null) {
                        logVO.setOperatorId(loginUserId);
                    } else {
                        logVO.setOperatorId(userId);
                        logVO.setOperatorName(userName);
                    }
                    logVO.setOrderId(selfRechargeId.toString());
                    logVO.setRemark(tradeName);
                    logVO.setTradeMode(tradeMode);
                    logVO.setTradeType(type);
                    // 余额表示交易前金额 授信表示交易前已使用额度
                    logVO.setBeforeBalance(beforeBalance);
                    // 余额表示交易后金额 授信表示交易后已使用额度
                    logVO.setAfterBalance(afterBalance);
                    logVO.setTradeAmount(money);
                    logVO.setTradeTime(LocalDateTime.now());
                    logVO.setDistributorId(id);
                    Long tradeId = createTradeLog(logVO);
                    if (tradeId != null) {
                        return tradeId;
                    }
                }
            }
        } catch (ServiceException e) {
            log.error("[分销商自助充值]{}", e.getMessage());
            throw new ServiceException(e.getCode(), e.getMessage());
        } catch (Exception ex) {
            log.error("[分销商自助充值]", ex);
            throw exception(DISTRIBUTOR_RECHARGE_FAILED);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return null;
    }
}
