package com.infoepoch.pms.dispatchassistant.domain.oa;

import com.infoepoch.pms.commons.utils.ResponseUtils;
import com.infoepoch.pms.dispatchassistant.common.component.RedisTool;
import com.infoepoch.pms.dispatchassistant.common.component.SnowFlake;
import com.infoepoch.pms.dispatchassistant.common.constant.RedisKeys;
import com.infoepoch.pms.dispatchassistant.common.constant.StoreKeys;
import com.infoepoch.pms.dispatchassistant.common.exception.ValidationException;
import com.infoepoch.pms.dispatchassistant.common.utils.*;
import com.infoepoch.pms.dispatchassistant.domain.basic.store.KeyValueStoreService;
import com.infoepoch.pms.dispatchassistant.domain.basic.todo.ITodoRepository;
import com.infoepoch.pms.dispatchassistant.domain.basic.todo.Todo;
import com.infoepoch.pms.dispatchassistant.domain.basic.todo.TodoType;
import com.infoepoch.pms.dispatchassistant.domain.basic.user.IUserRepository;
import com.infoepoch.pms.dispatchassistant.domain.basic.user.User;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Service
public class OaService {

    private final static Logger logger = LoggerFactory.getLogger(OaService.class);

    @Value("${spring.application.name}")
    private String applicationName;

    @Autowired
    private RedisTool redisTool;
    @Autowired
    private KeyValueStoreService keyValueStoreService;

    @Autowired
    private ITodoRepository todoRepository;
    @Autowired
    private IUserRepository userRepository;

    /**
     * 批量同步待办
     *
     * @param todoList
     */
    @Async
    public void syncTodoList(List<Todo> todoList) {
        for (Todo todo : todoList) {
            syncTodo(todo);
        }
    }

    /**
     * 同步单个待办
     *
     * @param todo
     */
    @Async
    public void syncTodo(Todo todo) {
        HttpClientBuilder httpClient = HttpClientBuilder.create();
        CloseableHttpClient closeableHttpClient = httpClient.build();
        HttpPost httpPost;
        String parm;
        if (judgeTodoSyncAddFlag(todo)) {
            parm = createAddParam(todo);
            httpPost = new HttpPost(keyValueStoreService.queryValueByKey(StoreKeys.TODO_SYNC_ADD_TASK));
        } else {
            parm = createUpdateParam(todo);
            httpPost = new HttpPost(keyValueStoreService.queryValueByKey(StoreKeys.TODO_SYNC_UPDATE_TASK));
        }
        logger.info("同步待办入参：" + parm);
        StringEntity entity = new StringEntity(parm, "UTF-8");
        httpPost.setEntity(entity);
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(60000).setConnectTimeout(60000).build();
        httpPost.setConfig(requestConfig);
        HttpResponse httpResponse;
        Map<String, Object> map;
        try {
            httpResponse = closeableHttpClient.execute(httpPost);
            org.apache.http.HttpEntity httpEntity = httpResponse.getEntity();
            if (null != httpEntity) {
                String result = EntityUtils.toString(httpEntity, "UTF-8");
                logger.info("同步待办出参：" + result);
                map = JsonUtils.jsonToMap(result);
                if ("0".equals(StringTool.getString(map.get("result"), ""))) {
                    throw new ValidationException("send to OA fail，errorCode:" + StringTool.getString(map.get("errorCode"), ""));
                }
                todo.setSyncStatus(1);// 修改待办同步状态为同步成功
                todoRepository.update(todo);
            } else {
                logger.info("send to OA fail,httpEntity is null");
                throw new ValidationException("send to OA fail,httpEntity is null");
            }
        } catch (Exception e) {
            logger.info(e.getMessage());
            throw new ValidationException(e.getMessage());
        }
    }

