package com.zzwtec.wechat.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jfinal.kit.StrKit;
import com.xiaoleilu.hutool.util.StrUtil;
import com.zzwtec.jdbc.entity.ThirdUserWechat;
import com.zzwtec.jdbc.service.ThirdUserWechatService;
import com.zzwtec.wechat.common.constant.UrlConstants;
import com.zzwtec.wechat.common.enums.CallType;
import com.zzwtec.wechat.common.enums.DoorType;
import com.zzwtec.wechat.config.WeChatConfig;
import com.zzwtec.wechat.rpc.APIResponse;
import com.zzwtec.wechat.rpc.api.APIService;
import com.zzwtec.wechat.rpc.inject.ProxyBuilder;
import com.zzwtec.wechat.sdk.api.ApiConfigKit;
import com.zzwtec.wechat.sdk.inlet.MsgController;
import com.zzwtec.wechat.sdk.kit.HttpKit;
import com.zzwtec.wechat.sdk.msg.out.OutNewsMsg;
import com.zzwtec.wechat.service.LoginService;
import com.zzwtec.wechat.util.CharUtil;
import com.zzwtec.wechat.util.CommonUtil;
import com.zzwtec.wechat.util.TimeUtil;
import com.zzwtec.wechat.util.security.MyBase64Util;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.xiaoleilu.hutool.util.StrUtil.isEmpty;
import static com.zzwtec.wechat.config.WeChatConfig.weChatConfig;
import static com.zzwtec.wechat.sdk.api.ApiConfigKit.getAppId;
import static com.zzwtec.wechat.util.CharUtil.PARA_SEPARATE;

/**
 * 门铃呼叫信息控制器<br>
 * 用户点击门铃呼叫模版消息后有这个控制显示
 *
 * @author 邓燎燕
 *         2016年5月9日
 */
@Controller
@RequestMapping("/doorbell")
public class DoorbellController {
    private static ThirdUserWechatService thirdUserWechatService;
    @Autowired
    private HttpServletRequest request;
    private static StringRedisTemplate redisTemplate;
    @Autowired
    private LoginService loginService;
    private static final String CODE_KEY = "wechat-code-";
    private static final String DOOR_INFO_KEY = "wechat-door-";
    private static final APIService apiService = ProxyBuilder.build(APIService.class, WeChatConfig.weChatConfig);
    private static final Logger logger = LoggerFactory.getLogger(DoorbellController.class);

    @Autowired
    public void setStringRedisTemplate(StringRedisTemplate redisTemplate) {
        DoorbellController.redisTemplate = redisTemplate;
    }
    @Autowired
    public void setThirdUserWechatService(ThirdUserWechatService thirdUserWechatService) {
        DoorbellController.thirdUserWechatService = thirdUserWechatService;
    }

    /**
     * 进入呼叫页面
     * v0 room
     * v1 callName
     * v2 openid
     */
    @RequestMapping("/callroom/{_room}/{_callName}/{openid}")
    public String callroom(@PathVariable("_room") String room, @PathVariable("_callName") String callName, @PathVariable("openid") String openid) {
        room = StrUtil.isBlank(room) ? null : room.replaceAll("_", "-");
        callName = StrUtil.isBlank(callName) ? null : CharUtil.decode(callName).replaceAll("_", "-");
        String res = HttpKit.get(weChatConfig.getWsHost() + "/capture-" + room + "/1");
        request.setAttribute("room", room);
        request.setAttribute("callName", callName);
        request.setAttribute("res", res);
        if (openid != null) {
            APIResponse response = apiService.findUserByAppidAndOpenid(ApiConfigKit.getAppId(), openid,apiService);
            if (response.isSuccess()) {
                request.setAttribute("userId", JSONObject.parseObject(response.getData()).getString("id"));
            }
        }
        return "/door/view/callroom.html";
    }

