package com.izouma.nineth.web;

import com.alibaba.fastjson.JSONObject;
import com.gexin.rp.sdk.base.IPushResult;
import com.gexin.rp.sdk.base.impl.PushResult;
import com.izouma.nineth.config.Constants;
import com.izouma.nineth.config.SysConstants;
import com.izouma.nineth.domain.*;
import com.izouma.nineth.dto.*;
import com.izouma.nineth.dto.pay.UniformBindBank;
import com.izouma.nineth.enums.*;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.repo.*;
import com.izouma.nineth.security.Authority;
import com.izouma.nineth.security.JwtTokenUtil;
import com.izouma.nineth.security.JwtUserFactory;
import com.izouma.nineth.service.*;
import com.izouma.nineth.service.pay.UniformPayConstants;
import com.izouma.nineth.service.pay.UniformPayUtil;
import com.izouma.nineth.service.security.RSACacheService;
import com.izouma.nineth.service.sms.AliSmsService;
import com.izouma.nineth.service.sms.SmsService;
import com.izouma.nineth.utils.SecurityUtils;
import com.izouma.nineth.utils.SensitiveFilterUtil;
import com.izouma.nineth.utils.excel.ExcelUtils;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Pattern;

@AllArgsConstructor
@RestController
@RequestMapping("/user")
public class UserController extends BaseController {
    private UserRepo userRepo;
    private UserService userService;
    private UserRegisterService userRegisterService;
    private JwtTokenUtil jwtTokenUtil;
    private FollowService followService;
    private UserBankCardRepo userBankCardRepo;
    private UniformPayUtil uniformPayUtil;
    private SysConfigService sysConfigService;
    private ActivityInviteService activityInviteService;
    private AliSmsService aliSmsService;
    private UserFundAccountService userFundAccountService;
    private UserFundAccountRepo userFundAccountRepo;
    private ActivityInviteRepo activityInviteRepo;
    private InviteDateRepo inviteDateRepo;
    private SmsService smsService;
    private AssetRepo assetRepo;
    private UserWareHouseRepo userWareHouseRepo;
    private ProjectService projectService;
    private UserFundAccountForLianlianpayService userFundAccountForLianlianpayService;
    private LianlianPayService lianlianPayService;

    private RSACacheService rsaCacheService;

    private PushService pushService;

    /**
     * 管理后台注册？
     * @param username
     * @param password
     * @return
     */
    @PostMapping("/register")
    public User register(@RequestParam String username,
                         @RequestParam String password) {
        throw new BusinessException("暂不支持注册功能");
    }

    /**
     * 管理员创建账户
     * @param userRegister
     * @return
     */
    @PreAuthorize("hasRole('ADMIN')")
    @PostMapping("/create")
    public User create(@RequestBody UserRegister userRegister) {
        User user = userRegisterService.registerUser(userRegister);
        userService.createNFTAccount(user.getId());
        user.setPassword(null);
        return user;
    }


    @PostMapping("/save")
    public User save(@RequestBody User user) {
        //过滤敏感词
        user.setNickname(SensitiveFilterUtil.sensitiveReplace(user.getNickname()));
        //过滤敏感词
        user.setIntro(SensitiveFilterUtil.sensitiveReplace(user.getIntro()));
        if (user.getId() != null) {
            user = userService.update(user);
        } else {
            userRepo.save(user);
        }
        user.setPassword(null);
        return user;
    }

    @GetMapping("/my")
    public User my() {
        User user = userRepo.findById(SecurityUtils.getAuthenticatedUser().getId()).orElseThrow(new BusinessException("用户不存在"));
        user.setPassword(null);
        user.setTradeCode(null);
        return user;

    }

