package com.bizzan.bitrade.controller.finance;

import static com.bizzan.bitrade.constant.BooleanEnum.IS_FALSE;
import static com.bizzan.bitrade.constant.SysConstant.SESSION_MEMBER;
import static com.bizzan.bitrade.constant.WithdrawStatus.FAIL;
import static com.bizzan.bitrade.constant.WithdrawStatus.SUCCESS;
import static com.bizzan.bitrade.constant.WithdrawStatus.WAITING;
import static org.springframework.util.Assert.isTrue;
import static org.springframework.util.Assert.notNull;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.bizzan.bitrade.entity.transform.AuthMember;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import com.bizzan.bitrade.annotation.AccessLog;
import com.bizzan.bitrade.constant.AdminModule;
import com.bizzan.bitrade.constant.BooleanEnum;
import com.bizzan.bitrade.constant.NotifyType;
import com.bizzan.bitrade.constant.PageModel;
import com.bizzan.bitrade.constant.SysConstant;
import com.bizzan.bitrade.constant.TransactionType;
import com.bizzan.bitrade.constant.WithdrawStatus;
import com.bizzan.bitrade.controller.common.BaseAdminController;
import com.bizzan.bitrade.entity.Admin;
import com.bizzan.bitrade.entity.Coin;
import com.bizzan.bitrade.entity.Member;
import com.bizzan.bitrade.entity.MemberTransaction;
import com.bizzan.bitrade.entity.MemberWallet;
import com.bizzan.bitrade.entity.QMember;
import com.bizzan.bitrade.entity.QWithdrawRecord;
import com.bizzan.bitrade.entity.WithdrawRecord;
import com.bizzan.bitrade.es.ESUtils;
import com.bizzan.bitrade.model.screen.WithdrawRecordScreen;
import com.bizzan.bitrade.service.CoinService;
import com.bizzan.bitrade.service.LocaleMessageSourceService;
import com.bizzan.bitrade.service.MemberService;
import com.bizzan.bitrade.service.MemberTransactionService;
import com.bizzan.bitrade.service.MemberWalletService;
import com.bizzan.bitrade.service.WithdrawChainConfigService;
import com.bizzan.bitrade.service.WithdrawRecordService;
import com.bizzan.bitrade.util.InnerInvokeUtil;
import com.bizzan.bitrade.util.MessageResult;
import com.bizzan.bitrade.vendor.provider.SMSProvider;
import com.bizzan.bitrade.vo.WithdrawRecordVO;
import com.querydsl.core.types.Predicate;
import com.sparkframework.security.Encrypt;

import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;

/**
 * @author
 * @description 提现
 * @date 2019/2/25 11:22
 */
@RestController
@Slf4j
@RequestMapping("/finance/withdraw-record")
public class WithdrawRecordController extends BaseAdminController {
    @Autowired
    private WithdrawRecordService withdrawRecordService;

    @Autowired
    private MemberWalletService memberWalletService;

    @Autowired
    private MemberTransactionService memberTransactionService;

    @Autowired
    private LocaleMessageSourceService messageSource;
    @Autowired
    private ESUtils esUtils;

    @Autowired
    private SMSProvider smsProvider;

    @Autowired
    private MemberService memberService;
    @Autowired
    private CoinService coinService;
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;
    @Autowired
    private WithdrawChainConfigService withdrawChainConfigService;

    @Value("${withdraw.auto}")
    private boolean autoWithdraw;

    @Value("${spark.system.md5.key}")
    private String md5Key;
    @Autowired
    private InnerInvokeUtil invokeUtil;

    @RequiresPermissions("finance:withdraw-record:all")
    @GetMapping("/all")
    @AccessLog(module = AdminModule.FINANCE, operation = "所有提现记录WithdrawRecord")
    public MessageResult all() {
        List<WithdrawRecord> withdrawRecordList = withdrawRecordService.findAll();
        if (withdrawRecordList == null || withdrawRecordList.size() < 1) {
            return error(messageSource.getMessage("NO_DATA"));
        }
        return success(withdrawRecordList);
    }
    
    @RequiresPermissions("finance:withdraw-record:all")
    @GetMapping("/count")
    @AccessLog(module = AdminModule.FINANCE, operation = "所有提现记录WithdrawRecord")
    public MessageResult allCount() {
        return success(withdrawRecordService.countAll());
    }

