package com.sonic.lion.controller.web;

import com.google.common.collect.Lists;
import com.sonic.common.auth.domains.Session;
import com.sonic.common.rpc.Result;
import com.sonic.common.utils.RedisLock;
import com.sonic.lion.domain.entity.PayAccountFundThird;
import com.sonic.lion.domain.entity.WithdrawRequest;
import com.sonic.lion.domain.enums.CoinType;
import com.sonic.lion.domain.resp.GetPayAccountFundThirdResp;
import com.sonic.lion.enums.*;
import com.sonic.lion.service.PayAccountFundThirdService;
import com.sonic.lion.service.PayConfigService;
import com.sonic.lion.service.WithdrawService;
import com.sonic.lion.utils.MaskUtils;
import com.sonic.lion.utils.RedisKeyUtils;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author: code
 * @date: 2025/06/22
 * @Description:
 * @version: 1.0.0
 */
@Slf4j
@RestController
public class WithdrawController {

    @Autowired
    private PayAccountFundThirdService payAccountFundThirdService;
    @Autowired
    private WithdrawService withdrawService;
    @Autowired
    private PayConfigService payConfigService;
    @Autowired
    private RedisLock.RedisWrapper redisWrapper;
    @Autowired
    private RedisKeyUtils redisKeyUtils;

    @ApiOperation(value = "提现", tags = "提现")
    @PostMapping("/web/pay/trade/withdraw")
    public Result withdraw(@ApiParam @RequestBody @Validated WithdrawReq req, @ApiParam(hidden = true) Session session) {
        //加锁，避免并发重复处理
        RedisLock redisLock = new RedisLock(redisKeyUtils.withdrawLockKey(session.getUserId()), redisWrapper);
        redisLock.tryAcquireRun(60 * 1000, () -> {
            //校验渠道开关是否打开
            boolean channelSwitchBl = payConfigService.payoutChannelSwitchCheckPass(session.getUserId(), req.getPayChannel());
            ToastResultCode.CHANNEL_NOT_OPEN.check(!channelSwitchBl);

            PayAccountFundThird payAccountFundThird = payAccountFundThirdService.getByAccountIdAndAppType(session.getUserId(), ThirdAccountType.getWithdrawAccount(req.getPayChannel()));
            ToastResultCode.NO_THIRD_PARTY_ACCOUNT.check(payAccountFundThird == null);

            String desAccountNo = payAccountFundThird.getOpenId();
            ToastResultCode.THIRD_PARTY_ACCOUNT_UNBIND.check(StringUtils.isBlank(desAccountNo));

            //针对Airwallex渠道，获取用户的目标货币类型
            String currencyType = null;
            if(ThirdAccountType.STRIPE_PAYOUT == payAccountFundThird.getAppType()) {
//                //Airwallex渠道提现，但是扩展字段值为空的话直接抛出异常
//                ToastResultCode.THIRD_PARTY_ACCOUNT_UNBIND.check(StringUtils.isBlank(payAccountFundThird.getExtend()));
//                //查询三方渠道表获取渠道目标货币类型
//                CreateBeneficiaryOutput userBeneficiary = payAccountFundThirdService.getAirwallexPayOutBaseInfo(payAccountFundThird.getExtend());
//                //设置收款人的货币类型
//                currencyType = userBeneficiary.getAccountCurrency();
//                //Airwallex渠道提现，但是货币类型为空的话直接抛出异常
//                ToastResultCode.THIRD_PARTY_ACCOUNT_UNBIND.check(StringUtils.isBlank(currencyType));
            }
            //金额判断
            withdrawService.checkAmount(req.getPayChannel(),req.getAmount(),session.getUserId());

            //提现发起 将字段 挪动到 提现冻结中 。
            //提现失败 从冻结中 回滚 到 可提现账户  状态 失败。
            WithdrawRequest input = WithdrawRequest.builder()
                    .platform(Platform.PAY.getDesc())
                    .payChannel(req.getPayChannel().getValue())
                    .name(BizType.WITHDRAW.getDesc())
                    .srcAccountId(session.getUserId())
                    .srcAccountName("")
                    .desAccountNo(desAccountNo)
                    .desAccountName(payAccountFundThird.getName())
                    .amount(req.getAmount())
                    .remark(req.getRemark())
                    .coinType(req.getCoinType())
                    .currencyType(currencyType)
                    .createTime(LocalDateTime.now())
                    .build();
            if (req.getBindDataType() != null) {
                input.setDesAccountType(req.getBindDataType().getValue());
            }

            withdrawService.withdrawReview(input);
            return true;
        });
        return Result.success();
    }

