package com.philisense.www.controller.web;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.philisense.www.common.config.CommonConst;
import com.philisense.www.config.ActiveProject;
import com.philisense.www.controller.BaseUtilController;
import com.philisense.www.access.WebRestURLSecurityMetadataSource;
import com.philisense.www.annotation.log.OperateLog;
import com.philisense.www.core.text.Convert;
import com.philisense.www.enums.ClientStatus;
import com.philisense.www.exception.ServiceException;
import com.philisense.www.pojos.entity.JwtUser;
import com.philisense.www.service.JwtUserDetailsService;
import com.philisense.www.service.ProResAuthService;
import com.philisense.www.service.ProUserService;
import com.philisense.www.utils.JwtTokenUtils;
import com.philisense.www.utils.SecurityUtils;
import com.philisense.www.utils.ServletUtils;
import com.philisense.www.utils.StringUtils;

import com.philisense.www.utils.ip.IpUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;

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.data.redis.core.StringRedisTemplate;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping(CommonConst.WEB_REST_SERVICE_PREFICX+"auth")
@Api(tags = "AuthenticationControllerForWeb |一个用来操作-{用户Web登陆}-业务模块的控制器")
public class AuthenticationControllerForWeb  extends BaseUtilController {

    private static final Logger log = LoggerFactory.getLogger(AuthenticationControllerForWeb.class);

    @Value("${jwt.tokenHead}")
    private String tokenHeader;

    @Autowired
    private ProUserService proUserService;

    @Autowired
    private ProResAuthService proResAuthService;

    @Autowired
    private StringRedisTemplate redisTemplate;



    @Autowired
    private JwtTokenUtils jwtTokenUtils;

    @Autowired
    private JwtUserDetailsService jwtUserDetailsService;

    @Autowired
    private ActiveProject activeProject;

