package com.zcxk.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.zcxk.dao.LoginLogMapper;
import com.zcxk.dao.UserMapper;
import com.zcxk.dto.UserAuthDto;
import com.zcxk.dto.UserDto;
import com.zcxk.entity.LoginLog;
import com.zcxk.entity.MenuEntity;
import com.zcxk.entity.Org;
import com.zcxk.entity.User;
import com.zcxk.exception.ServiceException;
import com.zcxk.model.LoginUser;
import com.zcxk.model.Permission;
import com.zcxk.model.ProgramItem;
import com.zcxk.model.ResultStatus;
import com.zcxk.security.mobile.MobileLoginAuthenticationToken;
import com.zcxk.service.UserService;
import com.zcxk.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import sun.misc.BASE64Encoder;

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

/**
 * 用户(User表)服务实现类
 *
 * @author makejava
 * @since 2020-10-21 15:23:52
 */
@Service("userService")
@Slf4j
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private ConsumerTokenServices consumerTokenServices;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private HttpServletRequest request;
    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    private AuthorizationServerTokenServices authorizationServerTokenServices;
    @Autowired
    private LoginLogMapper loginLogMapper;
    @Autowired
    private TokenStore tokenStore;
    @Value("${iot.url}")
    private String iotUrl;

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    @Override
    public User selectById(Integer id) {
        return this.userMapper.selectById(id);
    }


    /**
     * 查询所有
     *
     * @return 实例对象的集合
     */
    @Override
    public List<User> selectAll() {
        return this.userMapper.selectAll();
    }

    /**
     * 根据条件查询
     *
     * @return 实例对象的集合
     */
    @Override
    public List<User> selectList(User user) {
        return this.userMapper.selectList(user);
    }

    /**
     * 新增数据
     *
     * @param user 实例对象
     * @return 实例对象
     */
    @Override
    public int insert(User user) {
        return this.userMapper.insert(user);
    }

    /**
     * 批量新增
     *
     * @param users 实例对象的集合
     * @return 生效的条数
     */
    @Override
    public int batchInsert(List<User> users) {
        return this.userMapper.batchInsert(users);
    }

    /**
     * 修改数据
     *
     * @param user 实例对象
     * @return 实例对象
     */
    @Override
    public int update(User user) {

        return this.userMapper.update(user);
    }

    /**
     * 通过主键删除数据
     *
     * @param id 主键
     * @return 是否成功
     */
    @Override
    public int deleteById(Integer id) {
        return this.userMapper.deleteById(id);
    }

    /**
     * 查询总数据数
     *
     * @return 数据总数
     */
    @Override
    public int count() {
        return this.userMapper.count();
    }

    /**
     * 分页查询
     *
     * @return 对象列表
     */
    @Override
    public IPage<User> selectPage(User user, IPage<User> page) {
        return this.userMapper.selectPage(page, user);
    }

    @Override
    public User findUser(User user) {
        List<User> users = userMapper.selectList(user);
        if(users!=null&&users.size()>0){
            return users.get(0);
        }
        return null;
    }


    public void offline(String clientId, Integer userId, Integer status) {
        Collection<OAuth2AccessToken> c = tokenStore.findTokensByClientIdAndUserName(clientId, String.valueOf(userId));
        System.out.println(c.size());
        for (OAuth2AccessToken oAuth2AccessToken : c) {
            String accessToken = oAuth2AccessToken.getValue();
            String tokenKey = "offlineStatus:" + accessToken;
            redisUtil.setExpire(tokenKey.getBytes(), ByteArrayUtils.objectToBytes(status).get(), 1800);//15分钟过期
            tokenStore.removeAccessToken(oAuth2AccessToken);
        }
    }

    @Override
    public String getRangeCode(String appId) {
        UserAuthDto userAuthDto = new UserAuthDto();
        LoginUser currentUser = UserUtil.getCurrentUser();
        Integer id = currentUser.getId();
        String appSecret=userMapper.getAppSecret(appId);
        String uniqueUserID=userMapper.getUniqueUserID(id);
        if(uniqueUserID==null){
            uniqueUserID=UUID.randomUUID().toString();
            userMapper.updateUniqueUserID(id,uniqueUserID);
        }
        Random random = new Random();
        String code="";


        if(uniqueUserID!=null){
            userAuthDto.setUniqId(uniqueUserID);
            userAuthDto.setMobile(currentUser.getPhoneNumber());
            for (int i=0;i<6;i++)
            {
                code+=random.nextInt(10);
            }
            String key=code+"_"+appId+"_"+appSecret;
            redisUtil.set(key, JSONObject.toJSONString(userAuthDto));
            redisUtil.setExpire(key,300);
        }

        return code;
    }

    @Override
    public String getUniqId(String appId, String appSecret, String code) {
        String key=code+"_"+appId+"_"+appSecret;
        return redisUtil.get(key);
    }

    @Override
    public UserAuthDto getUniqId(String appId, String encryptValue) {
        String appSecret = userMapper.getAppSecret(appId);
        if (StringUtils.isEmpty(appSecret)){
            throw new ServiceException(ResultStatus.USER_AUTH_APPID_NOT_FOUND);
        }
        String key = null;
        try {
            // 验证加密的内容是否正确 32
            encryptValue=encryptValue.replaceAll(" ","+");
            key = DESUtil.decrypt(encryptValue,appSecret);
            // 截取里面的时间
            String date = key.substring(key.lastIndexOf("_") + 1, key.length());
            // 验证在5分钟之内是否是有效的加密

        } catch (Exception e) {
            throw new ServiceException(ResultStatus.USER_AUTH_ENCRYPT_VALUE_VERIFY_ERROR);
        }
        String value = redisUtil.get(key.substring(0, key.lastIndexOf("_")));
        if (StringUtils.isEmpty(value)){
            throw new ServiceException(ResultStatus.USER_AUTH_UNIQID_OR_CODE_ISNULL);
        }
        return JSONObject.parseObject(value,UserAuthDto.class);
    }


    @Override
    public User chooseUser(User user) {

        LoginUser currentUser = UserUtil.getCurrentUser();

        if(currentUser.getTenantId()!=null){
            user.setPhone(currentUser.getPhoneNumber());
            user.setTenantId(currentUser.getTenantId());
        }
        user=findUser(user);
        UserDto userDto=new UserDto();
        userDto.setPhone(user.getPhone());


       List<ProgramItem>programItems=new ArrayList<>();

        Permission permission= findPermission(user);

        boolean returnFlag=false;
        if((permission!=null&&permission.getRoleState()==0)){
            user.setEnableState("0");
            returnFlag=true;

        }
        if((user.getTenantState()!=null&&user.getTenantState()==0)){
            user.setEnableState("0");
            returnFlag=true;
        }
        if((user.getCompanyOrgState()!=null&&user.getCompanyOrgState()==0)){
            user.setEnableState("0");
            returnFlag=true;
        }
        if((user.getDepartmentOrgState()!=null&&user.getDepartmentOrgState()==0)){
            user.setEnableState("0");
            returnFlag=true;
        }
        if("0".equals(user.getEnableState())){
         returnFlag=true;
        }
        if(returnFlag){
            return user;
        }
        offline("smart-city-v2",user.getId(),1);


        getDataPermission(permission.getPermissionType(),user,programItems);
        //角色必须是ROLE_开头，其余的是用户功能权限
        /*
        Integer permissionType = permissionList.get(0).getPermissionType();
        List<ProgramItem> programItemList = newArrayList();
        //获取数据权限

        //分配功能权限
        if (permissionList.size() > 0) {
            for (Permission permission : permissionList) {
                if(!StringUtils.equals("",permission.getPermission())){
                    GrantedAuthority authority1 = new SimpleGrantedAuthority(permission.getPermission());
                    grantedAuthorities.add(authority1);
                }
            }
        }

        */


        Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
        OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) oAuth2Authentication.getPrincipal();
        //放置系统级菜单对应用户appid
        loginUser.setAppIds(findUserFirstMenu(user.getTenantId(),user.getId(),user.getUserType()));
        loginUser.setProgramItemList(programItems);
        loginUser.setName(user.getUsername());
        loginUser.setId(user.getId());
        loginUser.setUsername(user.getUsername());
        loginUser.setCompanyId(user.getCompanyOrgId());
        loginUser.setDepartmentId(user.getDeptOrgId());
        loginUser.setTenantId(user.getTenantId());
        loginUser.setType(user.getUserType());
        loginUser.setPhoneNumber(user.getPhone());
        loginUser.setAuthorities(grantedAuthorities);
        loginUser.setPermissonType(permission.getPermissionType());
        loginUser.setRoleId(permission.getRoleId());
        loginUser.setLoginToken(UUID.randomUUID().toString());

        MobileLoginAuthenticationToken authenticationTokenReslut = new MobileLoginAuthenticationToken(loginUser,oAuth2Authentication.getCredentials(),loginUser.getAuthorities());
        authenticationTokenReslut.setDetails(oAuth2Authentication.getDetails());
        OAuth2Authentication oAuth2AuthenticationNew = new OAuth2Authentication(oAuth2Authentication.getOAuth2Request(), authenticationTokenReslut);

        OAuth2AccessToken accessToken = authorizationServerTokenServices.createAccessToken(oAuth2AuthenticationNew);
        //可以加一个映射表
        String iotToken=getLoginIotToken(user.getIotPhoneNumber());
        if(iotToken!=null){
            String iotTokenKey = "iotToken:" + accessToken.getValue();
            redisUtil.set(iotTokenKey,iotToken);
            redisUtil.setExpire(iotTokenKey,60*30);
        }
        insertLoginLog(loginUser,"登录");
        user.setToken(accessToken.getValue());
        return user;
    }
    public String getLoginIotToken(String account){
        log.info("begin IotService login account={}",account);
        String url = iotUrl + "/api/integration/auth/login";
        RestTemplate restTemplate = new RestTemplate();
        if(account==null){
            return null;
        }
        MultiValueMap<String, String> map= new LinkedMultiValueMap<String, String>();
        map.add("source","rmcp");
        map.add("code",new BASE64Encoder().encode(account.getBytes()));
        HttpHeaders headers = new HttpHeaders();
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map,headers);
        ResponseEntity<JSONObject> responseEntity  = restTemplate.postForEntity(url, request, JSONObject.class);
        log.info("responseEntity ={}", JSONObject.toJSONString(responseEntity));
        if(responseEntity !=null && responseEntity.getStatusCode() == HttpStatus.OK){
            JSONObject jsonObject =  responseEntity.getBody();
            if(jsonObject != null){
                if(jsonObject.getInteger("status")==0){
                    String token = jsonObject.getString("data");
                    log.info("IotService getLoginIotToken success res={}",token);
                    return token;
                }else {
                    log.info("IotService getLoginIotToken fail");
                    return null;

                }
            }else {
                return null;
            }
        }

        return null;
    }
    private void getDataPermission(int permissionType,User user, List<ProgramItem> programItemList){
        ProgramItem defaultProgramItem=new ProgramItem();
        defaultProgramItem.setOrgCompanyId(-999);
        defaultProgramItem.setOrgDeparmtmentId(-999);
        programItemList.add(defaultProgramItem);
        if(permissionType==1){
            ProgramItem programItem=new ProgramItem();
            programItem.setOrgId(user.getCompanyOrgId());
            programItem.setOrgCompanyId(user.getCompanyOrgId());
            programItemList.add(programItem);
        }else if(permissionType==2){
            Map<Integer, List<Org>> allOrgs = getAllOrgs(null,user.getTenantId());
            ProgramItem programItem=new ProgramItem();
            programItem.setOrgId(user.getCompanyOrgId());
            programItem.setOrgCompanyId(user.getCompanyOrgId());
            getOrg(user.getCompanyOrgId(),allOrgs,programItemList);
            programItemList.add(programItem);

        }else if(permissionType==3&&user.getDeptOrgId()!=null){
            ProgramItem programItem=new ProgramItem();
            programItem.setOrgId(user.getDeptOrgId());
            programItem.setOrgDeparmtmentId(user.getDeptOrgId());
            programItemList.add(programItem);
        }else if(permissionType==4&&user.getDeptOrgId()!=null) {
            ProgramItem programItem=new ProgramItem();
            programItem.setOrgId(user.getDeptOrgId());
            programItem.setOrgDeparmtmentId(user.getDeptOrgId());
            Map<Integer, List<Org>> allOrgs = getAllOrgs("department",user.getTenantId());
            getOrg(user.getDeptOrgId(),allOrgs,programItemList);
            programItemList.add(programItem);
        }else if(permissionType==5){
            programItemList.addAll( userMapper.findOrgRole(user));
            programItemList.forEach(programItem -> {
                if("company".equals(programItem.getNewType())){
                    programItem.setOrgCompanyId(programItem.getOrgId());
                }else if("department".equals(programItem.getNewType())){
                    programItem.setOrgDeparmtmentId(programItem.getOrgId());
                }
                programItem.setCode(user.getTenantId());
            });
        }


    }
    private    Map<Integer, List<Org>> getAllOrgs(String orgType,String tenantId){
        Org org=new Org();
        org.setOrgType(orgType);
        org.setTenantId(tenantId);
        List<Org> orgs = userMapper.findOrgs(org);
        Map<Integer, List<Org>> collect = orgs.stream().
                collect(Collectors.groupingBy(d -> d.getParentOrgId()));
        return collect;
    };
    public void getOrg(Integer id, Map<Integer, List<Org>> collect,List<ProgramItem>programItemList){
        List<Org> orgs = collect.get(id);
        if(orgs!=null){
            orgs.forEach(org->{
                ProgramItem item=new ProgramItem();
                item.setOrgId(org.getId());
                if("company".equals(org.getOrgType())){
                    item.setOrgCompanyId(org.getId());
                }else if("department".equals(org.getOrgType())){
                    item.setOrgDeparmtmentId(org.getId());
                }
                item.setCode(org.getTenantId());
                programItemList.add(item);
                getOrg(org.getId(),collect,programItemList);

            });
        }
    }
    private Permission findPermission(User user){
        List<Permission> permissionList = userMapper.findPermission(user);
        if(permissionList.size()==0){
            return null;
        }

        return permissionList.get(0);
    }
    private List<String> findUserFirstMenu(String tenantId,  Integer userId,String userType){
        List<MenuEntity> menuEntities = userMapper.findUserFirstMenu(tenantId, userId);
        switch (userType) {
//			case "1"://超级管理员查询所有系统菜单
//				menuEntities = menuMapper.findAllFirstMenu();
//				break;
//			case "2"://普通用户按角色查询租户菜单
//				menuEntities = menuMapper.findUserFirstMenu(currentUser.getTenantId(),userId);
//				break;
            case "-9999"://超级管理员查询所有系统菜单
                menuEntities = userMapper.findAllFirstMenu();
                break;
            case "-999"://租户管理员查询租户菜单
                menuEntities = userMapper.findTenantFirstMenu(tenantId);
                break;
            default:	//其他租户用户暂时不区分类型
                menuEntities = userMapper.findUserFirstMenu(tenantId,userId);
                break;
        }
        List<String>appIds=new ArrayList<>();

        menuEntities.forEach(menuEntity -> {
            appIds.add(menuEntity.getAppId());
        });
        return appIds;
    }
    @Override
    public boolean logOut(String token) {

        boolean flag=consumerTokenServices.revokeToken(token);
        insertLoginLog(UserUtil.getCurrentUser(),"登出");
        return flag;
    }
    /**
     * 新增注册数据
     * @author yjy
     * @param user 实例对象
     * @return 影响行数
     */
    @Override
    public int insertRegister(User user) {
        return this.userMapper.insertRegister(user);
    }
    private void insertLoginLog(LoginUser loginUser,String type){
        LoginLog loginLog=new LoginLog();
        loginLog.setName(loginUser.getName());
        loginLog.setPhone(loginUser.getPhoneNumber());
        loginLog.setTenantId(loginUser.getTenantId());
        loginLog.setCompanyId(loginUser.getCompanyId());
        loginLog.setDepartmentId(loginUser.getDepartmentId());
        loginLog.setType(type);
        loginLog.setCreateTime(new Date());
        loginLog.setLoginIp(Util.getIpAddr(request));
        loginLogMapper.insert(loginLog);
    }
}