package cn.quantgroup.xyqb.controller.internal.querylog;

import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.quantgroup.xyqb.exception.UserQueryLogException;
import cn.quantgroup.xyqb.util.encrypt.Rsa;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.*;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.google.gson.*;
import io.swagger.annotations.ApiOperation;

import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.entity.Address;
import cn.quantgroup.xyqb.entity.UserDetail;
import cn.quantgroup.xyqb.entity.UserQueryLog;
import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.model.PageModel;
import cn.quantgroup.xyqb.model.UserQueryInfo;
import cn.quantgroup.xyqb.service.http.IHttpService;
import cn.quantgroup.xyqb.service.user.IAddressService;
import cn.quantgroup.xyqb.service.user.IUserDetailService;
import cn.quantgroup.xyqb.service.user.IUserQueryLogService;
import cn.quantgroup.xyqb.util.IdcardValidator;
import cn.quantgroup.xyqb.util.ValidationUtil;
import cn.quantgroup.xyqb.util.encrypt.Md5Util;

/**
 * Created by zenglibin on 17/06/13.
 */
@Slf4j
@RestController
@RequestMapping("/query")
public class UserQueryLogController {

    @Autowired
    private IUserQueryLogService userQueryLogService;
    @Autowired
    private IUserDetailService userDetailService;
    @Autowired
    private IAddressService addressService;

    @Value("${payapi.http}")
    private String payCenterUrl;
    @Value("${xyqb.paycenter.id}")
    private String payCenterId;
    @Value("${opapi.http}")
    private String yunyingUrl;


    @Autowired
    private IHttpService httpService;

    private static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYiw1PKWnCbkKyzHK+blHpKTR/qtO3Oq7nvjSdcopCSmOJqji3B+qJMrf03242mYJIQeF3YSTQZTfri5EkNgoqn0Y/KYpLAKuq89jPdIkB3lvirvew9tpfbAT4B14WgoWdMH5ooqBt0ly3f+JjoBM5dKFTOrhckhFDoaB3UAaaiQIDAQAB";

    private static final String PRIVATE_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJiLDU8pacJuQrLMcr5uUekpNH+q07c6rue+NJ1yikJKY4mqOLcH6okyt/TfbjaZgkhB4XdhJNBlN+uLkSQ2CiqfRj8piksAq6rz2M90iQHeW+Ku97D22l9sBPgHXhaChZ0wfmiioG3SXLd/4mOgEzl0oVM6uFySEUOhoHdQBpqJAgMBAAECgYA3DfCWwoaWEr9l0p4TFrPfZ+y3qwrQVZCsuRw6Ow2lUT3NgK8JeATw0WpNKZqYgBziQUzDjj8AK5fcHjobDJnsKGqC2VQ+j05hQZztoHTrYxOx6xrGxIzqmbt/dPsw779xXSRJu3DuUeCm6CrGZpVpPX/NtXBxIhXRY2KRNa1SZQJBAPMboc+M6/OeGPQqFvXg9jgEWcosBpy6HtukfjONQAVuM5e0pZa8SQCLhcoHgbbqcEhbDAJEqr9x9eZjjFPSt08CQQCgoe/hOVf0s5oo1IM1TVEUkGOIzVVlyTMwu0p4jwt3987D7BKZe7mCl41quWDwL4JIQ0GcivVMpJzYsDBZHRqnAkBMgCKAHHlXdSWnF+OXxg3U/NGAhDAke5EgTvgDouxFiTMlBwygjWlviXg1Zf1UoRtqOXRi9lbA3cyijirnacSTAkBmve0ug30MmOvbfcHGkANyQcBIOf2LMxu46bKCVgwh2bC4hACJhydqrgDX6GZmehy8l7gZpo+rTAa+WkMyXHk1AkEArt6ElkyNI7TDu0By59Zin05tuZJr6QoMXs9bVH+xP3OBG1KfPYTBc9yb5MOjXIxAjyGGeDpUfhuUDBe56GTOBA==";