    @PostMapping("/preEmptionPrivilege")
    public UserDTO preEmptionPrivilege(@RequestParam Integer projectId) {
        User user = currentUser();
        if (user == null) return null;
        List<UserWareHouse> userWareHouses = userWareHouseRepo.findUserIdItem(user.getId(),projectId);
        int count = 0;//白名单优先购次数
        for (UserWareHouse userWareHouse : userWareHouses) {
            if (userWareHouse.getAssetId() == null || userWareHouse.getAssetId() == 0) {
                LocalDateTime overdueTime  = userWareHouse.getOverdueTime();
                //判断白名单是否过期了
                if (overdueTime!=null && LocalDateTime.now().isAfter(overdueTime)){
                    userWareHouseRepo.softDelete(userWareHouse.getId());
                }else {
                    count += userWareHouse.getAdvancePurchaseCount();
                }
            }
        }
        UserDTO userDTO = userService.toDTO(user, false);
        if (count > 0) {
            userDTO.setPreEmptionPrivilege(new PreEmptionPrivilege(count, 0));//白名单
        }
        userDTO.setCount(count);
        user.setTradeCode(null);
        user.setPassword(null);
        user.setPhone(null);
        return  userDTO;
    }

    @GetMapping("/myAdmin")
    @PreAuthorize("hasRole('ADMIN')")
    public User myAdmin() {
        User user = userRepo.findById(SecurityUtils.getAuthenticatedUser().getId()).orElseThrow(new BusinessException("用户不存在"));
        user.setPassword(null);
        return user;
    }

//    @PreAuthorize("hasRole('ADMIN')")
    @PostMapping("/all")
    public Page<UserDTO> all(@RequestBody PageQuery pageQuery) {
        if (!(SecurityUtils.getAuthenticatedUser() != null && SecurityUtils.getAuthenticatedUser().isAdmin())) {
            pageQuery.getQuery().put("hasRole", "ROLE_MINTER");
        }
        return userService.toDTO(userService.all(pageQuery));
    }

    //    @PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/get/{id}")
    public UserDTO get(@PathVariable Long id) {
        UserDTO userDTO = userService.toDTO(userRepo.findById(id).orElseThrow(new BusinessException("无记录")), true);
        return userDTO;
    }

    @PreAuthorize("hasRole('ADMIN')")
    @PostMapping("/del/{id}")
    public void del(@PathVariable Long id) {
        userService.del(id);
    }

    @PostMapping("/excel")
    @ResponseBody
    public void excel(HttpServletResponse response, @RequestBody PageQuery pageQuery) throws IOException {
        List<User> data = userService.all(pageQuery).getContent();
        ExcelUtils.export(response, data);
    }

    @PostMapping("/getMaUserInfo")
    @ApiOperation(value = "获取小程序用户信息")
    public User getMaUserInfo(String sessionKey, String rawData, String signature, String encryptedData, String iv) {
        User user = userService.getMaUserInfo(sessionKey, rawData, signature, encryptedData, iv);
        if (user != null) {
            user.setPassword(null);
            return user;
        }
        throw new BusinessException("获取用户信息失败");
    }

    @PostMapping("/code2openId")
    @ApiOperation(value = "获取OpenId")
    public String code2openId(@RequestParam String code) throws WxErrorException {
        return userService.code2openId(code);
    }

    @PreAuthorize("hasRole('ADMIN')")
    @PostMapping("/setPasswordAdmin")
    public String setPasswordAdmin(@RequestParam Long userId, @RequestParam String password) {
        return userService.setPassword(userId, password);
    }

    @PostMapping("/changePassword")
    @ApiOperation("修改密码")
    public String changePassword(@RequestParam String password, @RequestParam String code) {
        return userService.setPassword(SecurityUtils.getAuthenticatedUser().getId(), code, password);
    }

    @PostMapping("/forgotPassword")
    @ApiOperation("忘记密码")
    public String forgotPassword(@RequestParam String phone, @RequestParam String password, @RequestParam String code) {
        return userService.forgotPassword(phone, password, code);
    }

    @PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/getToken/{userId}")
    public String getToken(@PathVariable Long userId) {
        return jwtTokenUtil.generateToken(JwtUserFactory.create(userRepo.findById(userId)
                .orElseThrow(new BusinessException("用户不存在"))));
    }

