package com.eactive.eai.rms.common.acl.role;

import com.eactive.eai.rms.common.base.BaseService;
import com.eactive.eai.rms.common.context.MonitoringContext;
import com.eactive.eai.rms.common.datasource.DataSourceType;
import com.eactive.eai.rms.common.datasource.DataSourceTypeManager;
import com.eactive.eai.rms.common.util.StringUtils;
import com.eactive.eai.rms.data.entity.man.menu.Menu;
import com.eactive.eai.rms.data.entity.man.menu.MenuService;
import com.eactive.eai.rms.data.entity.man.role.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

import static com.eactive.eai.rms.common.context.MonitoringContext.MENU_RENDER_ADDITIONAL_SERVICES;

@Service
@Transactional(transactionManager = "transactionManagerForEMS")
public class RoleManService extends BaseService {

	@Autowired
	private RoleService roleService;

	@Autowired
	private RoleMenuAuthService roleMenuAuthService;

	@Autowired
	private RoleUIMapper roleUIMapper;

	@Autowired
	private MenuService menuService;

   @Autowired @Qualifier("monitoringContext")
   private MonitoringContext monitoringContext;

	public Page<RoleUI> selectList(Pageable pageable, RoleUI roleUI) {
		Page<Role> page = roleService.findAll(pageable, roleUI);
		return page.map(roleUIMapper::toVo);
	}

	public RoleUI selectDetail(String roleId) {
		Role role = roleService.getById(roleId);
		return roleUIMapper.toVo(role);
	}

	public void insert(RoleUI roleUI) {
		Role role = roleUIMapper.toEntity(roleUI);
		roleService.save(role);
	}

	public void update(RoleUI roleUI) {
		Role role = roleService.getById(roleUI.getRoleId());
		roleUIMapper.updateToEntity(roleUI, role);
		roleService.save(role);
	}

	public void delete(String roleId) {
		roleService.deleteById(roleId);
	}

	/* START selectRoleMenuTree */
	public List<RoleMenuTreeUI> selectRoleMenuTree(String roleId) {
		Menu rootMenu = menuService.getById("ROOT_DEV");
      List<Menu> rootMenus = new ArrayList<>(rootMenu.getChildMenus());

      Map<String, List<AuthByService>> authsByServiceByMenuId =
            StreamSupport.stream(roleMenuAuthService.findAllByRoleId(roleId).spliterator(), false)
                  .collect(Collectors.groupingBy(
                        roleMenuAuth -> roleMenuAuth.getId().getMenuId(),
                        Collectors.mapping(
                              roleMenuAuth -> new AuthByService(roleMenuAuth.getId().getServiceType(), roleMenuAuth.getAuth()),
                              Collectors.toList()
                        )
                  ));

      Set<String> additionalRenderServices =   Arrays.stream(monitoringContext.getStringProperty(MENU_RENDER_ADDITIONAL_SERVICES, "").split(","))
            .map(String::trim)
            .filter(StringUtils::isNotBlank)
            .collect(Collectors.toSet());

		List<RoleMenuTreeUI> menuTreeUI = new ArrayList<>();
      rootMenus.forEach(menu -> buildMenuTree(menu, menuTreeUI, 0, authsByServiceByMenuId, additionalRenderServices));

		return menuTreeUI;
	}

   private void buildMenuTree(Menu menu, List<RoleMenuTreeUI> menuTreeUI, int level, Map<String, List<AuthByService>> authsByServiceByMenuId, Set<String> additionalRenderServices) {
		if (menu == null || !"Y".equals(menu.getUseYn())) {
			return;
		}

      List<String> renderTarget;
      if (additionalRenderServices.contains(menu.getAppCode())) {
         renderTarget = new ArrayList<String>() {{ add(menu.getAppCode()); }};
      } else {
         renderTarget = DataSourceTypeManager.getDynamicDataSourceTypes().stream().filter(dt -> dt.getRender().contains(menu.getAppCode())).map(DataSourceType::getName).collect(Collectors.toList());
      }

      List<AuthByService> authsByService = Optional.ofNullable(authsByServiceByMenuId.get(menu.getId())).orElse(new ArrayList<>());

      menuTreeUI.add(new RoleMenuTreeUI(menu, level, renderTarget, authsByService));
      menu.getChildMenus().forEach(childMenu -> buildMenuTree(childMenu, menuTreeUI, level + 1, authsByServiceByMenuId, additionalRenderServices));
	}
	/* END selectRoleMenuTree */

   public void transactionRoleMenu(String roleId, List<Map<String, String>> roleMenuList) {
		Role role = roleService.getById(roleId);
		role.getRoleMenuAuths().clear();

      for (Map<String, String> roleMenuMap : roleMenuList) {

         String auth = roleMenuMap.get("value");

         if (StringUtils.equals(auth, "N")) {
            continue;
			}

         // map.get("name") like "radio_0101001_MCC"
         String[] name = roleMenuMap.get("name").split("_");

         RoleMenuAuth roleMenuAuth = new RoleMenuAuth();
         roleMenuAuth.setId(new RoleMenuAuthId(roleId, name[1], name[2]));
         roleMenuAuth.setAuth(auth);
         role.getRoleMenuAuths().add(roleMenuAuth);
		}

      roleService.save(role);
	}

	// 역할 권한 상세 결과 가져오기(DB)
	public String getRoleScreenId(List<String> roleIds) {
		List<String> menuId = roleService.getRoleScreenId(roleIds);
		return menuId.stream().findFirst().orElse("");
	}

   public List<String> getServiceTypesForMenuRendering() {

      List<String> serviceTypesForMenuRendering =
            DataSourceTypeManager.getDynamicDataSourceTypes().stream().map(DataSourceType::getName).collect(Collectors.toList());

      serviceTypesForMenuRendering.addAll(
            Arrays.asList(monitoringContext.getStringProperty(MENU_RENDER_ADDITIONAL_SERVICES, "").split(",")));

      return serviceTypesForMenuRendering.stream()
            .map(String::trim)
            .filter(StringUtils::isNotBlank)
            .collect(Collectors.toList());
   }

   public List<DataSourceType> selectRoleServiceTypeList(List<String> roleIds) {
      return DataSourceTypeManager.getDynamicDataSourceTypes().stream()
            .filter(dt -> roleMenuAuthService.findAllServiceTypeByRoleIds(roleIds).contains(dt.getName()))
            .collect(Collectors.toList());
   }

}