    /**
     * 查看呼叫历史
     * v0 userId
     * v1 page
     */
    @RequestMapping("/callhistory/{userId}/{page}")
    public String callHistory(@PathVariable("userId") String userId, @PathVariable("page") int page) {

        if (StrKit.isBlank(userId)) {
            return "/door/view/nocallhistory.html";

        }
        // 查询呼叫历史
        APIResponse response = apiService.getCallLog(userId, page, 20);
        if (APIResponse.isFail(response)) {
            return "/common/view/failure_tips.html";
        }
        if (StrKit.isBlank(response.getData())) {
            return "/door/view/nocallhistory.html";
        }
        // 处理呼叫历史
        JSONObject pageJson = JSONObject.parseObject(response.getData());
        List<Map<String, Object>> callLogList = toCallLogList(pageJson.getJSONArray("data"));
        if (callLogList == null || callLogList.isEmpty()) {
            return "/door/view/nocallhistory.html";
        }
        request.setAttribute("appId", ApiConfigKit.getAppId());
        request.setAttribute("total", pageJson.getIntValue("total"));
        request.setAttribute("logArr", callLogList);
        return "/door/view/callhistory.html";
    }

    /**
     * 查看呼叫快照
     * v0 captureKey
     */
    @RequestMapping(value = "/getcallcapture",produces = "application/json")
    @ResponseBody
    public String getCallCapture(String captureKey) {
        APIResponse response = apiService.queryCaptureURLById(captureKey);
        if (!APIResponse.isSuccess(response)) {
            return APIResponse.toJSONObject(response).toJSONString();
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("url",response.getData());
        return jsonObject.toJSONString();
    }

    /**
     * 获取门列表
     * v0 openid
     * v1 communityId
     * v2 buildId
     * v3 nowTime
     */
    @RequestMapping("/getdoorlist")
    public ModelAndView getDoorList(String e) {
        ModelAndView modelAndView = new ModelAndView();
        if (StrKit.isBlank(e)) {
            // 操作超时 重新点击“开门”菜单获取
            request.setAttribute("tips", "操作超时 重新点击“开门”菜单获取");
            modelAndView.setViewName("/door/view/opendoor.html");
            return modelAndView;
        }


        // openid&communityId&buildId&timestamp
        String decode = MyBase64Util.decode(e);
        if (decode == null) {
            // 操作无效 重新点击“开门”菜单获取
            request.setAttribute("tips", "操作无效 重新点击“开门”菜单获取");
            modelAndView.setViewName("/door/view/opendoor.html");
            return modelAndView;

        }
        String[] paras = decode.split(PARA_SEPARATE);
        if (paras.length != 4) {
            // 操作无效 重新点击“开门”菜单获取
            request.setAttribute("tips", "操作无效 重新点击“开门”菜单获取");
            modelAndView.setViewName("/door/view/opendoor.html");
            return modelAndView;
        }
        if (isTimeout(paras[3])) {
            // 操作超时 重新点击“开门”菜单获取
            request.setAttribute("tips", "操作超时 重新点击“开门”菜单获取");
            modelAndView.setViewName("/door/view/opendoor.html");
            return modelAndView;
        }
        String tokenId=loginService.getLoginToken();
        String doorListView = getDoorList(this, request, paras[0], paras[1], paras[2],tokenId);
        if (StrKit.notBlank(doorListView)){
            modelAndView.setViewName(doorListView);
        }else{
            modelAndView = null;
        }

        return modelAndView;
    }

    /**
     * 开门
     * v0 openid
     * v1 doorId
     * v2 nowTime
     */
    @RequestMapping("/opendoor")
    public String opendoor(String e) {
        APIResponse response = thridOpenDoor(e);
        request.setAttribute("tips", response.getMsg());
        return "/door/view/opendoor.html";
    }

    /**
     * 开门
     * v0 openid
     * v1 doorId
     * v2 nowTime
     */
    @RequestMapping(value = "/opendoor2",produces = "application/json")
    @ResponseBody
    public JSONObject opendoor2(String e) {
        APIResponse response = thridOpenDoor(e);
        return APIResponse.toJSONObject(response);
    }

    public static String getDoorList(Object target, HttpServletRequest request, String openid, String communityId, String buildId,String tokenId) {
        MsgController msgController = null;
        if (target instanceof MsgController) {
            msgController = (MsgController) target;
        }

        APIResponse re = apiService.getDoorList(communityId, buildId, tokenId);
        if (re == null || re.getCode() != 0) {
            if (msgController != null) {
                ((MsgController) target).renderOutTextMsg("请求失败，请重新操作");
                return null;
            } else {
                return "/common/view/failure_tips.html";
            }

        }

        JSONArray doorArr = JSONArray.parseArray(re.getData());

        //该房产没有门
        if(CollectionUtils.isEmpty(doorArr) && msgController != null){
            msgController.renderOutTextMsg("暂无对应的门!");
            return null;
        }
        if (msgController != null && doorArr.size() == 1) {
            // 只有一个门
            JSONObject doorJson = doorArr.getJSONObject(0);
            Boolean remote = doorJson.getBoolean("remote");
            if(remote != null && remote){
                logger.info("门的json数据--->{}",doorJson.toJSONString());
                APIResponse response = openCheck(openid, communityId, buildId);
                if(response!=null){
                    msgController.renderOutTextMsg(response.getMsg());
                    return null;
                }
                response = apiService.thridOpenDoor(openid, doorJson.getString("doorId"),apiService);
                boolean fail = response.isFail();
                if(fail){
                    msgController.renderOutTextMsg("服务器繁忙,开门失败!请稍后重试!");
                    return null;
                }
                msgController.renderOutTextMsg("开门成功!");
                return null;
            }else {
                msgController.renderOutTextMsg("暂无对应的门!");
                return null;
            }

        } else {
            // 多个门
            JSONArray doorbells = JSONArray.parseArray("[]");
            JSONArray enclosures = JSONArray.parseArray("[]");
            for (int i = 0; i < doorArr.size(); i++) {
                JSONObject doorJson = doorArr.getJSONObject(i);
                Boolean remote = doorJson.getBoolean("remote");
                if(remote != null && remote){
                    //0表示楼栋门，1表示小区门
                    if (doorJson.getIntValue("type") == DoorType.getDoorType(DoorType.BUILD_DOOR)) {
                        doorbells.add(doorJson);
                    } else if(doorJson.getIntValue("type") == DoorType.getDoorType(DoorType.COMMUNITY_DOOR)) {
                        enclosures.add(doorJson);
                    }

                    String itemDoorId = doorJson.getString("doorId");
                    String itemCommunityId = doorJson.containsKey("communityId") ? doorJson.getString("communityId") : communityId;
                    String itemBuildId = "0";
                    if (doorJson.getIntValue("type") == DoorType.getDoorType(DoorType.BUILD_DOOR)) {
                        itemBuildId = doorJson.containsKey("buildId") ? doorJson.getString("buildId") : buildId;
                    }
                    redisTemplate.opsForValue().set(DOOR_INFO_KEY+itemDoorId,itemCommunityId+PARA_SEPARATE+itemBuildId,5,TimeUnit.MINUTES);
                }
            }

            int wxArticleCount = Integer.MAX_VALUE;
            int maxShow = Integer.MAX_VALUE;
            OutNewsMsg outNewsMsg = null;
            List<Map<String, Object>> list = null;
            if (msgController == null) {
                list = new ArrayList<>();
            } else {
                outNewsMsg = new OutNewsMsg(msgController.getInMsg());
                wxArticleCount = weChatConfig.getWxArticleCount();
                maxShow = weChatConfig.getWxArticleCount();
                if (doorArr.size() > wxArticleCount) {
                    maxShow = wxArticleCount-1;
                }
                logger.info("wxArticleCount:{} maxShow:{} doorbells.size:{} enclosures.size:{}", wxArticleCount, maxShow, doorbells.size(), enclosures.size());
            }
            String appId = ApiConfigKit.getAppId();
            int i = 0;
            long nowTime = System.currentTimeMillis();
            for (int j = 0; j < doorbells.size(); j++, i++) {
                if (i < maxShow) {
                    JSONObject doorJson = doorbells.getJSONObject(j);
                    String doorId = doorJson.getString("doorId");
                    String title = doorJson.getString("name");
                    String encode = getDoorEncode(openid, doorId, title, nowTime);
                    if (list != null) {
                        Map<String, Object> map = new HashMap<>();
                        map.put("title", title);
                        map.put("e", encode);
                        list.add(map);
                    } else {
                        String url = weChatConfig.getHost() + UrlConstants.DOORBELL_OPEN_DOOR + "?appId=" + appId + "&e=" + encode;
                        outNewsMsg.addNews(title, "楼栋门", null, url);
                    }
                } else {
                    break;
                }
            }
            if (list != null && list.size() > 0) {
                request.setAttribute("doorbells", list);
                list = new ArrayList<>();
            }

            for (int j = 0; j < enclosures.size(); i++, j++) {
                if (i < maxShow) {
                    JSONObject doorJson = enclosures.getJSONObject(j);
                    String doorId = doorJson.getString("doorId");
                    String title = doorJson.getString("name");
                    String encode = getDoorEncode(openid, doorId, title, nowTime);

                    if (list != null) {
                        Map<String, Object> map = new HashMap<>();
                        map.put("title", title);
                        map.put("e", encode);
                        list.add(map);
                    } else {
                        String url = weChatConfig.getHost() + UrlConstants.DOORBELL_OPEN_DOOR + "?appId=" + appId + "&e=" + encode;
                        outNewsMsg.addNews(title, "小区门", null, url);
                    }

                } else {
                    break;
                }
            }
            if (list != null && list.size() > 0) {
                request.setAttribute("enclosures", list);
            }
            if (msgController == null) {
                request.setAttribute("appId", ApiConfigKit.getAppId());
                return "/door/view/doorlist.html";
            } else {
                if(doorArr.size() > wxArticleCount){
                    String encode = MyBase64Util.encode(openid + PARA_SEPARATE + communityId + PARA_SEPARATE + buildId + PARA_SEPARATE + nowTime);
                    String url = weChatConfig.getHost() + UrlConstants.DOORBELL_GET_DOOR_LIST + "?e=" + encode;
                    outNewsMsg.addNews("点击选择要开锁的门", "门列表", null, url);
                }
                msgController.render(outNewsMsg);
            }
            return null;
        }
    }

    private static String getDoorEncode(String openid, String doorId, String title, long nowTime) {
        return MyBase64Util.encode(openid + PARA_SEPARATE + doorId + PARA_SEPARATE + title + PARA_SEPARATE + nowTime);
    }

    private List<Map<String, Object>> toCallLogList(JSONArray callLogJson) {
        if (callLogJson == null || callLogJson.size() == 0) {
            return null;
        }
        List<Map<String, Object>> callLogList = new ArrayList<>(callLogJson.size());
        for (int i = 0; i < callLogJson.size(); i++) {
            JSONObject item = callLogJson.getJSONObject(i);
            Map<String, Object> map = new HashMap<>();
            boolean isCallee = isCallee(item.getIntValue("calleeType"));
            String[] calltimeStr = TimeUtil.timeLong2Str(item.getLong("calltime")).split(" ");
            map.put("isCallee", isCallee);
            map.put("tips", isCallee ? "访客呼叫" : "呼叫管理员");
            map.put("date", calltimeStr[0].replaceAll("\\-", "/"));
            map.put("time", calltimeStr[1]);
            map.put("callname", item.getString("callName"));
            map.put("calleename", item.getString("calleeName"));
            map.put("captureKey", item.containsKey("img") ? item.getString("img") : "");
            callLogList.add(map);
        }
        return callLogList;
    }

    private boolean isCallee(int calleeType) {
        CallType calledType = CallType.findType(calleeType);
        return calledType == CallType.CELL || calledType == CallType.PHONE;
    }

    private APIResponse thridOpenDoor(String e) {
        if (StrKit.isBlank(e)) {
            return new APIResponse(415, "操作无效 重新点击“开门”菜单获取");
        }
        String decode = MyBase64Util.decode(e);
        if (decode == null) {
            return new APIResponse(415, "操作无效 重新点击“开门”菜单获取");
        }
        // openid&doorId&title&nowTime
        String[] paras = decode.split(PARA_SEPARATE);
        if (paras.length != 4) {
            return new APIResponse(415, "操作无效 重新点击“开门”菜单获取");
        }
        String openid = paras[0];
        String doorId = paras[1];
        String time = paras[3];
        if (isTimeout(time)) {
            return new APIResponse(415, "操作超时 重新点击“开门”菜单获取");
        }
        APIResponse response = openCheck(openid, doorId);
        if(response!=null){
            return response;
        }
        String code = getCode(e);
        String result = redisTemplate.opsForValue().get(code);
        if(StringUtils.isEmpty(result)){
            redisTemplate.opsForValue().set(code,e,2,TimeUnit.MINUTES);
            return apiService.thridOpenDoor(openid, doorId, apiService);
        }else{
            return new APIResponse(415, "门锁已开!");
        }
    }
    /**
     * 开门权限检查
     * @param openid
     * @param doorId
     * @return
     */
    private static APIResponse openCheck(String openid, String doorId) {
        // communityId&buildId
        String communityIdBuildId = redisTemplate.opsForValue().get(DOOR_INFO_KEY+doorId);
        if(communityIdBuildId == null){
            return new APIResponse(415, "操作超时 重新点击“开门”菜单获取");
        }
        String[] communityIdBuildIds = communityIdBuildId.split(PARA_SEPARATE);
        String communityId = communityIdBuildIds[0];
        String buildId = communityIdBuildIds[1];

        return openCheck(openid, communityId, buildId);
    }

    private static APIResponse openCheck(String openid, String communityId, String buildId) {
        // 查询微信关绑定系失败
        ThirdUserWechat thirdUserWechat = thirdUserWechatService.queryUserWechatByOpenid(openid);
        // 没有绑定账号
        if (thirdUserWechat==null || isEmpty(thirdUserWechat.getUserId())) {
            return new APIResponse(310, "查询不到您的用户信息");
        }
        APIResponse userCellResponse = apiService.queryUserAndCellByUserId(thirdUserWechat.getUserId());
        if(userCellResponse.isFail()){
            return new APIResponse(415, "操作超时 重新点击“开门”菜单获取");
        }
        JSONArray userCells = JSONArray.parseArray(userCellResponse.getData());
        if(userCells == null || userCells.isEmpty()){
            return new APIResponse(415, "您没有房产");
        }
        boolean has = false;
        boolean disable = false;
        for(int i=0; i<userCells.size(); i++) {
            String userCellBuildId = ((JSONObject)userCells.get(i)).getString("buildId");
            String userCellCommunityId = ((JSONObject)userCells.get(i)).getString("communityId");
            if(userCellCommunityId.equals(communityId) && userCellBuildId.equals(buildId)){
                has = true;
                disable = ((JSONObject)userCells.get(i)).getBooleanValue("disable");
                break;
            }
        }
        if(!has){
            return new APIResponse(415, "您没有该楼栋的房产");
        }
        if(disable){
            return new APIResponse(415, "您的该楼栋房产开门权限已被物业禁用，请联系物业");
        }
        return null;
    }

    public static boolean isTimeout(String timestamp) {
        long oldTime = getTimestamp(timestamp);
        long nowTime = System.currentTimeMillis();
        return Math.abs(nowTime - oldTime) > (2 * 60 * 1000);
    }

    private static long getTimestamp(String timestamp) {
        char last = timestamp.charAt(timestamp.length() - 1);
        if (last < '0' || last > '9') {
            timestamp = timestamp.substring(0, timestamp.length() - 1);
        }
        return Long.parseLong(timestamp);
    }

    private String getCode(String e){
        return CODE_KEY + CommonUtil.getMD5(e.getBytes());
    }
}