    @RequiresPermissions(value = {"finance:withdraw-record:page-query", "finance:withdraw-record:page-query:success"}, logical = Logical.OR)
    @RequestMapping("/page-query")
    @AccessLog(module = AdminModule.FINANCE, operation = "分页查询提现记录WithdrawRecord")
    public MessageResult pageQuery(
            PageModel pageModel,
            WithdrawRecordScreen screen) {
        List<Predicate> predicates = new ArrayList<>();
        predicates.add(QWithdrawRecord.withdrawRecord.memberId.eq(QMember.member.id));

        if (screen.getMemberId() != null) {
            predicates.add(QWithdrawRecord.withdrawRecord.memberId.eq(screen.getMemberId()));
        }

        if ( !StringUtils.isEmpty(screen.getMobilePhone())){
            Member member = memberService.findByPhone(screen.getMobilePhone());
            predicates.add(QWithdrawRecord.withdrawRecord.memberId.eq(member.getId()));
        }

        if ( !StringUtils.isEmpty(screen.getOrderSn())){
            predicates.add(QWithdrawRecord.withdrawRecord.transactionNumber.eq(screen.getOrderSn()));
        }

        if (screen.getStatus() != null) {
            predicates.add(QWithdrawRecord.withdrawRecord.status.eq(screen.getStatus()));
        }

        if (screen.getIsAuto() != null) {
            predicates.add(QWithdrawRecord.withdrawRecord.isAuto.eq(screen.getIsAuto()));
        }

        if (!StringUtils.isEmpty(screen.getAddress())) {
            predicates.add(QWithdrawRecord.withdrawRecord.address.eq(screen.getAddress()));
        }

        if (!StringUtils.isEmpty(screen.getUnit())) {
            predicates.add(QWithdrawRecord.withdrawRecord.coin.unit.equalsIgnoreCase(screen.getUnit()));
        }

        if (!StringUtils.isEmpty(screen.getAccount())) {
            predicates.add(QMember.member.username.like("%" + screen.getAccount() + "%")
                    .or(QMember.member.realName.like("%" + screen.getAccount() + "%")));
        }

        Page<WithdrawRecordVO> pageListMapResult = withdrawRecordService.joinFind(predicates, pageModel);
        return success(pageListMapResult);
    }

    @GetMapping("/{id}")
    @RequiresPermissions("finance:withdraw-record:detail")
    @AccessLog(module = AdminModule.FINANCE, operation = "提现记录WithdrawRecord 详情")
    public MessageResult detail(@PathVariable("id") Long id) {
        WithdrawRecord withdrawRecord = withdrawRecordService.findOne(id);
        notNull(withdrawRecord, messageSource.getMessage("NO_DATA"));
        return success(withdrawRecord);
    }

    //一键审核通过
    @RequiresPermissions("finance:withdraw-record:audit-pass")
    @PatchMapping("/audit-pass")
    @AccessLog(module = AdminModule.FINANCE, operation = "提现记录WithdrawRecord一键审核通过")
    public MessageResult auditPass(@RequestParam("ids") Long[] ids) {
        withdrawRecordService.audit(ids, WAITING, "", autoWithdraw);
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String date = df.format(new Date());
        boolean abnormal = false;
        for(Long id : ids) {
        	WithdrawRecord record = withdrawRecordService.findOne(id);
        	
        	Member member = memberService.findOne(record.getMemberId());
        	if(member != null) {
        		try {
					smsProvider.sendCustomMessage(member.getMobilePhone(), "尊敬的用户，恭喜您于" + date + "提取 " + record.getCoin().getUnit() + "成功，数量为" + record.getArrivedAmount() + "。");
				} catch (Exception e) {
					e.printStackTrace();
				}
        	}
        	//abnormal = !invokeUtil.validateWithdrawAddress(record.getMemberId(),record.getCoin().getUnit(),record.getId(),record.getAddress());
        }
        if(abnormal)
        	return success("提币地址异常，请对照提币短信谨慎操作");
        else
        	return success(messageSource.getMessage("PASS_THE_AUDIT"));
    }

    //一键审核不通过
    @RequiresPermissions("finance:withdraw-record:audit-no-pass")
    @PatchMapping("/audit-no-pass")
    @AccessLog(module = AdminModule.FINANCE, operation = "提现记录WithdrawRecord一键审核不通过")
    public MessageResult auditNoPass(@RequestParam("ids") Long[] ids, String remark) {
        withdrawRecordService.audit(ids, FAIL, remark, autoWithdraw);
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String date = df.format(new Date());
        for(Long id : ids) {
        	WithdrawRecord record = withdrawRecordService.findOne(id);
        	Member member = memberService.findOne(record.getMemberId());
        	if(member != null) {
        		try {
					smsProvider.sendCustomMessage(member.getMobilePhone(), "尊敬的用户，万分抱歉，您于" + date + "提取 " + record.getCoin().getUnit() + "失败，数量为" + record.getArrivedAmount() + "，请联系客服处理。");
				} catch (Exception e) {
					e.printStackTrace();
				}
        	}
        }
        return success(messageSource.getMessage("AUDIT_DOES_NOT_PASS"));
    }