    /**
     * 创建入参
     *
     * @param todo
     * @return
     */
    private String createAddParam(Todo todo) {
        Map<String, Object> map = new HashMap<>();
        map.put("app_id", keyValueStoreService.queryValueByKey(StoreKeys.APP_ID));
        map.put("app_pwd", keyValueStoreService.queryValueByKey(StoreKeys.APP_PASSWORD));
        map.put("msg_type", todo.getType().getValue());
        map.put("app_msg_id", todo.getId());
        map.put("instance_id", todo.getId());
        User sender = userRepository.selectById(todo.getSender());
        map.put("sender", sender == null ? todo.getSender() : sender.getUsername());
        map.put("sender_name", todo.getSenderName());
        User receiver = userRepository.selectById(todo.getReceiver());
        map.put("receiver", receiver == null ? todo.getReceiver() : receiver.getUsername());
        map.put("receiver_name", todo.getReceiverName());
        map.put("msg_title", todo.getTitle());
        map.put("msg_url", keyValueStoreService.queryValueByKey(StoreKeys.TODO_REDIRECT_BASE_URL) + todo.getId());
        map.put("stick", "1");
        map.put("moa_config", "no");
        if (todo.getType() == TodoType.TO_READ) {
            map.put("msg_status", StringUtils.isEmpty(todo.getStage()) ? todo.getDocumentType() : todo.getStage());
        } else {
            map.put("msg_status", todo.getStage());
        }
        map.put("urgent_level", "1");
        map.put("doc_type", todo.getDocumentType());
        if (StringUtils.isNotBlank(todo.getUrlMoa())) {
            map.put("moa_config", "yes");
            map.put("sys_todo_type", todo.getDocumentType());
        }
        return JsonUtils.objectToJson(map);
    }

    private String createUpdateParam(Todo todo) {
        Map<String, Object> map = new HashMap<>();
        map.put("app_id", keyValueStoreService.queryValueByKey(StoreKeys.APP_ID));
        map.put("app_pwd", keyValueStoreService.queryValueByKey(StoreKeys.APP_PASSWORD));
        map.put("msg_type", todo.getType().getValue());
        map.put("app_msg_id", todo.getId());
        User receiver = userRepository.selectById(todo.getReceiver());
        map.put("receiver", receiver == null ? todo.getReceiver() : receiver.getUsername());
        User sender = userRepository.selectById(todo.getSender());
        map.put("sender", sender == null ? todo.getSender() : sender.getUsername());
        return JsonUtils.objectToJson(map);
    }

