package com.fzm.mall.server.admin.home_page.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fzm.mall.server.admin.auth_center.enums.type.AdminRoleTypeEnum;
import com.fzm.mall.server.admin.auth_center.service.IAdminRoleService;
import com.fzm.mall.server.admin.block_chain.nft.NftEvm;
import com.fzm.mall.server.admin.chain.FzmChain;
import com.fzm.mall.server.admin.config.Redis;
import com.fzm.mall.server.admin.constant.MALLGlobalConfig;
import com.fzm.mall.server.admin.constant.MallResponseError;
import com.fzm.mall.server.admin.enums.response.MallResponseEnum;
import com.fzm.mall.server.admin.general_model.service.IPlatformSettingService;
import com.fzm.mall.server.admin.home_page.enums.status.AdminStatusEnum;
import com.fzm.mall.server.admin.home_page.enums.status.IsDeleteEnum;
import com.fzm.mall.server.admin.exception.MyException;
import com.fzm.mall.server.admin.home_page.dao.MerchantMapper;
import com.fzm.mall.server.admin.home_page.entity.dto.*;
import com.fzm.mall.server.admin.home_page.entity.vo.MerchantVO;
import com.fzm.mall.server.admin.home_page.enums.status.MerchantRegisterEnum;
import com.fzm.mall.server.admin.home_page.enums.type.AdminTypeEnum;
import com.fzm.mall.server.admin.home_page.model.Admin;
import com.fzm.mall.server.admin.home_page.model.Merchant;
import com.fzm.mall.server.admin.home_page.service.IAdminService;
import com.fzm.mall.server.admin.home_page.service.IMerchantService;
import com.fzm.mall.server.admin.system.model.SysConfig;
import com.fzm.mall.server.admin.system.service.ISysConfigService;
import com.fzm.mall.server.admin.util.*;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.dozer.DozerBeanMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author wangp
 * @date 2021/3/2 13:42
 * @description 服务实现类
 * @since JDK 1.8
 */
@Service
@Transactional
public class MerchantServiceImpl extends ServiceImpl<MerchantMapper, Merchant> implements IMerchantService {

    @Autowired
    private DozerBeanMapper beanMapper;
    @Autowired
    private IAdminService adminService;
    @Autowired
    private Redis redis;
    @Autowired
    private FzmChain fzmChain;
    @Autowired
    private IAdminRoleService adminRoleService;
    @Autowired
    private ISysConfigService sysConfigService;
    @Autowired
    private IPlatformSettingService platformSettingService;

    @Override
    public String merchantRegister(RegisterDTO registerDTO) {
        String merchantId = UUIDUtil.getUUID();

        //发行地址、回收地址、发行地址私钥
        String issueAddr = fzmChain.accountCreate(merchantId + MALLGlobalConfig.ADDR_ISSUE_SUFFIX);
        String recoverAddr = fzmChain.accountCreate(merchantId + MALLGlobalConfig.ADDR_RECOVER_SUFFIX);
        String externAddr = fzmChain.accountCreate(merchantId + MALLGlobalConfig.ADDR_EXTERN_SUFFIX);
        String nftAddr = fzmChain.accountCreate(merchantId + MALLGlobalConfig.ADDR_NFT_SUFFIX);
        String priKey = fzmChain.addressPriKey(issueAddr);

        QRCodeUtil.encode(issueAddr, System.getProperty("user.dir") +
                File.separator + "qr_url" +
                File.separator + merchantId + ".png");

        Merchant merchant = new Merchant();
        merchant.setMerchantId(merchantId);
        merchant.setRegisterPhone(registerDTO.getPhone());
        merchant.setTokenIssueAddr(issueAddr);
        merchant.setTokenRecoverAddr(recoverAddr);
        merchant.setExternalTokenAddr(externAddr);
        merchant.setNftAddr(nftAddr);
        merchant.setIssueAddrKey(priKey);
        merchant.setStatus(MerchantRegisterEnum.WAIT_IMPROVE.getStatus());
        merchant.setPrefix(RandomUtil.generateByShuffle());
        boolean save = save(merchant);

        if (!save)
            throw new MyException(MallResponseError.SAVE_ERROR);
        return merchantId;
    }