    @PostMapping("/bindPhone")
    public void bindPhone(@RequestParam String phone) {
//        userService.bindPhone(SecurityUtils.getAuthenticatedUser().getId(), phone);
        throw new BusinessException("很抱歉,当前不支持该操作。");

    }

    @GetMapping("/{id}/follow")
    public void follow(@PathVariable Long id) {
        followService.follow(SecurityUtils.getAuthenticatedUser().getId(), id);
    }

    @GetMapping("/{id}/unfollow")
    public void unfollow(@PathVariable Long id) {
        followService.unfollow(SecurityUtils.getAuthenticatedUser().getId(), id);
    }

    @GetMapping("/myFollows")
    @ApiOperation("我的关注")
    public List<UserDTO> myFollows() {
        return userService.toDTO(userRepo.userFollows(SecurityUtils.getAuthenticatedUser().getId()));
    }

    @GetMapping("/myFollowers")
    @ApiOperation("关注我的")
    public List<UserDTO> myFollowers() {
        return userService.toDTO(userRepo.userFollowers(SecurityUtils.getAuthenticatedUser().getId()));
    }

    @PostMapping("/setTradeCode")
    @ApiOperation("修改交易密码")
    public void setTradeCode(@RequestParam String token, @RequestParam String tradeCode) {
        userService.setTradeCode(SecurityUtils.getAuthenticatedUser().getId(), token, tradeCode);
    }

    @PostMapping("/verifyTradeCode")
    @ApiOperation("验证交易密码")
    public void verifyTradeCode(@RequestParam String tradeCode) {
        userService.verifyTradeCode(SecurityUtils.getAuthenticatedUser().getId(), tradeCode);
    }

    @PostMapping("/searchByPhone")
    public Map<String, Object> searchByPhone(@RequestParam String phone) {
        return userService.searchByPhone(phone);
    }

    @PreAuthorize("hasAnyRole('ADMIN', 'OPERATOR')")
    @PostMapping("/searchByPhoneAdmin")
    public Map<String, Object> searchByPhoneAdmin(@RequestParam String phone) {
        return userService.searchByPhoneAdmin(phone);
    }

    @PreAuthorize("hasAnyRole('ADMIN', 'OPERATOR')")
    @PostMapping("/searchByIdAdmin")
    public Map<String, Object> searchByIdAdmin(@RequestParam String ids) {
        return userService.searchByIdAdmin(ids);
    }


    @GetMapping("/tradeCodeStatus")
    public Object tradeCodeStatus() {
        return new HashMap<String, Object>() {{
            put("set", StringUtils.isNotBlank(
                    userRepo.findById(SecurityUtils.getAuthenticatedUser().getId()).map(User::getTradeCode).orElse(null)
            ));
        }};
    }

    @GetMapping("/myBankCard")
    public List<UserBankCard> myBankCard() {
        //String payPlatform = uniformPayUtil.getDefaultPayPlatform();
        User user = currentUser();
        //限定查询当前支付平台关联的卡
        List<UserBankCard> dataList = userBankCardRepo.findByUserId(user.getId());

        //是否存在钱包
        UserFundAccount lianlianAccount = userFundAccountForLianlianpayService.getAccount(user.getId());
        if(lianlianAccount != null && lianlianAccount.getAccountStatus() == AccountStatus.NORMAL){
            dataList.forEach((item) -> {
                item.setHasWallet(true);
            });
        }

        return dataList;
    }

