package com.sunyard.ssp.modules.user.controller;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.sunyard.chsm.utils.IpUtils;
import com.sunyard.chsm.utils.JsonUtils;
import com.sunyard.ssp.common.Result;
import com.sunyard.ssp.constv.Alg;
import com.sunyard.ssp.modules.monitor.log.entity.AuditLog;
import com.sunyard.ssp.modules.monitor.log.service.IAuditLogService;
import com.sunyard.ssp.modules.sdk.SdkApiService;
import com.sunyard.ssp.modules.sysconf.paramconf.service.IParamConfService;
import com.sunyard.ssp.modules.user.entity.ScRole;
import com.sunyard.ssp.modules.user.entity.ScUser;
import com.sunyard.ssp.modules.user.entity.ULoginDto;
import com.sunyard.ssp.modules.user.entity.UkeyLoginParm;
import com.sunyard.ssp.modules.user.service.IScUserRoleService;
import com.sunyard.ssp.modules.user.service.IScUserService;
import com.sunyard.ssp.util.BytesUtil;
import com.sunyard.ssp.utils.ResultUtil;
import com.sunyard.ssp.utils.SecurityUtil;
import com.sunyard.ssp.utils.UserUtil;
import com.sunyard.ssp.utils.sm2.SM2Util;
import com.sunyard.ssp.vo.UPublicKeyVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * <p>
 * U盾认证管理
 * </p>
 *
 * @author wp
 * @since 2021-08-24
 */
@RestController
@Slf4j
@RequestMapping("/ushield")
@Api(description = "U盾认证管理接口", tags = "U盾")
@Transactional
public class ScUShieldController {


    @Autowired
    private IScUserRoleService iScUserRoleService;

    @Autowired
    private IScUserService userService;

    @Autowired
    private SecurityUtil securityUtil;

    @Autowired
    private IParamConfService iParamConfService;
    @Autowired
    private SdkApiService sdkApiService;
    @Autowired
    private IAuditLogService auditLogService;