    /**
     * 单个打款 转账成功添加流水号
     *
     * @param id
     * @param transactionNumber
     * @return
     */
    @RequiresPermissions("finance:withdraw-record:add-transaction-number")
    @PatchMapping("/add-transaction-number")
    @AccessLog(module = AdminModule.FINANCE, operation = "添加交易流水号")
    @Transactional(rollbackFor = Exception.class)
    public MessageResult addNumber(
            @RequestParam("id") Long id,
            @RequestParam("transactionNumber") String transactionNumber) {
        WithdrawRecord record = withdrawRecordService.findOne(id);
        Assert.notNull(record, "该记录不存在");
        Assert.isTrue(record.getIsAuto() == BooleanEnum.IS_FALSE, "该提现单为自动审核");
        record.setTransactionNumber(transactionNumber);
        record.setStatus(WithdrawStatus.SUCCESS);
        Coin coin = record.getCoin();
        if(coin.getUnit().equals("TUSDT")||coin.getUnit().equals("EUSDT")||coin.getUnit().equals("USDT-BEP20")) {
        	coin = coinService.findByUnit("USDT");
        }
        MemberWallet memberWallet = memberWalletService.findByCoinAndMemberId(coin, record.getMemberId());
        Assert.notNull(memberWallet, "member id " + record.getMemberId() + " 的 wallet 为 null");
        //memberWallet.setFrozenBalance(memberWallet.getFrozenBalance().subtract(record.getTotalAmount()));
        //memberWalletService.save(memberWallet);
        //invokeUtil.withdraw(coin.getUnit(), record.getMemberId(), record.getTotalAmount());
        int modifiedCount = memberWalletService.decreaseFrozen(memberWallet.getId(), record.getTotalAmount());

    	log.info("modifiedCount={}",modifiedCount);
    	if(modifiedCount <= 0)
        	return MessageResult.success(messageSource.getMessage("SUCCESS"), record);
    	
    	kafkaTemplate.send("trading-account-change", memberWallet.getMemberId()+"");
        
        record = withdrawRecordService.save(record);
        
    	JSONObject jsonObject = JSONObject.fromObject(record);
    	jsonObject.put("unit", coin.getUnit());
    	jsonObject.put("orderId", id);
    	memberService.notifyByEmail(record.getMemberId(), NotifyType.WITHDRAW, jsonObject);

    	log.info("MemberTransaction start");
        MemberTransaction memberTransaction = new MemberTransaction();
        memberTransaction.setMemberId(record.getMemberId());
        memberTransaction.setAddress(record.getAddress());
        memberTransaction.setAmount(BigDecimal.ZERO.subtract(record.getTotalAmount()));
        memberTransaction.setSymbol(coin.getUnit());
        memberTransaction.setCreateTime(record.getCreateTime());
        memberTransaction.setType(TransactionType.WITHDRAW);
        memberTransaction.setFee(record.getFee());
        memberTransaction.setRealFee(record.getFee()+"");
        memberTransaction.setDiscountFee("0");
        memberTransaction= memberTransactionService.save(memberTransaction);
    	log.info("MemberTransaction end");

        return MessageResult.success(messageSource.getMessage("SUCCESS"), record);
    }

