package com.ruoyi.biemo.business.service;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.util.StringUtil;
import com.ruoyi.biemo.business.domain.Category;
import com.ruoyi.biemo.business.domain.DocInfo;
import com.ruoyi.biemo.business.domain.Report;
import com.ruoyi.biemo.business.domain.event.CategoryDeleteEvent;
import com.ruoyi.biemo.business.domain.event.CategorySaveEvent;
import com.ruoyi.biemo.business.domain.event.ReportDeleteEvent;
import com.ruoyi.biemo.business.domain.event.ReportSaveEvent;
import com.ruoyi.biemo.core.MyConstants;
import com.ruoyi.biemo.core.page.Page;
import com.ruoyi.biemo.core.response.MyResponseResult;
import com.ruoyi.biemo.elasticsearch.util.EsService;
import com.ruoyi.biemo.mongodb.utils.CriteriaAndWrapper;
import com.ruoyi.biemo.mongodb.utils.MongoHelper;
import com.ruoyi.biemo.utils.MyObjects;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.sign.Md5Utils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.framework.web.service.SysLoginService;
import com.ruoyi.system.service.ISysLogininforService;
import com.ruoyi.system.service.ISysUserService;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.awt.image.ReplicateScaleFilter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * @author makesoft
 * @version 1.0
 * @date 2021/1/14 11:59
 */
@Service
public class ReportService extends EsService<Report> {

    @Autowired
    private MongoHelper mongoHelper;

    @Autowired
    private ISysUserService userService;

    @Autowired
    private SysLoginService loginService;

    @Autowired
    private ApplicationContext applicationContext;

    @Override
    protected Report loadData(SearchSourceBuilder context, SearchHit hit) {
        String id = hit.getId();
        if (Objects.isNull(id) || StringUtil.isEmpty(id)) return null;
        return mongoHelper.findById(id,Report.class);
    }
    @Override
    public List<Report> batchLoadData(SearchSourceBuilder context, SearchHit[] hitArr) {
        List<String> ids = new ArrayList<>();
        for(SearchHit hit:hitArr){
            String id = hit.getId();
            if (Objects.isNull(id) || StringUtil.isEmpty(id)) continue;
            ids.add(id);
        }
        return mongoHelper.findListByIds(ids,Report.class);
    }
    /**
     * 查询报告提交
     *
     * @param id 报告提交主键
     * @return 报告提交
     */
    public Report selectReportById(String id)
    {
        if(id==null||id.trim().length()==0){
            return null;
        }
        List<Report> reports = esLambdaQuery().eq(Report::getId,id).query();
        return reports!=null&&reports.size()>0?reports.get(0):null;
    }

    /**
     * 查询报告提交列表
     *
     * @param report 报告提交
     * @return 报告提交
     */
    public List<Report> selectReportList(Report report)
    {
        return esLambdaQuery().eqAll(report).query();
    }

    /**
     * 查询报告提交分页列表
     *
     * @param report 报告提交
     * @return 报告提交
     */
    public Page<Report> selectReportPage(Report report, Page<Report> page){
        return esLambdaQuery().eqAll(report).sort(Report::getCreateTime, SortOrder.DESC).page(page.getPageNum(),page.getPageSize()).queryPage(true);
    }

    /**
     * 新增报告提交
     *
     * @param report 报告提交
     * @return 结果
     */
    public String insertOrUpdateReport(Report report)
    {
        //生成报告编号
        if(report==null){
            return null;
        }
        Lock lock = new ReentrantLock();
        if(report.getReportNo()==null){
            lock.lock();
            Long count = mongoHelper.findCountByQuery(new CriteriaAndWrapper(), Report.class);
            report.setReportNo(MyObjects.getNo(count));
            lock.unlock();
        }
        String id = mongoHelper.insert(report);
        report.setId(id);
        applicationContext.publishEvent(new ReportSaveEvent(this,report));
        return id;
    }


    /**
     * 批量删除报告提交
     *
     * @param ids 需要删除的报告提交主键
     * @return 结果
     */
    public void deleteReportByIds(String[] ids)
    {
        List<String> list  = Arrays.stream(ids).collect(Collectors.toList());
        mongoHelper.deleteByIds(list, Report.class);
        applicationContext.publishEvent(new ReportDeleteEvent(this,list));

    }

    /**
     * 删除报告提交信息
     *
     * @param id 报告提交主键
     * @return 结果
     */
    public void deleteReportById(String id)
    {
        mongoHelper.deleteById(id,Report.class);
        applicationContext.publishEvent(new ReportDeleteEvent(this,id));
    }

    @EventListener(classes = {ReportSaveEvent.class})
    public void saveIndex(ApplicationEvent event) {
        ReportSaveEvent saveEvent = (ReportSaveEvent)event;
        List<Report> list = saveEvent.getList();
        insertBatch(list);
    }