    /**
     * 登录授权
     *
     * @param map
     * @return
     */
    @PostMapping(value = "${jwt.auth.path}")
    @ApiOperation(value = "根据-{用户名和密码}-登陆", notes = "成功返回:{\"success\":true,\"message\",\"登陆成功\"},失败返回:{\"success\":false,\"message\",\"数据失败\"}")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "body", name = "username", value = "用户名", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "password", value = "用户密码", required = true, dataTypeClass = String.class),
    })
    @OperateLog(name = "登录授权", value = "query", type = "PC")
    public Map<String, Object> loginForWeb(HttpServletRequest request, HttpServletResponse response, @RequestBody Map<String, Object> map) {
        Map<String, Object> result = new HashMap<>();
        try {
            String username = Convert.toStr(map.get("username"));
            String password = Convert.toStr(map.get("password"));
            String loginType = Convert.toStr(map.get("loginType"));
            if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
                throw new ServiceException("用户名或密码不能为空!");
            }

            //判断是邮箱还是手机号的正则表达式
            String em = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$";
            String ph = "^[1][34578]\\d{9}$";

            // 开始时间
            long stime = System.currentTimeMillis();
            final JwtUser jwtUser;
            if (String.valueOf(username).matches(ph) || "phone".equals(loginType)) {
                // 手机号登录
                jwtUser = (JwtUser) jwtUserDetailsService.loadUserByPhone(username);
            } else if (String.valueOf(username).matches(em) || "email".equals(loginType)) {
                // 邮箱登录
                jwtUser = (JwtUser) jwtUserDetailsService.loadUserByEmail(username);
            } else {
                // 用户名登录 account
                jwtUser = (JwtUser) jwtUserDetailsService.loadUserByUsername(username);
            }

            if (StringUtils.isNull(jwtUser)) {
                log.info("登录用户：{} 不存在.", username);
                throw new ServiceException("对不起，[" + username + "] 用户不存在或者密码错误");
            } else if (jwtUser.isAccountNonLocked()) {
                log.info("登录用户：{} 已被冻结.", username);
                throw new ServiceException("对不起，您的账号：[" + username + "] 已冻结");
            } else if (jwtUser.isEnabled()) {
                log.info("登录用户：{} 已被停用.", username);
                throw new ServiceException("对不起，您的账号：[" + username + "] 已停用");
            } else if (!SecurityUtils.matchesPassword(password, jwtUser.getPassword())) {
                /*// 输错3次锁定用户
                Map<String, Object> user = jwtUser.getUser();
                user.put("freeze_date", new Date());
                user.put("freeze_cause", "3次密码错误");
                user.put("status", "3");
                proUserService.update(user);*/
                log.info("登录用户：{} 密码错误.", username);
                throw new ServiceException("对不起，[" + username + "] 用户不存在或者密码错误");
            }
            // 结束时间
            long etime = System.currentTimeMillis();
            log.info("验证用户信息执行时长：{} 毫秒.", (etime - stime));

            stime = System.currentTimeMillis();
            // 记录用户登录信息
            Map<String, Object> user = jwtUser.getUser();
            user.put("last_login_ip", IpUtils.getIpAddr(ServletUtils.getRequest()));
            user.put("last_login_date", new Date());
            proUserService.update(user);
            etime = System.currentTimeMillis();
            log.info("记录用户登录信息执行时长：{} 毫秒.", (etime - stime));

            stime = System.currentTimeMillis();
            // 生成令牌
            jwtUser.setClient(ClientStatus.PC.ordinal());
            jwtUser.getUser().remove("password");
            final String token = jwtTokenUtils.generateToken(jwtUser);
            etime = System.currentTimeMillis();
            log.info("生成令牌执行时长：{} 毫秒.", (etime - stime));

            Map<String, Object> tempmap = new HashMap<>();
            tempmap.put("token", this.tokenHeader + token);
            tempmap.put("loginType", loginType);
            tempmap.put("type", "account");

            result.put(DATASUCCESS, SUCCESSFLAG);
            result.put(DATARECORD, tempmap);
        } catch (Exception e) {
            result.put(DATASUCCESS, ERRORFLAG);
            result.put(DATAMESSAGE, "登录失败！" + e.getMessage());
        }

        // 返回 token
        return result;
    }

    /**
     * 获取登陆用户信息
     *
     * @param map
     * @return
     */
    @PostMapping(value = "${jwt.auth.account}")
    @ApiOperation(value = "根据-{用户名}-获取用户信息", notes = "成功返回:{\"success\":true,\"message\",\"登陆成功\"},失败返回:{\"success\":false,\"message\",\"数据失败\"}")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "body", name = "username", value = "用户名", required = true, dataTypeClass = String.class)
    })
    @OperateLog(name = "获取登陆用户信息", value = "query", type = "PC")
    public Map<String, Object> currentUser(HttpServletRequest request, HttpServletResponse response, @RequestBody Map<String, Object> map) {
        Map<String, Object> result = new HashMap<String, Object>();
        try {
            JwtUser jwtUser = SecurityUtils.getJwtUser();
            if (StringUtils.isNotNull(jwtUser)) {
                Map<String, Object> user = jwtUser.getUser();

                // 角色集合
                Set<Map<String, Object>> roles = jwtUserDetailsService.getAuthoritiesNoGroupByUserId(jwtUser.getUserId());

                Map<String, Object> resultMap = new HashMap<>();
                resultMap.put("user", handleUserInfo(user));
                resultMap.put("isAdmin", SecurityUtils.isAdmin());
                resultMap.put("roles", roles.stream().map(role -> new HashMap<String, Object>() {
                    {
                        put("value", Convert.toStr(role.get("auth_type")));
                        put("label", Convert.toStr(role.get("auth_name")));
                    }
                }).collect(Collectors.toList()));

                result.put(DATASUCCESS, SUCCESSFLAG);
                result.put(DATAMESSAGE, QUERY_SUCCESS_MSG);
                result.put(DATARECORD, resultMap);
            }

        } catch (Exception e) {
            result.put(DATASUCCESS, ERRORFLAG);
            result.put(DATAMESSAGE, "未登录或登录已过期，请重新登录。" + e.getMessage());
            result.put(DATASTATUS, 401);
        }

        return result;
    }

    /**
     * 修改用户基本信息
     * @param request
     * @param response
     * @param map
     * @return
     */
    @PostMapping("/updateUser")
    @ApiOperation(value = "根据-{用户表}-对象更新记录", notes = "成功返回:{\"success\":true,\"message\",\"数据更新成功\"},失败返回:{\"success\":false,\"message\",\"数据更新失败\"}")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "body", name = "user_id", value = "用户Id", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "user_name", value = "用户名(登录账号)", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "user_name_cn", value = "用户名称", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "user_name_en", value = "员工英文名", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "password", value = "登录密码", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "email", value = "电子邮箱", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "phone", value = "手机号码", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "landline", value = "办公电话", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "avatar", value = "头像路径", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "sign", value = "个性签名", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "tags", value = "用户标签", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "birthday", value = "用户生日", required = true, dataTypeClass = Date.class),
            @ApiImplicitParam(paramType = "body", name = "country", value = "国家/地区", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "province", value = "省", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "city", value = "市", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "address", value = "街道地址", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "work_addr", value = "工作地点（01>总部飞利信大厦  选择楼层 02>非总部 选择省份）", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "floor", value = "所在楼层", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "inprovince", value = "所在省份", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "wx_openid", value = "绑定的微信号", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "wx_mpopenid", value = "绑定的微信公众号", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "wx_miniopenid", value = "绑定的微信小程序", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "wx_unionid", value = "绑定微信UnionID", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "mobile_imei", value = "绑定的手机串号", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "device_num", value = "绑定的手机设备号", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "al_taobao", value = "绑定的淘宝", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "al_alipay", value = "绑定的支付宝", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "al_dingding", value = "绑定的钉钉", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "pwd_security_level", value = "密码安全级别（0初始 1很弱 2弱 3安全 4很安全）", required = true, dataTypeClass = Integer.class),
            @ApiImplicitParam(paramType = "body", name = "pwd_update_date", value = "密码最后更新时间", required = true, dataTypeClass = Date.class),
            @ApiImplicitParam(paramType = "body", name = "wx_msg", value = "微信消息通知(0否1是)", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "email_msg", value = "邮件消息通知(0否1是)", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(paramType = "body", name = "system_msg", value = "系统消息通知(0否1是)", required = true, dataTypeClass = String.class),
    })
    @OperateLog(name = "根据-{用户表}-对象更新记录", value = "update", type = "PC")
    public Map<String, Object> updateForProUserForWeb(HttpServletRequest request, HttpServletResponse response, @RequestBody Map<String, Object> map) {
        Map<String, Object> result = new HashMap<>();
        try {
            String userid = Convert.toStr(map.get("user_id"));
            if (org.apache.commons.lang3.StringUtils.isEmpty(userid)) {
                result.put(DATASUCCESS, ERRORFLAG);
                result.put(DATAMESSAGE, UPDATE_FAIL_MSG + "记录ID不能为空");
                return result;
            }

            Map<String, Object> temmap = proUserService.selectByPrimaryKey(userid);
            if (null == temmap || temmap.size() == 0) {
                result.put(DATASUCCESS, ERRORFLAG);
                result.put(DATAMESSAGE, UPDATE_FAIL_MSG + "[" + userid + "] 记录不存在");
                return result;
            }

            if (null != map.get("password") && !temmap.get("password").equals(map.get("password"))) {
                BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
                map.put("password", bCryptPasswordEncoder.encode(map.get("password") + ""));
            }

            temmap.putAll(map);

            int flag = proUserService.update(temmap);
            if (1 == flag) {
                // 更新redis缓存
                JwtUser jwtUser = jwtTokenUtils.getJwtUser(request);
                JwtUser jwtUserInfo = (JwtUser) jwtUserDetailsService.loadUserById(userid);
                jwtUserInfo.getUser().remove("password");
                jwtUser.setUser(jwtUserInfo.getUser());
                jwtTokenUtils.setJwtUser(jwtUser);

                result.put(DATASUCCESS, SUCCESSFLAG);
                result.put(DATARECORD, handleUserInfo(jwtUser.getUser()));
                result.put(DATAMESSAGE, UPDATE_SUCCESS_MSG);
            } else {
                result.put(DATASUCCESS, ERRORFLAG);
                result.put(DATAMESSAGE, UPDATE_FAIL_MSG);
            }

        } catch (Exception e) {
            result.put(DATASUCCESS, ERRORFLAG);
            result.put(DATAMESSAGE, UPDATE_FAIL_MSG + e.getMessage());
        }
        return result;
    }


    /**
     * 获取登陆用户信息
     *
     * @param map
     * @return
     */
    @PostMapping(value = "/urlValidate")
    @ApiOperation(value = "根据-{用户名和url}-获取api权限信息", notes = "成功返回:{\"success\":true,\"message\",\"登陆成功\"},失败返回:{\"success\":false,\"message\",\"数据失败\"}")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "body", name = "url", value = "访问api地址", required = true, dataTypeClass = String.class)
    })
    @OperateLog(name = "获取api权限信息", value = "query", type = "PC")
    public Map<String, Object> urlvalidate(HttpServletRequest request, HttpServletResponse response, @RequestBody Map<String, Object> map) {
        Map<String, Object> result = new HashMap<String, Object>();
        String username = map.get("username") + "";
        String url = map.get("url") + "";
        try {

            boolean role_flag = false;
            //临时方案，跨账号未授权菜单访问服务鉴权通过角色菜单控制整个类粗粒度，不到方法级别。
            if (username != null) {
                //System.err.println("----------------url---------------------"+url);
                if (url.indexOf("/currentUser") != -1 || url.indexOf("/attachmentfile") != -1) {
                    role_flag = true;
                } else {
                    JwtUser userDetails = (JwtUser) this.jwtUserDetailsService.loadUserByUsername(username);

                    //System.err.println("----------------userDetails.getAuthorities()---------------------"+userDetails.getAuthorities());
                    HashMap<String, Collection<ConfigAttribute>> url_roles_map = WebRestURLSecurityMetadataSource.map;

                    for (Map.Entry<String, Collection<ConfigAttribute>> entry : url_roles_map.entrySet()) {
                        //System.err.println(entry.getKey() + ":" + entry.getValue());
                        String temp[] = entry.getKey().toString().split("/");
                        //System.out.println(temp.length);
                        if (null != temp && temp.length == 2) continue;
                        if (null != temp && temp.length > 2 && url.indexOf(temp[1]) != -1) {
                            //System.out.println( url+"----------------" + entry.getKey().toString());

                            for (ConfigAttribute c : entry.getValue()) {
                                String needRole = c.getAttribute();
                                for (GrantedAuthority ga : userDetails.getAuthorities()) {
                                    if (needRole.trim().equals(ga.getAuthority())) {
                                        role_flag = true;
                                    }
                                }

                            }

                        }
                    }
                }

            }
            result.put("role_flag", role_flag);
        } catch (Exception e) {
            result.put(DATASUCCESS, ERRORFLAG);
            result.put(DATAMESSAGE, "未授权或者未登录。" + e.getMessage());
            result.put(DATASTATUS, 401);
        }

        // 返回 token
        return result;
    }


    /**
     * 获取登陆用户信息
     * @param map
     * @return
     */
    @PostMapping(value = "/verifyToken")
    @ApiOperation(value="根据-{用户名和url}-获取api权限信息", notes="成功返回:{\"success\":true,\"message\",\"登陆成功\"},失败返回:{\"success\":false,\"message\",\"数据失败\"}")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType="body", name = "url", value = "访问api地址", required = true,  dataTypeClass = String.class)
    })
    @OperateLog(name = "获取api权限信息",value = "query",type = "PC")
    public Map<String, Object> verifyTokenForWeb(HttpServletRequest request , HttpServletResponse response, @RequestBody Map<String, Object> map){
        Map<String, Object> result = new HashMap<String, Object>();
        String uri = Convert.toStr(map.get("uri"), "");
        String systemid = Convert.toStr(map.get("systemid"), "");

        try {
            JwtUser jwtUser = SecurityUtils.getJwtUser();

            // 角色集合
            Set<Map<String, Object>> roles = new HashSet<>();
            boolean resAuth = false;
            if(StringUtils.isNotNull(jwtUser)) {
                // 查询用户角色
                roles = jwtUserDetailsService.getAuthoritiesNoGroupByUserId(jwtUser.getUserId());
                // uri为空不校验接口
                if(StringUtils.isNotEmpty(uri)) {
                    // uri 有值校验资源权限，并且系统id不能为空
                    if (StringUtils.isEmpty(systemid)) {
                        result.put(DATASUCCESS, SUCCESSFLAG);
                        result.put(DATAMESSAGE, "参数不能为空！");
                        return result;
                    }

                    /**
                     * 需要对资源验权
                     * 只对添加到资源权限管理的uri验权，其他uri默认放权
                     */
                    Map<String, Object> vars = new HashMap<>();
                    vars.put("systemid", systemid);
                    vars.put("res_type", "A");
                    vars.put("handler", uri);
                    List<Map<String,Object>> resAuthList = proResAuthService.getAllProResourceTreeByAuthID(vars);
                    if(StringUtils.isNotNull(resAuthList) && !resAuthList.isEmpty()) {
                        Set<String> authIds = roles.stream().map(role -> Convert.toStr(role.get("auth_id"), "")).collect(Collectors.toSet());
                        for (Map<String, Object> stringObjectMap : resAuthList) {
                            if(authIds.contains(Convert.toStr(stringObjectMap.get("auth_id"), ""))) {
                                resAuth = true;
                                break;
                            }
                        }
                    } else {
                        resAuth = true;
                    }
                } else {
                    resAuth = true;
                }
            }

            if(resAuth) {
                Map<String, Object> tempmap = new HashMap<>();
                tempmap.put("roles", roles.stream().map(role -> new HashMap<String, Object>() {
                    {
                        put("key", Convert.toStr(role.get("auth_id")));
                        put("value", Convert.toStr(role.get("auth_type")));
                        put("label", Convert.toStr(role.get("auth_name")));
                    }
                }).collect(Collectors.toList()));
                tempmap.put("user", jwtUser.getUser());

                result.put(DATASUCCESS, SUCCESSFLAG);
                result.put(DATAMESSAGE, "鉴权成功！");
                result.put(DATARECORD, tempmap);
            } else {
                result.put(DATASUCCESS, ERRORFLAG);
                result.put(DATAMESSAGE, "鉴权失败，您访问的资源没有权限！");
            }
        } catch (Exception e) {
            result.put(DATASUCCESS, ERRORFLAG);
            result.put(DATAMESSAGE, "未登陆或者登陆超时，无法访问该资源!" + e.getMessage());
            result.put(DATASTATUS, 203);
        }

        return result;
    }

    private Map<String,Object> handleUserInfo(Map<String,Object> userMap) {
        if (userMap == null|| userMap.isEmpty() ) {
            throw new RuntimeException("用户不存在");
        }

        String usernameCn = Convert.toStr(userMap.get("user_name_cn"), ""),
                deptName = Convert.toStr(userMap.get("dept_name"), ""),
                signature = Convert.toStr(userMap.get("sign"), ""),
                jobs = Convert.toStr(userMap.get("jobs"), ""),
                province = Convert.toStr(userMap.get("province"), ""),
                city = Convert.toStr(userMap.get("city"), ""),
                tags = Convert.toStr(userMap.get("tags"), ""),
                job = "";
        userMap.put("systemUser", ("0").equals(Convert.toStr(userMap.get("is_system_user"))));
        // 获取第一个岗位
        if(StringUtils.isNotEmpty(jobs)) {
            JSONArray jobsArray = JSONArray.parseArray(jobs);
            if(StringUtils.isNotNull(jobsArray) && jobsArray.size() > 0) {
                JSONObject jobObject = JSONObject.parseObject(JSONObject.toJSONString(jobsArray.get(0)));
                job = StringUtils.isNotEmpty(jobObject) ? "－" + jobObject.getString("label") : "";
            }
            userMap.put("jobs", jobsArray);
        }
        JSONObject provinceObject = StringUtils.isNotNull(province) ? JSONObject.parseObject(province) : new JSONObject();
        userMap.put("province", provinceObject);
        JSONObject cityObject = StringUtils.isNotNull(city) ? JSONObject.parseObject(city) : new JSONObject();
        userMap.put("city", cityObject);
        JSONArray tagsArray = StringUtils.isNotNull(tags) ? JSONArray.parseArray(tags) : new JSONArray();
        userMap.put("tags", tagsArray);
        userMap.put("group", StringUtils.format("飞利信{}{}", StringUtils.isNotEmpty(deptName) ? "－" + deptName : "", job));
        userMap.put("signature", signature);
        userMap.put("name", usernameCn);

        Map<String, Object> tempgeomapson = new HashMap<>();
        tempgeomapson.put("province", userMap.get("province"));
        tempgeomapson.put("city", userMap.get("city"));
        userMap.put("geographic", tempgeomapson);
        return userMap;
    }

}