	/**
	 * @yapi http://yapi.quantgroups.com/project/17/interface/api/259
	 */
    @RequestMapping("/queryLog")
    @ApiOperation(value = "给运营系统提供的查询查询记录的", notes = "给运营系统提供的查询查询记录的", httpMethod = "POST")
    public JsonResult queryLog(HttpServletRequest request, @RequestParam(required = false) String beginDate, @RequestParam(required = false) String endDate, Integer pageId, Integer pageSize) {
        log.info("查询日期：beginDate{},endDate{}", beginDate, endDate);
        String token = request.getHeader(Constants.X_AUTH_TOKEN);
        checkUserToken(token);
        try {
            SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
            SimpleDateFormat sfs = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date date1, date2;
            if (StringUtils.hasLength(beginDate)) {
                date1 = sfs.parse(beginDate + " 00:00:00");
            } else {
                String nowStr = sf.format(new Date());
                date1 = sfs.parse(nowStr + " 00:00:00");
            }
            if (StringUtils.hasLength(endDate)) {
                date2 = sfs.parse(endDate + " 23:59:59");
            } else {
                String nowEndStr = sf.format(new Date());
                date2 = sfs.parse(nowEndStr + " 23:59:59");
            }
            int startP = (pageId - 1) * pageSize;
            List<UserQueryLog> userQueryLogs = userQueryLogService.findByTimestamp(date1, date2, startP, pageSize);
            Long total = userQueryLogService.findByTimestampCount(date1, date2);
            PageModel<UserQueryLog> uqp = new PageModel<>();
            uqp.setTotal(total);
            if (pageSize == 0 || pageSize < 0) {
                pageSize = Constants.PAGE_SIZE_DEFAULT;
            } else if (pageSize > Constants.PAGE_SIZE_MAX) {
                pageSize = Constants.PAGE_SIZE_MAX;
            }
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            try {
                for (UserQueryLog ll : userQueryLogs) {
                    ll.setCreateDate(sdf.format(ll.getCreatedAt()));
                }
            } catch (Exception e) {
                log.info("日期时间转换异常");
                return JsonResult.buildErrorStateResult("日期时间转换异常", null);
            }
            uqp.setPageSize(pageSize);
            uqp.setPageId(pageId);
            uqp.setPageList(userQueryLogs);
            int yu = total.intValue() % pageSize;
            Double d = Math.ceil((double)total / (double)pageSize);
            uqp.setPages(yu > 0 ? d.intValue() + 1 : d.intValue());
            return JsonResult.buildSuccessResult("查询成功", uqp);
        } catch (Exception e) {
            return JsonResult.buildErrorStateResult("查询失败", null);
        }
    }