    @EventListener(classes = {ReportDeleteEvent.class})
    public void delIndex(ApplicationEvent event) {
        ReportDeleteEvent saveEvent = (ReportDeleteEvent)event;
        List<String> ids = saveEvent.getIds();
        deleteBatch(ids);
    }

    @Value("${sso.password}")
    private String password = "admin123";

    @Value("${sso.apiUrl}")
    private String apiUrl = "http://test.vsp.cqooc.com/open/api/v2/oauth/token?appId={APPID}&signature={SIGNATURE}&ticket={TICKET}";
    @Value("${sso.appId}")
    private String appId = "cb00a3e26b963b25e28cae6e046f1190";
    @Value("${sso.secretKey}")
    private String secretKey = "4ce36bf549d64d44b6cbc48e926abb05";

    @Value("${report.uploadUrl}")
    private String reportUploadUrl = "http://test.vsp.cqooc.com/open/api/v2/upload/data";

    @Value("${report.refreshUrl}")
    private String refreshUrl = "http://test.vsp.cqooc.com/open/api/v2/oauth/refresh?token=TOKEN&appid=APPID&signature=SIGNATURE";

    @Value("${report.validateUrl}")
    private String validateUrl = "http://test.vsp.cqooc.com/open/api/v2/oauth/validate?username=USERNAME&password=PASSWORD&nonce=NONCE&cnonce=CNONCE&appid=APPID&signature=SIGNATURE";

    @Value("${report.uploadAttachmentUrl}")
    private String uploadAttachmentUrl = "http://test.vsp.cqooc.com/open/api/v2/upload/attachment?access_token=ACCESS_TOKEN&appid=APPID&originId=ORIGINID&filename=FILENAME&title=TITLE&remarks=REMARKS";

    @Value("${report.getReportListUrl}")
    private String getReportListUrl = "http://test.vsp.cqooc.com/open/api/v2/query/data?appId=APPID&accessToken=ACCESS_TOKEN&signature=SIGNATURE";
    @Autowired
    private RedisCache redisCache;

    public String ticket2token(String ticket) {
        if(StringUtils.isNotBlank(ticket)){
            String sign = Md5Utils.hash(appId+secretKey+ticket).toUpperCase();
            Map<String, Object> map = new HashMap<>();
            map.put("APPID", appId);
            map.put("SIGNATURE", sign);
            map.put("TICKET",ticket);
            RestTemplate restTemplate = new RestTemplate();
            ResponseEntity<Map> entity = restTemplate.getForEntity(apiUrl, Map.class, map);
            Map loginMap = entity.getBody();
            String userName = "";
            String nickName = "";
            if (loginMap != null && (Integer) loginMap.get("code") == 0) {
               JSONObject jsonObject = (JSONObject) JSONObject.toJSON(loginMap);
               JSONObject data = jsonObject.getObject("data",JSONObject.class);
               userName = data.getString("userName");
               String openId = data.getString("openId");
               String accessToken = data.getString("accessToken");
               MyResponseResult myResponseResult = new MyResponseResult();
               myResponseResult.setAccessToken(accessToken);
               myResponseResult.setOpenId(openId);
               Long createTime = data.getLong("createTime");
               Long expiresTime = data.getLong("expiresTime");
               Long expiresLong = expiresTime-System.currentTimeMillis();
               redisCache.setCacheObject(MyConstants.TOKEN_PREFIX+userName, myResponseResult, expiresLong.intValue(), TimeUnit.MILLISECONDS);
               if(expiresLong<=0){
                   refreshToken(accessToken,userName,myResponseResult);
               }
               //用户名不存在就新建
                SysUser sysUser = new SysUser();
                if(UserConstants.UNIQUE.equals(userService.checkUserNameUnique(userName))){
                    sysUser.setUserName(userName);
                    if(StringUtils.isNotBlank(nickName)){
                        sysUser.setNickName(nickName);
                    }else{
                        sysUser.setNickName(userName);
                    }
                    sysUser.setRemark("平台用户");
                    sysUser.setRoleIds(new Long[]{1L});
                    sysUser.setCreateBy("xitong");
                    sysUser.setCreateTime(new Date());
                    sysUser.setSex("1");
                    sysUser.setDelFlag("0");
                    sysUser.setStatus("0");
                    sysUser.setDeptId(100l);
                    sysUser.setPassword(SecurityUtils.encryptPassword(password));
                    userService.insertUser(sysUser);
                }else{
                    sysUser = userService.selectUserByUserName(userName);
                    if(!"0".equals(sysUser.getDelFlag())){
                        sysUser.setDelFlag("0");
                        sysUser.setRoleIds(new Long[]{1L});
                        userService.updateUser(sysUser);
                    }
                }
                return loginService.login(sysUser.getUserName(),password,null, IdUtils.fastSimpleUUID());
            }
        }
        return null;
    }

    //验证用户信息