    @Override
    public String merchantImproveInfo(MerchantDTO merchantDTO) {
        Merchant merchant = beanMapper.map(merchantDTO, Merchant.class);
        merchant.setStatus(MerchantRegisterEnum.EXAMINE.getStatus());
        LambdaUpdateWrapper<Merchant> uw = new LambdaUpdateWrapper<>();
        uw.eq(Merchant::getMerchantId, merchant.getMerchantId());
        if (!update(merchant, uw)) {
            return MallResponseError.UPDATE_ERROR;
        }
        //TODO 注册成功后 初始化管理员账号并发送短信 暂时默认 延迟队列
        /*Merchant byMerchantId = getByMerchantId(merchantDTO.getMerchantId());
        AdminDTO dto = new AdminDTO();
        dto.setMerchantId(byMerchantId.getMerchantId());
        dto.setAccount(byMerchantId.getRegisterPhone());
        dto.setPassword(Sha256Util.encode("123456"));
        dto.setType(2);
        adminService.add(dto);
        SmsUtil.sendSms(byMerchantId.getRegisterPhone(), true, "89a8aa68d2514042b2ee6c3c66e6efc2",
                "123456");*/
        return MallResponseEnum.SUCCESS.code();
    }

    @Override
    public void merchantImproveInfo2(MerchantDTO merchantDTO) {
        Merchant merchant = beanMapper.map(merchantDTO, Merchant.class);
        merchant.setStatus(MerchantRegisterEnum.EXAMINE.getStatus());
        LambdaUpdateWrapper<Merchant> uw = new LambdaUpdateWrapper<>();
        uw.eq(Merchant::getRegisterPhone, merchant.getRegisterPhone());
//        uw.eq(Merchant::getStatus, MerchantRegisterEnum.WAIT_IMPROVE.getStatus());
        uw.eq(Merchant::getDel, IsDeleteEnum.NO.getStatus());
        if (!update(merchant, uw)) {
            throw new MyException(MallResponseError.UPDATE_ERROR);
        }
    }

    //商户申请通过后，系统短信通知商户，并告知商户初始登录密码，商户登录后，系统判断密码如果是初始密码的话，强制商户修改密码后，才可以登录
    @Override
    @Transactional
    public void registerPay(String phone) {
        Merchant merchant = getByRegisterPhone(phone);
        merchant.setStatus(MerchantRegisterEnum.WAIT_SET_PWD.getStatus());
        updateById(merchant);

        SysConfig headImg = sysConfigService.getOne(new QueryWrapper<SysConfig>().lambda().eq(SysConfig::getDictKey, "admin_head_img"));

        Admin admin = new Admin(
                merchant.getMerchantId(),
                adminRoleService.getByType(AdminRoleTypeEnum.MERCHANT_DEFAULT.getType()).getRoleId(),
                headImg.getDictValue(),
                phone,
                Sha256Util.encode(MALLGlobalConfig.INIT_PASSWORD),
                "超级管理员",
                AdminTypeEnum.MERCHANT.getType(),
                AdminStatusEnum.NORMAL.getStatus()
        );
        adminService.initAdmin(admin);

        /*//TODO 短信验证模板
        SmsUtil.sendSms(phone, true, "89a8aa68d2514042b2ee6c3c66e6efc2",
                "123456");*/

        /*JSONObject json = new JSONObject();
        json.put("outTradeNo", OrderUtil.getOid());
        json.put("subject", "商户入驻缴费");
        json.put("totalAmount", "100");

        String res = HttpUtils.apacheHttpClient4DoPost("http://localhost:8089/mallpay/alipay/pagePay", json, null);
        JSONObject jsonObject = JSONObject.parseObject(res);
        JSONObject data = jsonObject.getObject("data", JSONObject.class);

        //todo 状态更新 默认管理员 流水记录 短信模板
        return data.getString("form");*/
    }

    @Override
    public boolean checkPhoneExist(String phone) {
        QueryWrapper<Merchant> qw = new QueryWrapper<>();
        qw.lambda()
                .notIn(Merchant::getStatus, MerchantRegisterEnum.getExistValueList())
                .eq(Merchant::getDel, IsDeleteEnum.NO.getStatus())
                .eq(Merchant::getRegisterPhone, phone);
        return getOne(qw) != null;
    }

    @Override
    public Merchant getByRegisterPhone(String phone) {
        QueryWrapper<Merchant> qw = new QueryWrapper<>();
        qw.lambda()
                .eq(Merchant::getRegisterPhone, phone)
                .eq(Merchant::getDel, IsDeleteEnum.NO.getStatus());
        return getOne(qw);
    }