    @RequestMapping(value = "/ulogin", method = RequestMethod.POST)
    @ResponseBody
    @ApiOperation(value = "U盾登录接口")
    public Result<Object> ulogin(@RequestBody List<ULoginDto> uLoginDto, HttpServletRequest request) throws JsonProcessingException {

        if (uLoginDto.isEmpty()) {
            return new ResultUtil<Object>().setErrorMsg("参数不能为空");
        }
        AuditLog auditLog = new AuditLog();
        ULoginDto uLoginDto1 = uLoginDto.get(0);
        ScUser sysUser = userService.getById(uLoginDto1.getUserId());
        auditLog.setCreateTime(LocalDateTime.now());
        if (sysUser != null) {
            auditLog.setOperatorName(sysUser.getRealname());
            auditLog.setOperatorId(sysUser.getId());
        }
        auditLog.setOperateContent("ukey登录");
        auditLog.setOperateType("登录");
        List list = new ArrayList();
        for (ULoginDto loginDto : uLoginDto) {
            UkeyLoginParm ukeyLoginParm = new UkeyLoginParm(loginDto.getRa(), loginDto.getRb(), loginDto.getSignInfo(), loginDto.getUserId(), loginDto.getSignData(), loginDto.getRa() + loginDto.getRb() + loginDto.getSignData());
            list.add(ukeyLoginParm);
        }
        auditLog.setRequestParam(JsonUtils.toJsonString(list));
        //Ip信息
        auditLog.setIpAddress(IpUtils.getIpAddress(request));
        auditLog.setIpInfo("未知");

        //请求相关
        auditLog.setRequestMothed(request.getMethod());
        auditLog.setRequestPath(request.getRequestURI());

        //判断当前启用登陆模式
        Map map = iParamConfService.selectByItem(4);
        if (CollectionUtil.isNotEmpty(map)) {
            if (map.containsKey("loginMethod")) {
                String loginMethod = String.valueOf(map.get("loginMethod"));
                if (!"2".equals(loginMethod)) {
                    auditLog.setOperateResult("失败");
                    auditLogService.save(auditLog);
                    return new ResultUtil<Object>().setErrorMsg("当前不支持U盾登陆模式");
                }
            }
        }

        String uname = null;
        for (int i = 0; i < uLoginDto.size(); i++) {

            ULoginDto loginDto = uLoginDto.get(i);
            String CertA = loginDto.getSignInfo();
            Long userId = loginDto.getUserId();
            String ra = loginDto.getRa();
            String rb = loginDto.getRb();
            String signData = loginDto.getSignData();

            try {
                UserUtil.verifyUfield(CertA, userId);
            } catch (Exception e) {
                auditLog.setOperateResult("失败");
                auditLogService.save(auditLog);
                return new ResultUtil<Object>().setErrorMsg(e.getMessage());
            }

            //查询账户绑定U盾公钥
            ScUser user = userService.getById(userId);
            if (uname == null) {
                uname = user.getUsername();
            }
            //查询用户角色id集合
            List<ScRole> roles = iScUserRoleService.findByUserId(userId);
            List<Long> roldIds = new ArrayList<>();
            if (null != roles && roles.size() > 0) {
                roldIds = roles.stream().map(ScRole::getId).collect(Collectors.toList());
            }
            //排序
            Long[] ids = roldIds.toArray(new Long[roldIds.size()]);
            Arrays.sort(ids);
            //拼接原始数据
            StringBuilder sb = new StringBuilder();
            sb.append(userId);
            for (Long id : ids) {
                sb.append(id);
            }
            sb.append(user.getUPublickey());
            String orgData = sb.toString();


            // 验证智能密码钥匙公钥
            boolean verify = sdkApiService.PKCS1Verify(Alg.SM2, orgData.getBytes(), BytesUtil.hexString2Bytes(CertA));

            if (!verify) {
                auditLog.setOperateResult("失败");
                auditLogService.save(auditLog);
                return new ResultUtil<Object>().setErrorMsg("U盾登陆失败");
            }

            // 检查挑战数参数
            boolean checkRb = checkRb(rb);
            if (!checkRb ) {
                auditLog.setOperateResult("失败");
                auditLogService.save(auditLog);
                return new ResultUtil<Object>().setErrorMsg("挑战数 Rb 无效");
            }

            sb = new StringBuilder();
            sb.append(ra);
            sb.append(rb);
            sb.append(userId);
            for (Long id : ids) {
                sb.append(id);
            }
            sb.append(user.getUPublickey());
            orgData = sb.toString();

            // 验证 Token 签名
            boolean siginVerify = SM2Util.verifyByPk(orgData, user.getUPublickey(), signData);
            if (!siginVerify) {
                auditLog.setOperateResult("失败");
                auditLogService.save(auditLog);
                return new ResultUtil<Object>().setErrorMsg("第" + (i + 1) + "个ukey验签失败");
            }
        }
        //验签通过颁发token
        String accessToken = securityUtil.getToken(uname, false);
        //使用外部公钥加密token
//            byte[] bytes = sdkApiService.ASYMEncryptDataByOutPK(Alg.SM2,"CON.000000.ENDE.PK", accessToken);
//            return new ResultUtil<Object>().setData(BytesUtil.encodeBase64(bytes));
        auditLog.setOperateResult("成功");
        auditLogService.save(auditLog);
        return new ResultUtil<Object>().setData(accessToken);
    }



    private boolean checkRb(String rb) {
        log.info("rb:" + rb);
        if (rb == null || rb.length() != 32) {
            return false;
        }
        return true;
    }