    //上报实验结果
    public AjaxResult uploadReport(Report report,String username){
        if(report==null||StringUtils.isBlank(username)){
            return AjaxResult.error("参数不能为空！");
        }
        report.setAppId(appId);
        MyResponseResult myResponseResult = redisCache.getCacheObject(MyConstants.TOKEN_PREFIX+username);
        if(myResponseResult!=null&&myResponseResult.getAccessToken()!=null){
            //插入本地数据库
            String id = insertOrUpdateReport(report);
            report.setAccessToken(myResponseResult.getAccessToken());
            String signature = report.getSignature();
            signature = Md5Utils.hash(appId+secretKey+report.getAccessToken()).toUpperCase();
            report.setSignature(signature);
            report.setOpenId(myResponseResult.getOpenId());
            report.setOriginId(id);
            RestTemplate restTemplate = new RestTemplate();
            ResponseEntity<Map> uploadResponseEntity = restTemplate.postForEntity(reportUploadUrl, report, Map.class);
            Map result = uploadResponseEntity.getBody();
            if (result != null && (Integer) result.get("code") == 0) {
                return AjaxResult.success(result.get("data"));
            }else{
                return AjaxResult.error(result.get("message").toString());
            }
        }
        return AjaxResult.error("没有授权或授权到期！");
    }

    //获取实验结果
    public Page<Report> getReportList(String username){
        RestTemplate restTemplate = new RestTemplate();
        Map<String,Object> map = new HashMap<>();
        MyResponseResult myResponseResult =  redisCache.getCacheObject(MyConstants.TOKEN_PREFIX+username);
        if(myResponseResult!=null&&StringUtils.isNotBlank(myResponseResult.getAccessToken())){
            String accessToken = myResponseResult.getAccessToken();
            String signature = Md5Utils.hash(appId+secretKey+accessToken).toUpperCase();
            map.put("APPID",appId);
            map.put("ACCESS_TOKEN",accessToken);
            map.put("SIGNATURE",signature);
            map.put("ORIGINID",username);
            ResponseEntity<Map> responseEntity = restTemplate.getForEntity(getReportListUrl,Map.class,map);
            Map body = responseEntity.getBody();
            if(body!=null&&(Integer)body.get("code")==0){
                JSONObject jsonObject = (JSONObject) JSONObject.toJSON(body);
                JSONObject data = jsonObject.getObject("data",JSONObject.class);
                JSONObject meta = jsonObject.getObject("meta",JSONObject.class);
                String total = meta.getString("total");
                String start = meta.getString("start");
                String size = meta.getString("size");
                List<Report> reportList = JSONObject.parseArray(data.getString("data"),Report.class);
                Page<Report> page = new Page<>();
                page.setRows(reportList);
                page.setPageNum(Integer.parseInt(start));
                page.setPageSize(Integer.parseInt(size));
                page.setTotal(Long.valueOf(total));
                return page;
            }
        }
        return new Page<Report>();
    }

    //刷新token方法
    public String refreshToken(String oldToken,String username,MyResponseResult myResponseResult){
        String newToken = "";
        String signature = Md5Utils.hash(appId+secretKey+oldToken).toUpperCase();
        RestTemplate restTemplate = new RestTemplate();
        Map<String,Object> map = new HashMap<>();
        map.put("APPID",appId);
        map.put("TOKEN",oldToken);
        map.put("SIGNATURE",signature);
        ResponseEntity<Map> responseEntity = restTemplate.getForEntity(refreshUrl,Map.class,map);
        Map responseMap = responseEntity.getBody();
        if(responseMap!=null&&(Integer)responseMap.get("code")==0){
            JSONObject jsonObject = (JSONObject) JSONObject.toJSON(responseMap);
            JSONObject data = jsonObject.getObject("data",JSONObject.class);
            newToken = data.getString("accessToken");
            Long createTime = data.getLong("createTime");
            Long expiresTime = data.getLong("expiresTime");
            Long expiresLong = expiresTime-System.currentTimeMillis();
            myResponseResult.setAccessToken(newToken);
            redisCache.setCacheObject(MyConstants.TOKEN_PREFIX+username,myResponseResult,expiresLong.intValue(),TimeUnit.MILLISECONDS);
        }
        return newToken;
    }



    /**
     * "success": true,
     * "code": 0,
     * "total": 0,
     * "data": {
     *         "openId": "fb4078687af9ac748c00318d53a36c43",
     *         "userName": "测试用户",
     *         "accessToken": "61ynp2w3vxvcdhf730h7cilelr51yljgofkp0uxqoji7hzps9sn0hlfat7ys3cbm",
     *         "createTime": 1654159120374,
     *         "expiresTime": 1654245520374,
     *         "createTimeDisplay": "2022-06-02 16:38:40",
     *         "expiresTimeDisplay": "2022-06-03 16:38:40"
     *     }
     */
}
