package com.ywt.mg.services;

import com.fasterxml.jackson.databind.JsonNode;
import com.ywt.mg.core.MGRightTypeDef;
import com.ywt.mg.core.utils.Checker;
import com.ywt.mg.core.utils.StringHelper;
import com.ywt.mg.core.utils.serializers.JsonSerializer;
import com.ywt.mg.domain.entities.*;
import com.ywt.mg.domain.ywtDrugEntities.Pharmacy;
import com.ywt.mg.domain.ywtDrugEntities.PharmacyRepository;
import com.ywt.mg.web.BaseResponse;
import com.ywt.mg.web.interceptors.WebAppContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author daiyihua
 */
@Service
public class AuthService {
    private static Logger logger = LoggerFactory.getLogger(AuthService.class);

    @Autowired
    private MGAdminsRepository mgAdminsRepository;

    @Autowired
    private MgAdminsRoleRelsRepository mgAdminsRoleRelsRepository;

    @Autowired
    private MgSysModuleRepository mgSysModuleRepository;

    @Autowired
    private MgRolesRepository mgRolesRepository;

    @Autowired
    private MgRightRelsRepository mgRightRelsRepository;

    @Autowired
    private MgMenusRepository mgMenusRepository;

    @Autowired
    private MgAdminMenusViewRepository mgAdminMenusViewRepository;

    @Autowired
    private MgAdminRightViewRepository mgAdminRightViewRepository;

    @Autowired
    private JdbcTemplate ywtCenterJdbcTemplate;

    @Autowired
    private MgRightCacheService mgRightCacheService;

    @Autowired
    private HospitalCacheService hospitalCacheService;

    @Autowired
    private PharmacyRepository pharmacyRepository;

    public int getCurrentAdminId() {
        int adminId = WebAppContext.current().getAdminId();
//        return adminId > 0 ? adminId : 1;
        return adminId ;
    }

    public MGAdmins getCurrentAdmin() {
        return mgAdminsRepository.findOne(getCurrentAdminId());
    }

    public int getCurrentHospitalId() {
        int hospitalId = WebAppContext.current().getHospitalId();
//        return hospitalId > 0 ? hospitalId : 41;
        return hospitalId;
    }

    public int getCurrentPharmacyId() {
        int pharmacyId = WebAppContext.current().getPharmacyId();
//        return pharmacyId > 0 ? pharmacyId : 1;
        return pharmacyId ;
    }

    public int getCurrentEnterpriseId() {
        int enterpriseId = WebAppContext.current().getEnterpriseId();
//        return enterpriseId > 0 ? enterpriseId : 1;
        return enterpriseId ;
    }


    public Hospital getCurrentHospitalByCache() {
        return hospitalCacheService.getHospitalMap().get(getCurrentHospitalId());
    }

    public String getCurrentAdminAccount() {
        MGAdmins mgAdmins = getCurrentAdmin();
        if (!Checker.isNone(mgAdmins)) {
            return Checker.getStringValue(mgAdmins.getAccount());
        }
        return "";
    }

    public String getCurrentAdminAccountById(int adminId) {
        MGAdmins mgAdmins = getAdminsByAdminId(adminId);
        if (!Checker.isNone(mgAdmins)) {
            return Checker.getStringValue(mgAdmins.getAccount());
        }
        return "";
    }

    /**
     * 判断是否有权限
     *
     * @param menuCode
     * @param rightCode
     * @param types     1 : 菜单；2 : 权限点, 4: 登录状态
     * @return
     */
    public boolean hasRight(String[] menuCode, String[] rightCode, int types) {

        int adminId = getCurrentAdminId();
        List<MgAdminRightView> mgAdminRightViewList = mgRightCacheService.getRightListHigh();
        List<MgAdminMenusView> mgAdminMenusViewList = mgRightCacheService.getMenusListHigh();

        if ((types & MGRightTypeDef.Logined) == MGRightTypeDef.Logined) {
            return true;
        }

        // 判断权限点
        if (!Checker.isNone(rightCode) && ((types & MGRightTypeDef.Right) == MGRightTypeDef.Right)) {
            List<MgAdminRightView> rightViewList = mgAdminRightViewList.stream().filter(p -> p.getAdminId().intValue() == adminId).collect(Collectors.toList());
            for (String codeStr : rightCode) {
                MgAdminRightView rightView = rightViewList.stream().filter(p -> Checker.getStringValue(p.getRightCode()).equals(codeStr)).findFirst().orElse(null);
                if (rightView != null) {
                    return true;
                }
            }
            return false;
        }

        // 判断菜单点
        if (!Checker.isNone(menuCode) && ((types & MGRightTypeDef.Menu) == MGRightTypeDef.Menu)) {
            List<MgAdminMenusView> menusViewList = mgAdminMenusViewList.stream().filter(p -> p.getAdminId().intValue() == adminId).collect(Collectors.toList());
            for (String codeStr : menuCode) {
                MgAdminMenusView rightView = menusViewList.stream().filter(p -> Checker.getStringValue(p.getMenuCode()).equals(codeStr)).findFirst().orElse(null);
                if (rightView != null) {
                    return true;
                }
            }
            return false;
        }

        return true;
    }