    /**
     * 判断待办同步状态
     *
     * @param todo
     * @return
     */
    private boolean judgeTodoSyncAddFlag(Todo todo) {
        if (todo.getActionTime() == null) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * OA待办重定向
     *
     * @param todoId   待办ID
     * @param moaFlag  moa标识
     * @param backUrl  返回地址
     * @param request
     * @param response
     * @throws IOException
     */
    public void oaTodoRedirect(String todoId, String moaFlag, String backUrl, HttpServletRequest request, HttpServletResponse response) throws IOException {
        if (moaFlag != null && moaFlag.equals("yes")) {
            moaTodoRedirect(todoId, backUrl, request, response);
        } else {
            computerTodoRedirect(todoId, request, response);
        }
    }

    /**
     * 电脑端待办跳转
     *
     * @param todoId   待办ID
     * @param request
     * @param response
     * @throws IOException
     */
    private void computerTodoRedirect(String todoId, HttpServletRequest request, HttpServletResponse response) throws IOException {
        if (StringUtils.isBlank(todoId)) {
            logger.info("OA传递待办ID为空，跳转到报错页面");

            ResponseUtils.sendR(response,UrlTool.splicingUrl(
                    keyValueStoreService.queryValueByKey(StoreKeys.BASE_URL), // 待办跳转基础地址
                    keyValueStoreService.queryValueByKey(StoreKeys.ERROR_REDIRECT_URI) // 500报错跳转页面地址
            ));
            return;
        }
        Cookie[] cookies = request.getCookies();
        if (cookies.length == 0) {
            logger.info("OA登录信息失效,ObSSOCookie 为空");
            ResponseUtils.sendR(response,UrlTool.splicingUrl(
                    keyValueStoreService.queryValueByKey(StoreKeys.BASE_URL), // 待办跳转基础地址
                    keyValueStoreService.queryValueByKey(StoreKeys.ERROR_REDIRECT_URI) // 500报错跳转页面地址
            ));
            return;
        }
        String token = null;
        String server = null;
        for (Cookie cookie : cookies) {
            if ("ObSSOCookie".equals(cookie.getName())) {
                token = cookie.getValue();
            }
            if ("server".equals(cookie.getName())) {
                server = cookie.getValue();
            }
        }
        if (StringUtils.isBlank(token) || StringUtils.isBlank(server)) {
            logger.info("微服务获取OA登陆信息异常,location:TodoService.computerTodoRedirect");
            ResponseUtils.sendR(response,keyValueStoreService.queryValueByKey(StoreKeys.OA_MAIN_PAGE_URL));
            return;
        }
        User user = getUserByToken(token);
        if (user == null) {
            ResponseUtils.sendR(response,keyValueStoreService.queryValueByKey(StoreKeys.OA_MAIN_PAGE_URL));
            return;
        }
        // 用户自动登录
        autoLoginUser(user, request, response);
        // 重定向到待办指定地址
        Todo todo = todoRepository.selectById(todoId);
        if (todo == null) {
            logger.info("未查询到对应ID【" + todoId + "】的待办，跳转到报错页面");
            ResponseUtils.sendR(response,
                    UrlTool.splicingUrl(
                            keyValueStoreService.queryValueByKey(StoreKeys.BASE_URL), // 待办跳转基础地址
                            keyValueStoreService.queryValueByKey(StoreKeys.ERROR_REDIRECT_URI) // 500报错跳转页面地址
                    ));
            return;
        }
        if (Objects.equals(TodoType.TO_READ, todo.getType())) {
            // 待阅的待办直接修改为已阅，然后同步回oa
            todo.read();
            syncTodo(todo);
        }
        ResponseUtils.sendR(response,
                UrlTool.splicingUrlWithCheck(
                        keyValueStoreService.queryValueByKey(StoreKeys.BASE_URL),
                        todo.getUrl()
                )
        );
    }

    /**
     * moa待办跳转
     *
     * @param todoId   待办ID
     * @param backUrl  返回地址
     * @param request
     * @param response
     */
    private void moaTodoRedirect(String todoId, String backUrl, HttpServletRequest request, HttpServletResponse response) throws IOException {
        System.out.println("backUrl=" + backUrl);
        if (StringUtils.isBlank(todoId)) {
            logger.info("OA传递待办ID为空，跳转到返回地址页面");
            ResponseUtils.sendR(response,backUrl);
            return;
        }
        Cookie[] cookies = request.getCookies();
        if (cookies.length == 0) {
            logger.info("OA登录信息失效,ObSSOCookie 为空");
            ResponseUtils.sendR(response,backUrl);
            return;
        }
        String token = null;
        String server = null;
        for (Cookie cookie : cookies) {
            if ("ObSSOCookie".equals(cookie.getName())) {
                token = cookie.getValue();
            }
            if ("server".equals(cookie.getName())) {
                server = cookie.getValue();
            }
        }
        if (StringUtils.isBlank(token) || StringUtils.isBlank(server)) {
            logger.info("微服务获取OA登陆信息异常,location:TodoService.moaTodoRedirect");
            ResponseUtils.sendR(response,backUrl);
            return;
        }
        User user = getUserByToken(token);
        if (user == null) {
            ResponseUtils.sendR(response,backUrl);
            return;
        }
        // 用户自动登录
        autoLoginUser(user, request, response);
        // 重定向到待办指定地址
        Todo todo = todoRepository.selectById(todoId);
        if (todo == null) {
            logger.info("未查询到对应ID【" + todoId + "】的待办，跳转到返回地址页面");
            ResponseUtils.sendR(response,backUrl);
            return;
        }
        if (Objects.equals(TodoType.TO_READ, todo.getType())) {
            // 待阅的待办直接修改为已阅，然后同步回oa
            todo.read();
            syncTodo(todo);
        }
        String moaUrl = todo.getUrlMoa();
        if (moaUrl.contains("?")) {
            moaUrl = moaUrl + "&backUrl=" + backUrl;
        } else {
            moaUrl = moaUrl + "?backUrl=" + backUrl;
        }
        moaUrl = UrlTool.splicingUrlWithCheck(applicationName, moaUrl);
        ResponseUtils.sendR(response,
                UrlTool.splicingUrlWithCheck(
                        keyValueStoreService.queryValueByKey(StoreKeys.MOA_TODO_REDIRECT_BASE_URL),
                        moaUrl
                )
        );
//        response.sendRedirect(moaUrl);
    }

    /**
     * 根据token获取用户信息
     *
     * @param token
     * @return
     */
    private User getUserByToken(String token) {
        try {
            URL httpUrl = new URL(keyValueStoreService.queryValueByKey(StoreKeys.OA_LOGIN_PAGE_URL));
            logger.info("ssoLogin createSsoSession url: " + httpUrl.toString());
            HttpHeaders requestHeader = new HttpHeaders();
            requestHeader.setContentType(MediaType.APPLICATION_XML);
            StringBuffer xmlString = new StringBuffer();
            xmlString.append("<request><token>")
                    .append(token)
                    .append("</token></request>");
            // 创建 HttpEntity
            String url = httpUrl.toString();
            HttpEntity<String> requestEntity = new HttpEntity<>(xmlString.toString(), requestHeader);
            String responseEntity = invokeUrl(url, requestEntity);
            logger.info("responseEntity: " + responseEntity);
            //构造输入流
            InputStream is = IOUtils.toInputStream(responseEntity);
            SSOResponse response = (SSOResponse) XMLProxy.getInstance().fromResponseXML(is);// 从SSO获取响应结果
            String status = response.getStatus();
            logger.info("SSOResponse 信息：" + JsonUtils.objectToJson(response));
            if ("ok".equals(status)) {
                String username = response.getUid();
                User user = userRepository.selectByUsername(StringTool.getString(username, "").toLowerCase());
                if (user == null) {
                    logger.info("用户信息为空。location:TodoService.getUserByToken");
                }
                return user;
            } else {
                logger.info("OA获取用户信息失败: " + response.getMessage());
                throw new ValidationException("OA获取用户信息失败");
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
            logger.info("生成OA登录地址URL报错: " + e.getMessage());
            return null;
        } catch (Exception e) {
            e.printStackTrace();
            logger.info("获取用户信息失败: " + e.getMessage());
            return null;
        }
    }

    /*
     *
     * 功能描述: 注册集成RestTemplate
     * @Param: [url, requestEntity]
     * @Return: org.springframework.http.ResponseEntity<java.lang.String>
     * @Author: jill
     * @Date: 2020/3/31 18:27
     */
    private String invokeUrl(String url, HttpEntity<String> requestEntity) {
        StringHttpMessageConverter m = new StringHttpMessageConverter(Charset.forName("UTF-8"));
        RestTemplate restTemplate = new RestTemplateBuilder().additionalMessageConverters(m).build();
        return restTemplate.postForObject(url, requestEntity, String.class);

    }

    /**
     * 用户信息自动登录
     *
     * @param user
     * @param request
     * @param response
     */
    private void autoLoginUser(User user, HttpServletRequest request, HttpServletResponse response) {
        String sign = ServletTool.getSign();
        if (StringUtils.isBlank(sign)) {
            sign = SnowFlake.instant().nextId().toString();
            Cookie cookie = new Cookie(RedisKeys.SIGN, sign);
            cookie.setPath(request.getContextPath());
            response.addCookie(cookie);
        }
        // 用户信息失效时间：2小时
        redisTool.put(RedisKeys.AUTHED_USER + sign, JsonUtils.objectToJson(user), 2, TimeUnit.HOURS);
    }

}