    @Override
    public Merchant getByMerchantId(String merchantId) {
        QueryWrapper<Merchant> qw = new QueryWrapper<>();
        qw.lambda()
                .eq(Merchant::getMerchantId, merchantId)
                .eq(Merchant::getDel, IsDeleteEnum.NO.getStatus());
        return getOne(qw);
    }

    @Override
    public IPage<MerchantVO> page(String merchantId, String account, String linkMan, Integer type, Integer pageNum, Integer pageSize) {
        IPage<MerchantVO> page = new Page<>(pageNum, pageSize);
        IPage<MerchantVO> iPage = baseMapper.page(page, merchantId, account, linkMan, type);

        List<MerchantVO> records = iPage.getRecords().stream()
                .peek(v -> v.setIdCard(hideIdCard(v.getIdCard())))
                .collect(Collectors.toList());
        iPage.setRecords(records);

        //TODO member ship , register area
        return iPage;
    }

    @Override
    public void lock(MerchantLockDTO lockDTO) {
        String merchantId = lockDTO.getMerchantId();
        Integer lock = lockDTO.getLock();
        String suffix = MALLGlobalConfig.MALL_TOKEN_REDIS_KEY_SUFFIX;

        //商户锁定或解锁
        UpdateWrapper<Merchant> uw = new UpdateWrapper<>();
        uw.lambda()
                .set(Merchant::getStatus, lock)
                .eq(Merchant::getMerchantId, merchantId);
        if (!update(uw)) {
            throw new MyException(MallResponseError.UPDATE_ERROR);
        }

        //清除商户下辖所有管理员token,立即踢下线
        if (lock.equals(MerchantRegisterEnum.LOCKING.getStatus())) {
            List<Admin> admins = adminService.getByMerchantId(merchantId);
            admins.forEach(v -> redis.del(v.getAdminId() + suffix));
        }
    }

    @Override
    public void check(MerchantCheckDTO checkDTO) {
        String merchantId = checkDTO.getMerchantId();
        Integer check = checkDTO.getCheck();

        //商户审核
        String delayDelivery = platformSettingService.queryByEvent("user_delay_delivery");

        File file = new File(System.getProperty("user.dir") + File.separator + "qr_url" + File.separator + merchantId + ".png");
//        ClassPathResource resource = new ClassPathResource("qr_url" + File.separator + merchantId + ".png");
//        ClassPathResource resource = new ClassPathResource("qr_url/25d8908b76f14e83ac4e416fe5758693.png");
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(file);
        } catch (IOException e) {
            e.printStackTrace();
        }
        String logoPath = OBSUtil.uploadStream(inputStream, "manage/img/" + SerialIdsUtil.createDateStr() + "/", SerialIdsUtil.createFileName(".png"));

        UpdateWrapper<Merchant> uw = new UpdateWrapper<>();
        uw.lambda()
                .set(Merchant::getStatus, check)
                .set(Merchant::getDelayDelivery, Integer.valueOf(delayDelivery))
                .set(Merchant::getReason, checkDTO.getReason())
                .set(Merchant::getQrUrl, logoPath)
                .eq(Merchant::getMerchantId, merchantId);
        boolean update = update(uw);

        if (!update) {
            throw new MyException(MallResponseError.UPDATE_ERROR);
        }