    /**
     * 新的绑卡发短信方法
     * @param phone
     * @param bankNo
     * @param password
     * @param randomKey
     * @return
     */
    @PostMapping("/addBankCardSendVerify")
    public JSONObject addBankCardSendVerify(@RequestParam String phone, @RequestParam String bankNo,
                                            String password, String randomKey) {
        User user = currentUser();
        if (!Pattern.matches(Constants.Regex.PHONE, phone)) {
            throw new BusinessException("请输入正确的手机号");
        }

        JSONObject result = new JSONObject();
        //兼容钱包绑卡申请
        UserFundAccount lianlianAccount = userFundAccountForLianlianpayService.getAccount(user.getId());
        if(lianlianAccount != null && lianlianAccount.getAccountStatus() == AccountStatus.NORMAL){
            if(StringUtils.isEmpty(password) || StringUtils.isEmpty(randomKey)){
                throw new BusinessException("用户已开通钱包，需要提供支付密码进行绑卡");
            }
            //钱包绑卡逻辑
            JSONObject res = lianlianPayService.bindCardApply(user.getId(), bankNo, phone, password, randomKey);
            String bindOrderNo = res.getString("txn_seqno");
            String token = res.getString("token");
            result.put("token", token);
            result.put("bindOrderNo", bindOrderNo);
        }else{
            String subject = smsService.sendVerify(phone);
            result.put("subject", subject);
        }

        return result;
    }


    @PostMapping("/addBankCard")
    public UserBankCard addBankCard(@RequestParam String bankNo, @RequestParam String phone,
                            @RequestParam String code, @RequestParam int projectId
                            ,String bindOrderNo, String token) throws Exception {
        Long userId = SecurityUtils.getAuthenticatedUser().getId();

        //卡号不能重复 查询有没有其他人绑定了IdentityAuthService
        if (userBankCardRepo.findByBankNo(bankNo, userId) >= sysConfigService.getInt(SysConstants.BANK_CARD_CAN_BE_BOUND)) {
            throw new BusinessException("银行卡已经被其他用户绑定");
        }

        Integer holdCount = userBankCardRepo.countByUserIdAndBankNo(userId, bankNo);
        if(holdCount > 0){
            throw new BusinessException("用户已绑定过该卡，请勿重复绑定");
        }

        UniformBindBank.UniformBindBankBuilder builder = UniformBindBank.builder();
        builder.userId(userId).bankNo(bankNo).phone(phone).smsCode(code)
        .bindOrderNo(bindOrderNo)
        .token(token);
        UniformBindBank bindBank = builder.build();

        //如果开通了钱包，走钱包绑卡逻辑
        UserFundAccount lianlianAccount = userFundAccountForLianlianpayService.getAccount(userId);
        if(lianlianAccount != null && lianlianAccount.getAccountStatus() == AccountStatus.NORMAL){
            bindBank.setPayPlatform(UniformPayConstants.LIANLIANPAY_PLATFORM);
        }

        UserBankCard userBankCard = uniformPayUtil.addBankCard(bindBank);

        activityInviteService.risingStar(userId, bankNo ,InviteeQualificationsType.BIND_BANK_CARD);
        return userBankCard;
    }


    @PostMapping("/removeBankCardSendVerify")
    public String removeBankCardSendVerify() {
        User user = SecurityUtils.getAuthenticatedUser();
        List<UserBankCard> cardList = userBankCardRepo.findByUserId(user.getId());
        if (cardList.size() == 0) {
            throw new BusinessException("你没有绑定银行卡");
        }
        String phone = cardList.get(0).getPhone();
        if (!Pattern.matches(Constants.Regex.PHONE, phone)) {
            throw new BusinessException("请输入正确的手机号");
        }

        if (assetRepo.findAllByUserIdAndStatus(user.getId(), AssetStatus.TRADING).size() > 0) {
            throw new BusinessException("有藏品正在交易中，请稍后在试");
        }
        if (assetRepo.findAllByUserIdAndStatus(user.getId(), AssetStatus.GIFTING).size() > 0) {
            throw new BusinessException("有藏品正在赠送中，请稍后在试");
        }
        List<Asset> assets = assetRepo.findAllByUserIdAndStatus(user.getId(), AssetStatus.NORMAL);
        if (assets.size() > 0 && assets.stream().anyMatch(Asset::isConsignment)) {
            throw new BusinessException("有藏品正在寄售中，请先取消寄售");
        }

        UserFundAccount lianlianAccount = userFundAccountForLianlianpayService.getAccount(user.getId());
        if(lianlianAccount != null && lianlianAccount.getAccountStatus() == AccountStatus.NORMAL) {
            throw new BusinessException("用户已开通钱包，只允许换绑银行卡");
        }

        return smsService.sendVerify(phone);
    }