	/**
	 * @yapi http://yapi.quantgroups.com/project/17/interface/api/261
	 */
    @RequestMapping("/queryUserInfo")
    @ApiOperation(value = "给运营系统提供的查询用户信息的接口", notes = "给运营系统提供的查询用户信息的接口", httpMethod = "POST")
    public JsonResult queryForResult(HttpServletRequest request, String key, String keyValues, String columns, Integer pageId, Integer pageSize) {
        String token = request.getHeader(Constants.X_AUTH_TOKEN);
        String userName = checkUserToken(token);
        if (StringUtils.isEmpty(keyValues)) {
            return JsonResult.buildErrorStateResult("请输入查询条件", null);
        }
        // 支持
        String[] values = keyValues.split(";|,|\\n");
        List<String> queryV = Arrays.asList(values);
        log.info("[UserQueryLogController][exportExcel]运营系统导出用户信息开始 size:{}, key:{}, keyValues:{}, columns:{}", queryV.size(), key, keyValues, columns);
        if (queryV != null && queryV.size() > Constants.USER_QUERY_INFO_BATCH_SIZE) {
            return JsonResult.buildErrorStateResult("查询记录数是" + queryV.size() + ",超过"+Constants.USER_QUERY_INFO_BATCH_SIZE +"条限制", null);
        }
        List<UserDetail> userDetails = new ArrayList<>();
        Page<UserDetail> userDetailPage = null;
        if (pageSize == 0 || pageSize < 0) {
            pageSize = Constants.PAGE_SIZE_DEFAULT;
        } else if (pageSize > Constants.PAGE_SIZE_MAX) {
            pageSize = Constants.PAGE_SIZE_MAX;
        }
        //如果维度里面没有保护查询条件则加入条件
        if (!columns.contains(key)) {
            columns = columns + "," + key;
        }
        if (Objects.equals(key, Constants.USER_ID)) {
            List<Long> userIds = new ArrayList<>();
            try {
                for (String s : queryV) {
                    if (s.length() > 8) {
                        return JsonResult.buildErrorStateResult("查询的userId" + s + "长度超过8位", null);
                    }
                    long uid = Long.valueOf(s);
                    userIds.add(uid);
                }
            } catch (Exception e) {
                return JsonResult.buildErrorStateResult("查询的userId有误", null);
            }
            userDetailPage = userDetailService.getUserDetailsPage(userIds, null, null, pageId, pageSize, "auto");
        } else if (Objects.equals(key, Constants.PHONE_NO)) {
            for (String pn : queryV) {
                if (!ValidationUtil.validatePhoneNo(pn)) {
                    return JsonResult.buildErrorStateResult("查询的手机号码" + pn + "格式有误", null);
                }
            }
            userDetailPage = userDetailService.getUserDetailsPage(null, queryV, null, pageId, pageSize, "auto");
        } else if (Objects.equals(key, Constants.ID_NO)) {
            for (String idno : queryV) {
                if (!IdcardValidator.is18Idcard(idno) && !IdcardValidator.is15Idcard(idno)) {
                    return JsonResult.buildErrorStateResult("查询的身份证号" + idno + "格式有误", null);
                }
            }
            userDetailPage = userDetailService.getUserDetailsPage(null, null, queryV, pageId, pageSize, "auto");
        }
        if (userDetailPage != null) {
            userDetails = userDetailPage.getContent();
        }
        List<UserQueryInfo> userQueryInfos = toUserQueryInfo(key, columns, queryV, userDetails);
        //保存记录
        UserQueryLog log = new UserQueryLog();
        log.setQueryItems(columns.replace(Constants.USER_ID, "用户ID").replace(Constants.PHONE_NO, "手机号").replace(Constants.USER_NAME, "姓名").replace(Constants.ID_NO, "身份证号").replace(Constants.BANK_CARD, "银行卡号").replace(Constants.ADDRESS, "地址"));
        if(userDetailPage != null){
            log.setResultAmount(userDetailPage.getTotalElements());
        }
        //转换存储
        log.setQueryCondition(key.replace(Constants.USER_ID, "用户ID").replace(Constants.PHONE_NO, "手机号").replace(Constants.USER_NAME, "姓名").replace(Constants.ID_NO, "身份证号"));
        log.setQueryDetail(keyValues.replace("\n", ";"));
        log.setLoginUserName(userName);
        userQueryLogService.save(log);
        PageModel<UserQueryInfo> uqi = new PageModel<>();
        uqi.setPageId(pageId);
        uqi.setPageList(userQueryInfos);
        uqi.setPageSize(pageSize);
        if(userDetailPage != null){
            uqi.setPages(userDetailPage.getTotalPages());
            uqi.setTotal(userDetailPage.getTotalElements());
        }
        return JsonResult.buildSuccessResult("查询成功", uqi);
    }