    //批量打款
    @RequiresPermissions("finance:withdraw-record:remittance")
    @PatchMapping("/remittance")
    @AccessLog(module = AdminModule.FINANCE, operation = "提现记录/批量打款")
    @Transactional(rollbackFor = Exception.class)
    public MessageResult remittance(
            @RequestAttribute(SysConstant.SESSION_ADMIN) Admin admin,
            @RequestParam("ids") Long[] ids,
            @RequestParam("transactionNumber") String transactionNumber,
            @RequestParam("password") String password) {
        if (null == admin) {
            return error(messageSource.getMessage("DATA_EXPIRED_LOGIN_AGAIN"));
        }
        password = Encrypt.MD5(password + md5Key);
        if (!password.equals(admin.getPassword())) {
            return error(messageSource.getMessage("WRONG_PASSWORD"));
        }
        WithdrawRecord withdrawRecord;
        for (Long id : ids) {
            withdrawRecord = withdrawRecordService.findOne(id);
            notNull(withdrawRecord, "id :" + id + messageSource.getMessage("NO_DATA"));
            isTrue(withdrawRecord.getStatus() == WAITING, "提现状态不是等待放币,不能打款!");
            isTrue(withdrawRecord.getIsAuto() == IS_FALSE, "不是人工审核提现!");
            //标记提现完成
            withdrawRecord.setStatus(SUCCESS);
            Coin coin = withdrawRecord.getCoin();
            if(coin.getUnit().equals("TUSDT")||coin.getUnit().equals("EUSDT")||coin.getUnit().equals("USDT-BEP20")) {
            	coin = coinService.findByUnit("USDT");
            }
            //交易编码
            withdrawRecord.setTransactionNumber(transactionNumber);
            MemberWallet memberWallet = memberWalletService.findByCoinAndMemberId(coin, withdrawRecord.getMemberId());
            Assert.notNull(memberWallet, "member id " + withdrawRecord.getMemberId() + " 的 wallet 为 null");
            //memberWallet.setFrozenBalance(memberWallet.getFrozenBalance().subtract(withdrawRecord.getTotalAmount()));
            //memberWalletService.save(memberWallet);
            //invokeUtil.withdraw(coin.getUnit(), withdrawRecord.getMemberId(), withdrawRecord.getTotalAmount());
            int modifiedCount = memberWalletService.decreaseFrozen(memberWallet.getId(), withdrawRecord.getTotalAmount());
            if(modifiedCount <= 0)
            	continue;
            withdrawRecordService.save(withdrawRecord);
            
            JSONObject jsonObject = JSONObject.fromObject(withdrawRecord);
        	jsonObject.put("unit", withdrawRecord.getCoin().getUnit());
        	jsonObject.put("orderId", id);
        	memberService.notifyByEmail(withdrawRecord.getMemberId(), NotifyType.WITHDRAW, jsonObject);

            MemberTransaction memberTransaction = new MemberTransaction();
            memberTransaction.setMemberId(withdrawRecord.getMemberId());
            memberTransaction.setAddress(withdrawRecord.getAddress());
            memberTransaction.setAmount(BigDecimal.ZERO.subtract(withdrawRecord.getTotalAmount()));
            memberTransaction.setSymbol(withdrawRecord.getCoin().getUnit());
            memberTransaction.setCreateTime(withdrawRecord.getCreateTime());
            memberTransaction.setType(TransactionType.WITHDRAW);
            memberTransaction.setFee(withdrawRecord.getFee());
            memberTransaction.setRealFee(withdrawRecord.getFee()+"");
            memberTransaction.setDiscountFee("0");
            memberTransaction= memberTransactionService.save(memberTransaction);

        }
        return success();
    }

    /**
     * 链上放币 Step1：生成签名参数（不扣款，不改状态）
     * 前端拿到参数后调用合约，完成后回调 chain-callback
     */
    @RequiresPermissions("finance:withdraw-record:remittance")
    @PostMapping("/chain-apply/{id}")
    @AccessLog(module = AdminModule.FINANCE, operation = "链上提现申请签名")
    public MessageResult chainApply(@PathVariable("id") Long id) {
        WithdrawRecord record = withdrawRecordService.findOne(id);
        Assert.notNull(record, "提现记录不存在");
        Assert.isTrue(record.getStatus() == WAITING, "只能对等待放币(WAITING)状态的记录发起链上提现");
        try {
            java.util.Map<String, Object> params = withdrawChainConfigService.buildApplyParams(record);
            params.put("withdrawRecordId", record.getId());
            return success(params);
        } catch (Exception e) {
            log.error("[链上提现] 签名失败 id={}", id, e);
            return error(e.getMessage());
        }
    }