    @RequestMapping(value = "/urandom", method = RequestMethod.GET)
    @ApiOperation(value = "返回随机数")
    public Result<String> uRandomNumber() {
//        PointerByReference phSessionHandle = new PointerByReference();
//        // 打开会话
//        int ret = CipherJna.INSTANCE.SDF_OpenSession(hDeviceHandle, phSessionHandle);
//        if (ret != 0) {
//            log.error("打开会话失败 错误码：{}", ret);
//            return new ResultUtil<String>().setErrorMsg("打开会话失败");
//        }
//        byte[] radom = new byte[16];
//        // 生成随机数
//        ret = CipherJna.INSTANCE.SDF_GenerateRandom(phSessionHandle.getValue(), 16, radom);
//        if (ret != 0){
//            log.error("生成随机数失败 错误码：{}", ret);
//        }
        String urandom = UUID.randomUUID().toString().replace("-", "").toUpperCase();
        return new ResultUtil<String>().setData(urandom);
    }

    @RequestMapping(value = "/bindUPublickey", method = RequestMethod.PUT)
    @ApiOperation(value = "绑定U盾公钥/获取签名信息/更换绑定U盾公钥")
    public Result<UPublicKeyVo> getUShieldInfo(@RequestParam("userId") @ApiParam(value = "用户id") Long userId,
                                               @RequestParam("uPublicKey") @ApiParam(value = "U盾公钥") String uPublicKey) {
        if (null == userId || StrUtil.isBlank(uPublicKey)) {
            return new ResultUtil<UPublicKeyVo>().setErrorMsg("缺少必需字段");
        }
        //判断用户是否绑定了U盾
        ScUser user = userService.getById(userId);
        if (null == user) {
            return new ResultUtil<UPublicKeyVo>().setErrorMsg("用户不存在");
        }
        if (StrUtil.isNotBlank(user.getUPublickey())) {
            return new ResultUtil<UPublicKeyVo>().setErrorMsg("用户已绑定U盾");
        }

        List<ScUser> list = userService.list(new QueryWrapper<ScUser>().lambda().eq(ScUser::getUPublickey, uPublicKey));
        if (CollectionUtil.isNotEmpty(list)) {
            return new ResultUtil<UPublicKeyVo>().setErrorMsg("该U盾已绑定过用户");
        }

        //查询用户角色id集合
        List<ScRole> roles = iScUserRoleService.findByUserId(userId);

        List<Long> roldIds = new ArrayList<>();
        if (null != roles && roles.size() > 0) {
            roldIds = roles.stream().map(ScRole::getId).collect(Collectors.toList());
        }

        //排序
        Long[] ids = roldIds.toArray(new Long[roldIds.size()]);
        Arrays.sort(ids);

        // 调用sdk 平台私钥对用户id+ids+uPublicKey 签名
        StringBuilder sb = new StringBuilder();
        sb.append(userId);
        for (Long id : ids) {
            sb.append(id);
        }
        sb.append(uPublicKey);
        String orgData = sb.toString();
        byte[] bytes = sdkApiService.PKCS1Sign(Alg.SM2, orgData.getBytes());

        UPublicKeyVo uPublicKeyVo = new UPublicKeyVo(userId, ids, uPublicKey, "", BytesUtil.bytes2HexString(bytes));

        //更新用户绑定U盾公钥
        user.setUPublickey(uPublicKey);
        boolean b = userService.updateById(user);
        if (!b) {
            return new ResultUtil<UPublicKeyVo>().setErrorMsg("用户绑定U盾失败");
        }
        return new ResultUtil<UPublicKeyVo>().setData(uPublicKeyVo);
    }