    private HashMap<String, String> getBankCardsByPhoneNos(List<String> phoneNos) {
        HashMap<String, Object> ha = new HashMap<>();
        ha.put("type", "1");
        ha.put("phones", phoneNos);
        String phoneNoStr = new Gson().toJson(ha);

        String data = "";
        String sign = "";
        HashMap<String, String> phonesMap = new HashMap<>();
        try {
            data = Rsa.encrypt(phoneNoStr, PUBLIC_KEY);
            sign = Md5Util.build(data);
        } catch (Exception e) {
            log.info("参数加密异常", e);
            throw new UserQueryLogException("参数加密异常", e);
        }
        HashMap<String, String> parameters = new HashMap<>();
        parameters.put(Constants.RESULT_DATA, data);
        parameters.put("sign", sign);
        //访问支付中心查询用户银行卡接口
        String resultStr = httpService.post(payCenterUrl + "/ex/search/card_list", parameters);
        try {
            JsonObject obj = new JsonParser().parse(resultStr).getAsJsonObject();
            if (obj.get(Constants.RESULT_DATA) != null) {
                JsonObject data1 = obj.get(Constants.RESULT_DATA).getAsJsonObject();
                String dataStr = data1.get(Constants.RESULT_DATA).getAsString();
                String signStr = data1.get("sign").getAsString();
                //校验签名
                if (Md5Util.build(dataStr).equals(signStr)) {
                    String jsonDataStr = Rsa.decrypt(dataStr, PRIVATE_KEY);
                    JsonArray listObj = new JsonParser().parse(jsonDataStr).getAsJsonArray();
                    for (JsonElement jsonElement : listObj) {
                        JsonObject jo = jsonElement.getAsJsonObject();
                        JsonArray cardList = jo.get("cardList").getAsJsonArray();

                        String phone = jo.get("phone").getAsString();
                        StringBuilder cards = new StringBuilder();
                        for (JsonElement element : cardList) {
                            cards.append(element.getAsString()).append(";");
                        }
                        if (cards.length() > 0) {
                            phonesMap.put(phone, cards.substring(0, cards.length() - 1));
                        }

                    }
                } else {
                    log.error("签名校验失败！");
                    throw new UserQueryLogException("签名校验失败");
                }
            }
        } catch (Exception e) {
            log.error("查询银行卡信息接口返回解析异常", e);
            throw new UserQueryLogException("查询银行卡信息接口返回解析异常", e);
        }
        return phonesMap;
    }

	/**
	 * @yapi http://yapi.quantgroups.com/project/17/interface/api/257
	 */
    @RequestMapping("/exportUserInfo")
    @ApiOperation(value = "给运营系统提供的导出用户信息的接口", notes = "给运营系统提供的导出用户信息的接口", httpMethod = "POST")
    public JsonResult exportExcel(final HttpServletResponse response, HttpServletRequest request, String key, String keyValues, String columns) {
        String token = request.getHeader(Constants.X_AUTH_TOKEN);
        checkUserToken(token);
        if (StringUtils.isEmpty(keyValues)) {
            return JsonResult.buildErrorStateResult("请输入查询条件", null);
        }
        String[] values = keyValues.split(";");
        List<String> queryV = Arrays.asList(values);
        log.info("[UserQueryLogController][exportExcel]运营系统导出用户信息开始 size:{}, key:{}, keyValues:{}, columns:{}", queryV.size(), key, keyValues, columns);
        if (queryV != null && queryV.size() > Constants.USER_QUERY_INFO_BATCH_SIZE) {
            return JsonResult.buildErrorStateResult("查询记录数是" + queryV.size() + ",超过"+Constants.USER_QUERY_INFO_BATCH_SIZE +"条限制", null);
        }
        //如果维度里面没有保护查询条件则加入条件
        if (!columns.contains(key)) {
            columns = columns + "," + key;
        }
        List<UserDetail> userDetails = new ArrayList<>();
        if (Objects.equals(key, Constants.USER_ID)) {
            List<Long> userIds = new ArrayList<>();
            try {
                for (String s : queryV) {
                    if (s.length() > 8) {
                        return JsonResult.buildErrorStateResult("查询的userId" + s + "长度超过8位", s);
                    }
                    long uid = Long.valueOf(s);
                    userIds.add(uid);
                }
            } catch (Exception e) {
                return JsonResult.buildErrorStateResult("查询的userId有误", null);
            }
            userDetails = userDetailService.findByUserIdIn(userIds);
        } else if (Objects.equals(key, Constants.PHONE_NO)) {
            for (String pn : queryV) {
                if (!ValidationUtil.validatePhoneNo(pn)) {
                    return JsonResult.buildErrorStateResult("查询的手机号码" + pn + "格式有误", pn);
                }
            }
            userDetails = userDetailService.findByPhoneNos(queryV);
        } else if (Objects.equals(key, Constants.ID_NO)) {
            for (String idno : queryV) {
                if (!IdcardValidator.is18Idcard(idno) && !IdcardValidator.is15Idcard(idno)) {
                    return JsonResult.buildErrorStateResult("查询的身份证号" + idno + "格式有误", idno);
                }
            }
            userDetails = userDetailService.findByIdnos(queryV);
        }
        List<UserQueryInfo> userQueryInfos = toUserQueryInfo(key, columns, queryV, userDetails);
        /*
         * 将文件存到指定位置
         * 采用时间戳命名方式，避免同主机出现并发同名文件冲突引起的文件流被动关闭现象
         */
        try(OutputStream output = response.getOutputStream()){
            String filename = "users-".concat(LocalDateTime.now().toString()).concat(".xlsx");
            response.reset();
            response.setHeader("Content-disposition", "attachment; filename=".concat(filename));
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setCharacterEncoding("utf-8");
            HSSFWorkbook wb = getHssfWorkbook(userQueryInfos);
            wb.write(output);
            log.info("[UserQueryLogController][exportExcel]运营系统导出用户信息完成 filename:{}, size:{}, key:{}, keyValues:{}, columns:{}", filename, queryV.size(), key, keyValues, columns);
        } catch (Exception e) {
            log.error("解析运营系统用户token返回结果resultStr异常", e);
        }
        return JsonResult.buildSuccessResult("导出成功", null);
    }