        String registerPhone = getByMerchantId(merchantId).getRegisterPhone();
        //TODO 失败发送短信告知
        /*if (checkDTO.getCheck().equals(MerchantRegisterEnum.EXAMINE_FAIL.getStatus())) {
            AliSmsUtils.sendCode(registerPhone, checkDTO.getReason(), AliSmsUtils.MERCHANT_REGISTER_CHECK_FAIL, true);
        } else {
            AliSmsUtils.sendCode(registerPhone, checkDTO.getReason(), AliSmsUtils.MERCHANT_REGISTER_CHECK_SUCCESS, true);
        }*/

    }

    @Override
    public String getIdCard(String registerPhone) {
        Integer examineFail = MerchantRegisterEnum.EXAMINE_FAIL.getStatus();
        Integer payFail = MerchantRegisterEnum.PAY_FAIL.getStatus();
        QueryWrapper<Merchant> qw = new QueryWrapper<>();
        qw.lambda()
                .eq(Merchant::getRegisterPhone, registerPhone)
                .eq(Merchant::getDel, IsDeleteEnum.NO.getStatus())
                .ne(Merchant::getStatus, examineFail)
                .ne(Merchant::getStatus, payFail);
        return getOne(qw).getIdCard();
    }

    public String hideIdCard(String idCard) {
        if (!StringUtils.isBlank(idCard)) {
            String sub1 = idCard.substring(0, 3);
            String sub2 = idCard.substring(14, 18);
            return sub1 + "***********" + sub2;
        }
        return "";
    }

    @Override
    public void lastLoginTime(String merchantId, Long time) {
        UpdateWrapper<Merchant> uw = new UpdateWrapper<>();
        uw.lambda()
                .set(Merchant::getLastLogin, time)
                .eq(Merchant::getMerchantId, merchantId);
        if (!update(uw)) {
            throw new MyException(MallResponseError.UPDATE_ERROR);
        }
    }

    @Override
    public String getAllMerchantLinkPhone() {
        QueryWrapper<Merchant> qw = new QueryWrapper<>();
        qw.lambda().eq(Merchant::getStatus, MerchantRegisterEnum.NORMAL.getStatus())
                .eq(Merchant::getDel, IsDeleteEnum.NO.getStatus());

        JSONArray arr = new JSONArray();
        list(qw).forEach(v -> arr.add(v.getApplyPhone()));
        return arr.toJSONString();
    }

    @Override
    public void initPlatformMerchant(Merchant merchant) {
        String merchantId = merchant.getMerchantId();
        if (getByMerchantId(merchantId) != null)
            return;
        //发行地址、回收地址、发行地址私钥
        String issueAddr = fzmChain.accountCreate(merchantId + MALLGlobalConfig.ADDR_ISSUE_SUFFIX);
        String recoverAddr = fzmChain.accountCreate(merchantId + MALLGlobalConfig.ADDR_RECOVER_SUFFIX);
        String externAddr = fzmChain.accountCreate(merchantId + MALLGlobalConfig.ADDR_EXTERN_SUFFIX);
        String nftAddr = fzmChain.accountCreate(merchantId + MALLGlobalConfig.ADDR_NFT_SUFFIX);
        String priKey = fzmChain.addressPriKey(issueAddr);
        QRCodeUtil.encode(issueAddr, System.getProperty("user.dir") +
                File.separator + "qr_url" +
                File.separator + merchantId + ".png");

        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start <= 5000) ;

        File file = new File(System.getProperty("user.dir") + File.separator + "qr_url" + File.separator + merchantId + ".png");
//        ClassPathResource resource = new ClassPathResource("qr_url" + File.separator + merchantId + ".png");
//        ClassPathResource resource = new ClassPathResource("qr_url/25d8908b76f14e83ac4e416fe5758693.png");
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(file);
        } catch (IOException e) {
            e.printStackTrace();
        }
        String logoPath = OBSUtil.uploadStream(inputStream, "manage/img/" + SerialIdsUtil.createDateStr() + "/", SerialIdsUtil.createFileName(".png"));

        merchant.setTokenIssueAddr(issueAddr);
        merchant.setTokenRecoverAddr(recoverAddr);
        merchant.setExternalTokenAddr(externAddr);
        merchant.setNftAddr(nftAddr);
        merchant.setIssueAddrKey(priKey);
        merchant.setQrUrl(logoPath);
        save(merchant);
    }

    @Override
    public void delayDelivery(Integer delayDelivery, String merchantId) {
        LambdaUpdateWrapper<Merchant> uw = new LambdaUpdateWrapper<>();
        uw.set(Merchant::getDelayDelivery, delayDelivery);
        uw.eq(Merchant::getMerchantId, merchantId);
        update(uw);
    }

    @Override
    public void updateStatusByAccount(String account, Integer status) {
        LambdaUpdateWrapper<Merchant> uw = new LambdaUpdateWrapper<>();
        uw.set(Merchant::getStatus, status);
        uw.eq(Merchant::getRegisterPhone, account);
        update(uw);
    }

    @Override
    public Merchant queryByAddress(String address) {
        LambdaQueryWrapper<Merchant> qw = new LambdaQueryWrapper<>();
        qw.eq(Merchant::getTokenIssueAddr, address);
        return getOne(qw);
    }
}