    @ApiOperation(value = "渠道列表", tags = "提现输入申请页面")
    @PostMapping("/web/pay/trade/withdraw/channel-list")
    public Result Channel(@ApiParam(hidden = true) Session session) {
        List<String> payoutChannels ;
        List<String> debugUserIds = payConfigService.getDebugPayUserIds();
        if(debugUserIds != null && debugUserIds.contains(session.getUserId().toString())) {
            payoutChannels = Lists.newArrayList(PayChannel.STRIPE.name());
        } else {
            payoutChannels = payConfigService.getEnabledPayOutChannel();
        }
        if(payoutChannels.isEmpty()){
            return Result.success(new ArrayList<>());
        }
        List<WithdrawChannelResp> result = new ArrayList<>(payoutChannels.size()) ;
        for (String payoutChannel : payoutChannels) {
            WithdrawChannelResp withdrawChannelResp = new WithdrawChannelResp();
            withdrawChannelResp.setPayChannel(payoutChannel);
            PayAccountFundThird payAccountFundThird = payAccountFundThirdService.getByAccountIdAndAppType(session.getUserId(), ThirdAccountType.getWithdrawAccount(PayChannel.valueOf(payoutChannel)));
            withdrawChannelResp.setIsBind(payAccountFundThird!=null);
            if(payAccountFundThird != null){
                payAccountFundThird.setEmail(MaskUtils.maskEmail(payAccountFundThird.getEmail()));
            }
            result.add(withdrawChannelResp);
        }
        return Result.success(result);
    }


    @ApiOperation(value = "获取第三方提现账号信息")
    @PostMapping(value = "/web/pay/get_third_payout_account")
    public Result<List<GetPayAccountFundThirdResp>> getThirdPayoutAccount(@ApiParam(hidden = true) Session session) {
        log.info("get_third_payout_account userId:{}" ,session.getUserId());
        List<String> payoutChannels ;
        List<String> debugUserIds = payConfigService.getDebugPayUserIds();
        if (debugUserIds != null && debugUserIds.contains(session.getUserId().toString())) {
            payoutChannels = Lists.newArrayList(PayChannel.STRIPE.name());
        }else{
            payoutChannels = payConfigService.getEnabledPayOutChannel();
        }
        Map data = new HashMap<>(2);
        data.put("payoutChannel" , payoutChannels);
        return Result.success(data);
    }



    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class WithdrawReq {

        /**
         * 渠道ID
         */
        @NotNull
        @ApiModelProperty("支付渠道")
        private PayChannel payChannel;

        /**
         * 交易金额,单位:分
         */
        @NotNull
        @Min(value = 3000, message = "Must be greater than or equal to 30")
        @Max(value = 100000, message = "must be less than or equal to 1000")
        @ApiModelProperty("交易金额,单位:分")
        private Long amount;


        /**
         * 备注
         */
        @ApiModelProperty("备注")
        private String remark;

        /**
         * payChannel为braintree等聚合支付渠道时，转账需要选择所绑定的具体账户类型
         */
        @ApiModelProperty("payChannel为braintree等聚合支付渠道时，转账需要选择所绑定的具体账户类型")
        private BindDataType bindDataType;

        /**
         * 提现币类型
         */
        @ApiModelProperty("提现币类型，默认BUFF")
        private CoinType coinType = CoinType.BUFF;


        @NotBlank(message = "checkCode must not be empty")
        @ApiParam(value = "验证码", required = true)
        String checkCode;

    }


    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class WithdrawChannelResp {

        @ApiModelProperty("支付渠道")
        private String payChannel;

        @ApiModelProperty("是否绑定")
        private Boolean isBind;

        @ApiModelProperty("提现绑定的国家(是否是美国United States)")
        private Boolean isUs;

    }
}