    /**
     * 链上放币 Step3：前端提交 txHash，后端验证并标记成功
     * 成功后与手动放币逻辑一致：decreaseFrozen + 邮件通知 + MemberTransaction
     */
    @PostMapping("/chain-callback")
    @AccessLog(module = AdminModule.FINANCE, operation = "链上提现回调")
    @Transactional(rollbackFor = Exception.class)
    public MessageResult chainCallback(
            @RequestParam("id") Long id,
            @RequestParam("txHash") String txHash) {
        WithdrawRecord record = withdrawRecordService.findOne(id);
        Assert.notNull(record, "提现记录不存在");
        if (record.getStatus() == SUCCESS) return success("已成功");
        Assert.isTrue(record.getStatus() == WAITING, "提现状态异常");

        boolean ok = withdrawChainConfigService.verifyTransaction(record, txHash);
        if (!ok) {
            log.warn("[链上提现] 链上验证失败 id={} txHash={}", id, txHash);
            return error("链上验证失败，交易未确认或金额不匹配，请稍后重试");
        }

        // 与 addNumber 逻辑一致
        record.setTransactionNumber(txHash);
        record.setStatus(SUCCESS);
        record.setDealTime(new Date());

        Coin coin = record.getCoin();
        if (coin.getUnit().equals("TUSDT") || coin.getUnit().equals("EUSDT") || coin.getUnit().equals("USDT-BEP20")) {
            coin = coinService.findByUnit("USDT");
        }
        MemberWallet memberWallet = memberWalletService.findByCoinAndMemberId(coin, record.getMemberId());
        Assert.notNull(memberWallet, "用户钱包不存在");

        int modifiedCount = memberWalletService.decreaseFrozen(memberWallet.getId(), record.getTotalAmount());
        if (modifiedCount > 0) {
            kafkaTemplate.send("trading-account-change", memberWallet.getMemberId() + "");
        }

        withdrawRecordService.save(record);

        JSONObject jsonObject = JSONObject.fromObject(record);
        jsonObject.put("unit", coin.getUnit());
        jsonObject.put("orderId", id);
        memberService.notifyByEmail(record.getMemberId(), NotifyType.WITHDRAW, jsonObject);

        MemberTransaction memberTransaction = new MemberTransaction();
        memberTransaction.setMemberId(record.getMemberId());
        memberTransaction.setAddress(record.getAddress());
        memberTransaction.setAmount(BigDecimal.ZERO.subtract(record.getTotalAmount()));
        memberTransaction.setSymbol(coin.getUnit());
        memberTransaction.setCreateTime(record.getCreateTime());
        memberTransaction.setType(TransactionType.WITHDRAW);
        memberTransaction.setFee(record.getFee());
        memberTransaction.setRealFee(record.getFee() + "");
        memberTransaction.setDiscountFee("0");
        memberTransactionService.save(memberTransaction);

        log.info("[链上提现] 成功 id={} txHash={}", id, txHash);
        return success("链上提现成功");
    }

    /**
     * 查询代理商邀请用户提现记录
     * @param pageModel
     * @param screen
     * @param memberId
     * @return
     */
    @RequiresPermissions("finance:withdraw-record:superwithdraw-page-query")
    @PostMapping(value = "/superwithdraw-page-query")
    @AccessLog(module = AdminModule.FINANCE, operation = "查询代理商邀请用户提现记录WithdrawRecordController")
    @Transactional(rollbackFor = Exception.class)
    public MessageResult pageQuerySuper(
            PageModel pageModel,
            WithdrawRecordScreen screen,
            Long memberId) {

        Member checkMember = memberService.findOne(memberId);
        if(!checkMember.getSuperPartner().equals("1")) {
            return error("您不是代理商！");
        }

        List<Predicate> predicates = new ArrayList<>();
        predicates.add(QWithdrawRecord.withdrawRecord.memberId.eq(QMember.member.id));
        predicates.add(QMember.member.inviterId.eq(checkMember.getId()));

        if (screen.getMemberId() != null) {
            predicates.add(QWithdrawRecord.withdrawRecord.memberId.eq(screen.getMemberId()));
        }

        if ( !StringUtils.isEmpty(screen.getMobilePhone())){
            Member member = memberService.findByPhone(screen.getMobilePhone());
            predicates.add(QWithdrawRecord.withdrawRecord.memberId.eq(member.getId()));
        }

        if ( !StringUtils.isEmpty(screen.getOrderSn())){
            predicates.add(QWithdrawRecord.withdrawRecord.transactionNumber.eq(screen.getOrderSn()));
        }

        if (screen.getStatus() != null) {
            predicates.add(QWithdrawRecord.withdrawRecord.status.eq(screen.getStatus()));
        }

        if (screen.getIsAuto() != null) {
            predicates.add(QWithdrawRecord.withdrawRecord.isAuto.eq(screen.getIsAuto()));
        }

        if (!StringUtils.isEmpty(screen.getAddress())) {
            predicates.add(QWithdrawRecord.withdrawRecord.address.eq(screen.getAddress()));
        }

        if (!StringUtils.isEmpty(screen.getUnit())) {
            predicates.add(QWithdrawRecord.withdrawRecord.coin.unit.equalsIgnoreCase(screen.getUnit()));
        }

        if (!StringUtils.isEmpty(screen.getAccount())) {
            predicates.add(QMember.member.username.like("%" + screen.getAccount() + "%")
                    .or(QMember.member.realName.like("%" + screen.getAccount() + "%")));
        }

        Page<WithdrawRecordVO> pageListMapResult = withdrawRecordService.joinFind(predicates, pageModel);
        return success(pageListMapResult);
    }
}