    @PostMapping("/removeBankCard")
    public void removeBankCard(@RequestParam String code, @RequestParam String password, @RequestParam Integer projectId) throws Exception {
        User user = SecurityUtils.getAuthenticatedUser();
        UserFundAccount lianlianAccount = userFundAccountForLianlianpayService.getAccount(user.getId());
        if(lianlianAccount != null && lianlianAccount.getAccountStatus() == AccountStatus.NORMAL) {
            throw new BusinessException("用户已开通钱包，只允许换绑银行卡");
        }

        userService.verifyTradeCode(user.getId(), password);//验证交易密码

        List<UserBankCard> cardList = userBankCardRepo.findByUserId(user.getId());
        if (cardList.size() == 0) {
            throw new BusinessException("你没有绑定银行卡");
        }
        if (assetRepo.findAllByUserIdAndStatus(user.getId(), AssetStatus.TRADING).size() > 0) {
            throw new BusinessException("有藏品正在交易中，请稍后在试");
        }
        if (assetRepo.findAllByUserIdAndStatus(user.getId(), AssetStatus.GIFTING).size() > 0) {
            throw new BusinessException("有藏品正在赠送中，请稍后在试");
        }
        List<Asset> assets = assetRepo.findAllByUserIdAndStatus(user.getId(), AssetStatus.NORMAL);
        if (assets.size() > 0 && assets.stream().anyMatch(Asset::isConsignment)) {
            throw new BusinessException("有藏品正在寄售中，请先取消寄售");
        }

        String phone = cardList.get(0).getPhone();
        aliSmsService.verify(phone, code);//验证交易手机验证码
        uniformPayUtil.removeBankCard(user.getId());
    }

    @PostMapping("/removeBankCardAdmin")
    @PreAuthorize("hasAnyRole('ADMIN')")
    public void removeBankCardAdmin(@RequestParam Long userId) throws Exception {
        UserFundAccount lianlianAccount = userFundAccountForLianlianpayService.getAccount(userId);
        if(lianlianAccount != null && lianlianAccount.getAccountStatus() == AccountStatus.NORMAL) {
            throw new BusinessException("用户已开通钱包，只允许换绑银行卡");
        }

        uniformPayUtil.removeBankCard(userId);
    }

    @PostMapping("/removeAuthAdmin")
    @PreAuthorize("hasAnyRole('ADMIN')")
    public void removeAuthAdmin(@RequestParam Long userId) {
        userService.removeAuth(userId);
    }

    @PreAuthorize("hasAnyRole('ADMIN')")
    @PostMapping("/batchRegister")
    public Map<String, Object> batchRegister(@RequestParam String phones, @RequestParam String defaultPassword) {
        Map<String, Object> resultMap = userRegisterService.batchRegister(phones, defaultPassword);
        return resultMap;
    }

    @PreAuthorize("hasAnyRole('ADMIN')")
    @PostMapping("/exportInvite")
    @ResponseBody
    public void exportInvite(HttpServletResponse response, @RequestBody PageQuery pageQuery) throws IOException {
        List<InvitePhoneDTO> data = userService.all(pageQuery)
                .map(InvitePhoneDTO::new)
                .getContent();
        ExcelUtils.export(response, data);
    }


    @PreAuthorize("hasAnyRole('ADMIN')")
    @GetMapping("/checkSettleAccount")
    public String checkSettleAccount() {
        userService.checkSettleAccountAsync();
        return "ok";
    }

    /**
     * 绑定的资金账户
     *
     * @return
     */
    @GetMapping("/myFundAccount")
    public List<UserFundAccount> myFundAccount() {
        Long userId = SecurityUtils.getAuthenticatedUser().getId();
        return userFundAccountRepo.findByUserId(userId);
    }

    @PostMapping("/addFundAccount")
    public UserFundAccount addFundAccount(@RequestBody UserFundAccountDTO userFundAccountDTO) {
        Long userId = SecurityUtils.getAuthenticatedUser().getId();
        UserFundAccount userFundAccount = userFundAccountService.addAlipayAccount(userId, userFundAccountDTO);
        return userFundAccount;
    }