    /**
     * 较验用户名和密码，成功返回管理员信息
     *
     * @param userName
     * @param password
     * @return
     */
    public MGAdmins check(String userName, String password) {
        try {
            password = StringHelper.md5(password);
            MGAdmins mgAdmins = mgAdminsRepository.findByAccoutAndPwd(userName, password);
            return mgAdmins;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据adminid和appid得到相应的菜单
     * 思路：得到所有不重复的菜单ID（最外层的子菜单） --》得到所有相关的菜单 --》 分类分别得到子类集合和父类集合 --》 对子类集合和父类集合分别排序 --》
     *
     * @param adminid 用户ID
     * @param appId   系统的appId，对应mg_sys_module表的app_id字段
     * @return 多层的菜单集合
     */
    public List getMenusByAdminIdAndAppId(int adminid, String appId) {
        List list = new ArrayList<>();
        //此时得到的是所有的不重复的children菜单
        List<MgAdminMenusView> viewList = mgAdminMenusViewRepository.getAllByAdminIdAndAppId(adminid, appId);

        // 采取空间换时间的策略，先从数据库查找出所有的mgMenus，然后再过滤
        List<MgMenus> allMgMenus = mgMenusRepository.findAllMenus();
        //先找出所有不重复的菜单，包括父级菜单,这里面只存id （todo 在三层或三层以上的情况下需要用到）
        Set<Integer> allSetMenusIds = new HashSet();
        for (MgAdminMenusView menus : viewList) {
            allSetMenusIds = getParentMenus(menus.getMenuId(), allSetMenusIds, allMgMenus);
        }

        //将set转list
        List<Integer> allListMenusIds = new ArrayList<>(allSetMenusIds);

        if (!Checker.isNone(allListMenusIds)) {
            //找出所有的菜单，以及所有父级菜单
            List<MgMenus> mgMenusAllList = new ArrayList<>();
            List<MgMenus> parentMenusAllList = new ArrayList<>();
            // 遍历所有的菜单IDs
            for (Integer menuId : allListMenusIds) {
                MgMenus menus = allMgMenus.stream().filter(p -> p.getId().equals(menuId)).findFirst().orElse(null);
                if (Checker.isNone(menus)) {
                    continue;
                }
                mgMenusAllList.add(menus);
                // 得到所有父级菜单，如果这个对象的父级id不能找到，那就表示在这个集合里面的最上一级
                if (menus.getParentId().intValue() == 0) {
                    parentMenusAllList.add(menus);
                }
            }
            // 按特定字段进行排序
            mgMenusAllList = mgMenusAllList.stream().sorted(Comparator.comparing(MgMenus::getFlagValue)).collect(Collectors.toList());
            parentMenusAllList = parentMenusAllList.stream().sorted(Comparator.comparing(MgMenus::getFlagValue)).collect(Collectors.toList());

            // 遍历parentMenusAllList
            if (!Checker.isNone(parentMenusAllList)) {
                for (MgMenus menus : parentMenusAllList) {
                    Map<String, Object> set = getMenusTree(menus, mgMenusAllList);
                    list.add(set);
                }
            }
        }
        return list;
    }


    /**
     * 往上得到父级菜单(适用于多层)
     *
     * @param menuId         菜单ID
     * @param allSetMenusIds 菜单IDs的Set集合
     * @param allMgMenus     所有菜单对象的list集合
     * @return 菜单IDs的Set集合
     */
    private Set<Integer> getParentMenus(Integer menuId, Set<Integer> allSetMenusIds, List<MgMenus> allMgMenus) {
        MgMenus mgMenus = allMgMenus.stream().filter(p -> p.getId().equals(menuId)).findFirst().orElse(null);
        if (!Checker.isNone(mgMenus)) {
            allSetMenusIds.add(mgMenus.getId());
            //查找是否有还有父级
            allSetMenusIds = getParentMenus(mgMenus.getParentId(), allSetMenusIds, allMgMenus);
        }
        return allSetMenusIds;
    }


    /**
     * 遍历集合获取树
     *
     * @param menus
     * @param mgMenusAllList
     * @return
     */
    public Map getMenusTree(MgMenus menus, List<MgMenus> mgMenusAllList) {
        Map<String, Object> data = new HashMap<>(16);
        // 这里需要把层级关系找出来
        if (!Checker.isNone(menus)) {
            data.put("id", menus.getId());
            String name = menus.getMenuName();
            if (!Checker.isNone(menus.getAnotherName())) {
                name = menus.getAnotherName();
            }
            data.put("name", name);
            data.put("path", menus.getUrl());

            if (menus.getParentId() == 0) {
                data.put("icon", "database");
            }
            //循环不断的调用自己
            List<MgMenus> lv2List = mgMenusAllList.stream().filter(p -> p.getParentId().equals(menus.getId())).collect(Collectors.toList());
            if (!Checker.isNone(lv2List)) {
                List children = new LinkedList();
                for (MgMenus m2 : lv2List) {
                    Map<String, Object> childrenMap = getMenusTree(m2, mgMenusAllList);
                    children.add(childrenMap);
                }
                data.put("children", children);
            }
        }
        return data;
    }



    /**
     * 根据管理员ID获取可登录的系统模块
     *
     * @param adminId
     * @return
     */
    public List<MgSysModule> getAdminSysModule(int adminId) {
        try {
            String sql = "SELECT DISTINCT sm.*\n" +
                    "FROM mg_admin a, mg_roles r, mg_admin_role_rels arr, mg_sys_module sm\n" +
                    "WHERE a.id = arr.admin_id and arr.role_id = r.id and r.sys_module_id = sm.id and a.id = ?";
            List<MgSysModule> mgSysModules = ywtCenterJdbcTemplate.query(sql, new Object[]{adminId}, new BeanPropertyRowMapper<>(MgSysModule.class));
            return mgSysModules;
        } catch (Exception e) {
            logger.error("getAdminSysModule({}): {}", adminId, e.getMessage());
        }
        return new LinkedList<>();
    }


    /**
     * 修改密码
     *
     * @param adminId
     * @param oldPassword
     * @param newPassword
     * @return
     */
    public BaseResponse changePassword(int adminId, String oldPassword, String newPassword) {
        BaseResponse base = new BaseResponse();
        MGAdmins mgAdmins = mgAdminsRepository.findOne(adminId);
        if (!Checker.isNone(mgAdmins)) {
            oldPassword = StringHelper.md5(oldPassword);
            if (!mgAdmins.getPassword().equals(oldPassword)) {
                return base.failed(BaseResponse.PARAMETER_ERROR, "原密码输入不正确");
            }
            newPassword = StringHelper.md5(newPassword);
            mgAdmins.setPassword(newPassword);
            mgAdminsRepository.save(mgAdmins);
            return base.succeed("处理成功");
        }
        return base.failed(BaseResponse.PARAMETER_ERROR, "找不到用户");
    }


    /**
     * 根据用户对象得到用户名
     *
     * @param mgAdmins {@link MGAdmins}
     * @return account
     */
    public String getAdminAccount(MGAdmins mgAdmins) {
        if (!Checker.isNone(mgAdmins)) {
            return Checker.getStringValue(mgAdmins.getAccount());
        }
        return "";
    }

    /**
     * 根据登录的用户得到药房ID
     *
     * @param mgAdmins {@link MGAdmins}
     * @return pharmacyId
     */
    public int getPharmacyId(MGAdmins mgAdmins) {
        JsonNode jsonNode = JsonSerializer.readToNode(mgAdmins.getData());
        if (jsonNode.get("pharmacyId") != null) {
            return Checker.parseInt(jsonNode.get("pharmacyId").asText("0"));
        }
        return 0;
    }


    /**
     * 根据用户ID得到对象
     *
     * @param adminId
     * @return
     */
    public MGAdmins getAdminsByAdminId(int adminId) {
        return mgAdminsRepository.findOne(adminId);
    }

    /**
     * 得到当前用户的昵称
     *
     * @return string
     */
    public String getCurrentAdminNickName() {
        MGAdmins mgAdmins = getCurrentAdmin();
        if (!Checker.isNone(mgAdmins)) {
            return Checker.getStringValue(mgAdmins.getNickName());
        }
        return "";
    }



    /**
     * 这里校验单个地址，采取直接从数据库读取的方式（没有采用缓存），适合调用频率低的方法使用
     * @param rightCode 权限点
     * @return true: 有该权限， false：没有权限
     */
    public boolean checkRight(int adminId, String rightCode) {
        List<MgAdminRightView> rightViewList = mgAdminRightViewRepository.getAllByAdminId(adminId);
        MgAdminRightView rightView = rightViewList.stream().filter(p -> Checker.getStringValue(p.getRightCode()).equals(rightCode)).findFirst().orElse(null);
        if (rightView != null) {
            return true;
        }
        return false;
    }
    public boolean checkRight(String rightCode) {
        int adminId = getCurrentAdminId();
        return checkRight(adminId, rightCode);
    }

    /**
     * 这里校验单个地址，采取直接从数据库读取的方式（采用缓存），适合调用频率高的方法使用
     * @param rightCode 权限点
     * @return true: 有该权限， false：没有权限
     */
    public boolean checkCacheRight(int adminId, String rightCode) {
        List<MgAdminRightView> mgAdminRightViewList = mgRightCacheService.getRightListHigh();
        List<MgAdminRightView> rightViewList = mgAdminRightViewList.stream().filter(p -> p.getAdminId().intValue() == adminId).collect(Collectors.toList());
        MgAdminRightView rightView = rightViewList.stream().filter(p -> Checker.getStringValue(p.getRightCode()).equals(rightCode)).findFirst().orElse(null);
        if (rightView != null) {
            return true;
        }
        return false;
    }
    public boolean checkCacheRight(String rightCode) {
        int adminId = getCurrentAdminId();
        return checkRight(adminId, rightCode);
    }
    public Pharmacy getCurrentPharmacy() {
        int currentPharmacyId = getCurrentPharmacyId();
        return pharmacyRepository.findOne(currentPharmacyId);
    }
}