    @RequestMapping(value = "/init/bindUPublickey", method = RequestMethod.PUT)
    @ApiOperation(value = "绑定U盾公钥/获取签名信息/更换绑定U盾公钥")
    public Result<UPublicKeyVo> bindUPublickey(@RequestParam("userId") @ApiParam(value = "用户id") Long userId,
                                               @RequestParam("uPublicKey") @ApiParam(value = "U盾公钥") String uPublicKey) {
        try {
            if (null == userId || StrUtil.isBlank(uPublicKey)) {
                return new ResultUtil<UPublicKeyVo>().setErrorMsg("缺少必需字段");
            }
            //判断用户是否绑定了U盾
            ScUser user = userService.getById(userId);
            if (null == user) {
                return new ResultUtil<UPublicKeyVo>().setErrorMsg("用户不存在");
            }
            if (StrUtil.isNotBlank(user.getUPublickey())) {
                return new ResultUtil<UPublicKeyVo>().setErrorMsg("用户已绑定U盾");
            }

            List<ScUser> list = userService.list(new QueryWrapper<ScUser>().lambda().eq(ScUser::getUPublickey, uPublicKey));
            if (CollectionUtil.isNotEmpty(list)) {
                return new ResultUtil<UPublicKeyVo>().setErrorMsg("该U盾已绑定过用户");
            }

            //查询用户角色id集合
            List<ScRole> roles = iScUserRoleService.findByUserId(userId);

            List<Long> roldIds = new ArrayList<>();
            if (null != roles && roles.size() > 0) {
                roldIds = roles.stream().map(ScRole::getId).collect(Collectors.toList());
            }

            //排序
            Long[] ids = roldIds.toArray(new Long[roldIds.size()]);
            Arrays.sort(ids);

            // 调用sdk 平台私钥对用户id+ids+uPublicKey 签名
            StringBuilder sb = new StringBuilder();
            sb.append(userId);
            for (Long id : ids) {
                sb.append(id);
            }
            sb.append(uPublicKey);
            String orgData = sb.toString();
            byte[] bytes = sdkApiService.PKCS1Sign(Alg.SM2, orgData.getBytes());

            UPublicKeyVo uPublicKeyVo = new UPublicKeyVo(userId, ids, uPublicKey, "", BytesUtil.bytes2HexString(bytes));

            //更新用户绑定U盾公钥
            user.setUPublickey(uPublicKey);
            boolean b = userService.updateById(user);
            if (!b) {
                userService.remove(new QueryWrapper<>());
                return new ResultUtil<UPublicKeyVo>().setErrorMsg("用户绑定U盾失败");
            }
            return new ResultUtil<UPublicKeyVo>().setData(uPublicKeyVo);
        } catch (Exception e) {
            e.printStackTrace();
            userService.remove(new QueryWrapper<>());
            return new ResultUtil<UPublicKeyVo>().setErrorMsg("用户绑定U盾失败");
        }
    }

    @RequestMapping(value = "/clearUPublickey", method = RequestMethod.PUT)
    @ApiOperation(value = "解除绑定U盾公钥")
    public Result<Object> clearUPublickey(@RequestParam("userId") @ApiParam(value = "用户id") Long userId) {
        //查询用户
        ScUser user = userService.getById(userId);
        if (null == user) {
            return new ResultUtil<Object>().setErrorMsg("用户不存在");
        }
        if (StrUtil.isBlank(user.getUPublickey())) {
            return new ResultUtil<Object>().setErrorMsg("用户未绑定U盾");
        }
        user.setUPublickey("");
        boolean b = userService.updateById(user);
        if (!b) {
            return new ResultUtil<Object>().setErrorMsg("解除绑定公钥失败");
        }
        return new ResultUtil<Object>().setData("解除绑定公钥成功");
    }

    @RequestMapping(value = "/dowloadFile", method = RequestMethod.GET)
    @ApiOperation(value = "下载U盾插件接口")
    public ResponseEntity<Resource> dowloadFile(@RequestParam("fileName") @ApiParam(value = "文件名") String fileName) {
        try (InputStream is = getClass().getResourceAsStream("/files/" + fileName)) {
            Assert.notNull(is, "文件不存在");
            byte[] bytes = FileCopyUtils.copyToByteArray(is);
            // 设置下载响应的 headers
            HttpHeaders headers = new HttpHeaders();
            headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + URLEncoder.encode(fileName, "UTF-8") + "\"");

            // 返回带文件内容的响应
            return ResponseEntity.ok()
                    .headers(headers)
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .body(new ByteArrayResource(bytes));
        } catch (IOException e) {
            log.error("", e);
            throw new IllegalArgumentException("读取文件出错");
        }

    }

}