    @PostMapping("/removeFundAccount")
    public void removeFundAccount(@RequestParam Long accountId) {
        Long userId = SecurityUtils.getAuthenticatedUser().getId();
        userFundAccountService.removeAccount(userId, accountId);
    }

    @PostMapping("/getUserInviteData")
    public Map<String, Object> getUserInviteData(@RequestParam int projectId) {
        Long userId = SecurityUtils.getAuthenticatedUser().getId();
        Map<String, Object> result = new HashMap<>();

        ActivityInvite activityInvite = activityInviteRepo.findRecent();
        if (activityInvite == null) {
            return result;
        }
        List<User> users = userRepo.getUserInviteData(userId, activityInvite.getInviteStartdatetime());
        List<InviteDataDTO> list1 = new ArrayList<>();
        List<InviteDataDTO> list2 = new ArrayList<>();
        List<InviteDataDTO> list3 = new ArrayList<>();
        users.forEach(user -> {
            InviteDataDTO inviteDataDTO = new InviteDataDTO();
            inviteDataDTO.phone = user.getPhone() == null ? "" : user.getPhone().substring(0, 3) + "****" + user.getPhone().substring(7);
            inviteDataDTO.authStatus = AuthStatus.SUCCESS.equals(user.getAuthStatus());//是否实名认证
            if (activityInvite.getInviteeQualifications()==0){
                //是否开通钱包
                inviteDataDTO.bankCard = userService.whetherCreatewallet(user.getId());
            }else {
                //是否绑卡
                inviteDataDTO.bankCard = uniformPayUtil.isBindBankCard(user.getId());
            }
            if (inviteDataDTO.authStatus && inviteDataDTO.bankCard) {
                list1.add(inviteDataDTO);
            } else if (inviteDataDTO.authStatus) {
                list2.add(inviteDataDTO);
            } else {
                list3.add(inviteDataDTO);
            }
        });
        list1.addAll(list2);
        list1.addAll(list3);
        //邀请数量
        Integer inviteNum = inviteDateRepo.findinviterUserIdCount(activityInvite.getId(), userId, InvitationNote.SUCCESSFULLY.ordinal());
        //邀请新人空投数量
        Integer airDropAwardCount = inviteDateRepo.findinviterAirDropAward(activityInvite.getId(), userId);
        result.put("inviteData", list1);
        result.put("airdropcount", airDropAwardCount);
        result.put("inviteNum", inviteNum);
        return result;
    }


    @PreAuthorize("hasAnyRole('ADMIN')")
    @PostMapping("/pushMsg")
    public IPushResult pushMsg(@RequestParam Map<String,Object> paramMap) {
        String clientId = Objects.toString(paramMap.get("clientId"),"");
        String userIdStr = Objects.toString(paramMap.get("userId"), "");
        if(StringUtils.isNotBlank(userIdStr)){
            Long userId = Long.valueOf(userIdStr);
            User user = userRepo.findById(userId).orElseThrow(new BusinessException("指定的用户未找到"));
            if(StringUtils.isEmpty(user.getAppClientId())){
                throw  new BusinessException("用户没有关联App客户端ID,无法发送");
            }
            //指定用户的为准
            clientId = user.getAppClientId();
        }

        String pushAll = Objects.toString(paramMap.get("pushAll"), "");

        String title = Objects.toString(paramMap.get("title"),"");
        String content = Objects.toString(paramMap.get("content"),"");

        if(!"true".equals(pushAll) && StringUtils.isEmpty(clientId)){
            throw new BusinessException("推送单个用户需要指定客户端ID");
        }

        PushMsgDTO pushMsgDTO = new PushMsgDTO();
        pushMsgDTO.setClientId(clientId);
        pushMsgDTO.setTitle(title);
        pushMsgDTO.setContent(content);
        IPushResult pushResult = pushService.pushNotification(pushMsgDTO);

        return pushResult;
    }


}