    /**
     * 数据类型转换
     *
     * @param key
     * @param columns
     * @param queryV
     * @param userDetails
     * @return
     */
    private List<UserQueryInfo> toUserQueryInfo(String key, String columns, List<String> queryV, List<UserDetail> userDetails) {
        HashMap<String, String> phonesCards = new HashMap<>();
        if (columns.contains(Constants.BANK_CARD)) {
            //需加银行卡信息
            if (Objects.equals(key, Constants.PHONE_NO)) {
                phonesCards = getBankCardsByPhoneNos(queryV);
            } else {
                List<String> phones = new ArrayList<>();
                for (UserDetail uda : userDetails) {
                    phones.add(uda.getPhoneNo());
                }
                phonesCards = getBankCardsByPhoneNos(phones);
            }
        }
        List<Address> addresslist = new ArrayList<>();
        if (columns.contains(Constants.ADDRESS)) {
            List<Long> uidss = new ArrayList<>();
            //加地址信息
            for (UserDetail ud : userDetails) {
                uidss.add(ud.getUserId());
            }
            if (uidss != null && uidss.size() > 0) {
                addresslist = addressService.findByUserIds(uidss);
            }
        }
        List<UserQueryInfo> userQueryInfos = new ArrayList<>();
        for (UserDetail ud : userDetails) {
            UserQueryInfo uqi = new UserQueryInfo();
            if (columns.contains(Constants.ID_NO)) {
                uqi.setIdNo(ud.getIdNo());
            }
            if (columns.contains(Constants.PHONE_NO)) {
                uqi.setPhoneNo(ud.getPhoneNo());
            }
            if (columns.contains(Constants.USER_NAME)) {
                uqi.setName(ud.getName());
            }
            if (columns.contains(Constants.USER_ID)) {
                uqi.setUserId(ud.getUserId());
            }
            if (columns.contains(Constants.ADDRESS)) {
                Optional<Address> oad = addresslist.stream().filter(addr -> Objects.equals(addr.getUserId(), ud.getUserId())).findFirst();
                oad.ifPresent(address -> uqi.setAddress(address.toString()));
            }
            if (columns.contains(Constants.BANK_CARD) && phonesCards != null) {
                uqi.setBankCards(phonesCards.get(ud.getPhoneNo()));
            }
            userQueryInfos.add(uqi);
        }
        return userQueryInfos;
    }

    /**
     * 生成Excel
     * @param uqls
     * @return
     */
    private HSSFWorkbook getHssfWorkbook(List<UserQueryInfo> uqls) {
        HSSFWorkbook wb = new HSSFWorkbook();
        if(CollectionUtils.isEmpty(uqls)){
            return wb;
        }
        // 第二步，在webbook中添加一个sheet,对应Excel文件中的sheet
        HSSFSheet sheet = wb.createSheet("用户信息列表");
        // 第三步，在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制short
        HSSFRow row = sheet.createRow(0);
        // 第四步，创建单元格，并设置值表头 设置表头居中
        HSSFCellStyle style = wb.createCellStyle();
        // 创建一个居中格式
        style.setAlignment(HSSFCellStyle.ALIGN_CENTER);

        HSSFCell cell = row.createCell(0);
        cell.setCellValue(Constants.USER_ID);
        cell.setCellStyle(style);
        cell = row.createCell(1);
        cell.setCellValue("手机号");
        cell.setCellStyle(style);
        cell = row.createCell(2);
        cell.setCellValue("姓名");
        cell.setCellStyle(style);
        cell = row.createCell(3);
        cell.setCellValue("身份证号");
        cell.setCellStyle(style);
        cell = row.createCell(4);
        cell.setCellValue("银行卡号");
        cell.setCellStyle(style);
        cell = row.createCell(5);
        cell.setCellValue("地址");
        cell.setCellStyle(style);

        // 第五步，写入实体数据 实际应用中这些数据从数据库得到，
        for (int i = 0; i < uqls.size(); i++) {
            row = sheet.createRow(i + 1);
            UserQueryInfo user = uqls.get(i);
            // 第四步，创建单元格，并设置值
            if (user != null && row != null) {
                HSSFCell cell0 = row.createCell(0);
                if (cell0 != null) {
                    cell0.setCellValue(user.getUserId() == null ? " " : String.valueOf(user.getUserId()));
                }
                HSSFCell cell1 = row.createCell(1);
                if (cell1 != null) {
                    cell1.setCellValue(user.getPhoneNo() == null ? " " : user.getPhoneNo());
                }
                HSSFCell cell2 = row.createCell(2);
                if (cell2 != null) {
                    cell2.setCellValue(user.getName() == null ? " " : user.getName());
                }
                HSSFCell cell3 = row.createCell(3);
                if (cell3 != null) {
                    cell3.setCellValue(user.getIdNo() == null ? " " : user.getIdNo());
                }
                HSSFCell cell4 = row.createCell(4);
                if (cell4 != null) {
                    cell4.setCellValue(user.getBankCards() == null ? " " : user.getBankCards());
                }
                HSSFCell cell5 = row.createCell(5);
                if (cell5 != null) {
                    cell5.setCellValue(user.getAddress() == null ? " " : user.getAddress());
                }
            }
        }
        return wb;
    }

    private String checkUserToken(String token) {
        if (StringUtils.isEmpty(token)) {
            log.info("token为空，非法查询");
            throw new UserQueryLogException("缺少授权信息");
        }
        HashMap<String, String> parameters = new HashMap<>();
        parameters.put("token", token);
        //访问用户中心查询用户银行卡接口
        String resultStr = httpService.post(yunyingUrl + "/user/info", parameters);
        String userName = null;
        try {
            JsonObject resultUser = new JsonParser().parse(resultStr).getAsJsonObject();
            if (Objects.equals(Constants.SUCCESS_CODE, resultUser.get(Constants.RESULT_CODE).getAsString()) && resultUser.get(Constants.RESULT_DATA).getAsJsonObject() != null) {
                userName = resultUser.get(Constants.RESULT_DATA).getAsJsonObject().get("user").getAsString();
            }
        } catch (Exception e) {
            log.error("解析运营系统用户token返回结果resultStr异常", e);
            throw new UserQueryLogException("未授权查询", e);
        }
        if (StringUtils.isEmpty(userName)) {
            throw new UserQueryLogException("未授权查询");
        }
        return userName;
    }
}
