package com.eactive.eai.rms.data.entity.man.menu;
	
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.annotations.QueryHints;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.eactive.eai.rms.common.acl.menu.ui.MenuUISearch;
import com.eactive.eai.rms.common.util.CommonConstants;
import com.eactive.eai.rms.data.entity.man.role.QRole;
import com.eactive.eai.rms.data.entity.man.role.QRoleMenuAuth;
import com.eactive.eai.rms.data.entity.man.user.QUserRole;
import com.eactive.eai.rms.data.jpa.AbstractEMSDataSerivce;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.Tuple;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQuery;

@Service
@Transactional(transactionManager = "transactionManagerForEMS")
public class MenuService extends AbstractEMSDataSerivce<Menu, String, MenuRepository> {

	public Page<Menu> findAll(Pageable pageable, MenuUISearch menuUISearch) {
		QMenu qMenu = QMenu.menu;

		BooleanExpression predicate = qMenu.parentMenuId
				.ne("0")
				.and(qMenu.menuId.ne("0"))
				.and(qMenu.menuUrl.ne("NAN"))
				.and(qMenu.appCode.ne("BAP"));

		if (!StringUtils.isBlank(menuUISearch.getSearchName())) {
			predicate = predicate.and(qMenu.menuName.containsIgnoreCase(menuUISearch.getSearchName()));
		}
		if (!StringUtils.isBlank(menuUISearch.getSearchTblName())) {
			predicate = predicate.and(qMenu.appPath.containsIgnoreCase(menuUISearch.getSearchTblName()));
		}

		return repository.findAll(predicate, pageable);
	}

	public String getPosition(String menuId) {
		Menu menu = repository.findById(menuId).orElse(null);
		return (menu == null) ? "" : buildPositionString(menu, 0);
	}

	public String getMenuName(String menuId) {
		return repository.findById(menuId).map(Menu::getMenuName).orElse(null);
	}

	private String buildPositionString(Menu menu, int level) {
		if (menu == null || level > 2) {
			return "";
		}
		StringBuilder position = new StringBuilder();
		String parentPosition = buildPositionString(menu.getParentMenu(), level + 1);

		if (!parentPosition.isEmpty()) {
			position.append(parentPosition).append(" > ");
		}
		position.append(menu.getMenuName());
		return position.toString();
	}

   public String getUrlAuth(String userId, String url, String serviceType) {
		QUserRole qUserRole = QUserRole.userRole;
		QRole qRole = QRole.role;
		QRoleMenuAuth qRoleMenuAuth = QRoleMenuAuth.roleMenuAuth;
		QMenu qMenu = QMenu.menu;

		return getJPAQueryFactory()
				.select(qRoleMenuAuth.auth.max())
				.from(qUserRole)
				.join(qRole)
				.on(qUserRole.id.roleId.eq(qRole.roleId))
				.join(qRoleMenuAuth)
				.on(qRole.roleId.eq(qRoleMenuAuth.id.roleId))
				.join(qMenu)
				.on(qRoleMenuAuth.id.menuId.eq(qMenu.menuId))
            .where( qUserRole.id.userId.eq(userId),
                  qMenu.menuUrl.like(url.concat("%")), qRole.useYn.eq("Y"),
                  qRoleMenuAuth.auth.in("R", "W"),
                  serviceType != null ? qRoleMenuAuth.id.serviceType.eq(serviceType) : qRoleMenuAuth.id.serviceType.isNull()
            )
				.setHint(QueryHints.CACHEABLE, true)
				.fetchOne();
	}

   public List<Menu> getLeftMenuList(String menuId, List<String> roleIds, List<String> appCodes, String serviceType) {
		QMenu qMenu = QMenu.menu;
		QRoleMenuAuth qRoleMenuAuth = QRoleMenuAuth.roleMenuAuth;

		JPAQuery<String> roleMenuAuthSubQuery = getJPAQueryFactory()
				.select(qRoleMenuAuth.id.menuId)
				.from(qRoleMenuAuth)
            .where(qRoleMenuAuth.auth.in("R", "W"), qRoleMenuAuth.id.roleId.in(roleIds), qRoleMenuAuth.id.serviceType.eq(serviceType));

		return getJPAQueryFactory()
				.select(qMenu)
				.from(qMenu)
				.where(qMenu.parentMenuId.eq(menuId), qMenu.appCode.in(appCodes), qMenu.useYn.eq("Y"),
						qMenu.displayYn.eq("Y"), qMenu.menuId.in(roleMenuAuthSubQuery))
				.orderBy(qMenu.sortOrder.asc())
				.setHint(QueryHints.CACHEABLE, true)
				.fetch();
	}

	public boolean isMenuExistInService(List<String> appCodes, String menuId) {
		QMenu qMenu = QMenu.menu;
		BooleanExpression predicate = qMenu.appCode.in(appCodes).and(qMenu.menuId.eq(menuId));

		return repository.findOne(predicate).isPresent();
	}

   public List<Map<String, Object>> getMenuList(List<String> roleIds, List<String> appCodes, String serviceType) {
		QMenu qMenu = QMenu.menu;
		QMenu qParentMenu = new QMenu("qParentMenu");
		QMenu qGrandParentMenu = new QMenu("qGrandParentMenu");
		QMenu qGreatGrandParentMenu = new QMenu("qGreatGrandParentMenu");

		List<Tuple> menus = getJPAQueryFactory()
				.select(qMenu, qParentMenu, qGrandParentMenu, qGreatGrandParentMenu)
				.from(qMenu)
				.leftJoin(qMenu.parentMenu, qParentMenu)
				.on(qParentMenu.menuId.eq(qMenu.parentMenuId))
				.leftJoin(qParentMenu.parentMenu, qGrandParentMenu)
				.on(qGrandParentMenu.menuId.eq(qParentMenu.parentMenuId))
				.leftJoin(qGrandParentMenu.parentMenu, qGreatGrandParentMenu)
				.on(qGreatGrandParentMenu.menuId.eq(qGrandParentMenu.parentMenuId))
            .where(menuQueryConditions(qMenu, qParentMenu, qGrandParentMenu, qGreatGrandParentMenu, roleIds, appCodes, serviceType))
				.orderBy(qGreatGrandParentMenu.sortOrder.asc(), qGrandParentMenu.sortOrder.asc(),
						qParentMenu.sortOrder.asc(), qMenu.sortOrder.asc())
				.setHint(QueryHints.CACHEABLE, true)
				.fetch();

		return menus.stream().map(this::convertToMap).collect(Collectors.toList());
	}

   private Predicate menuQueryConditions(
         QMenu qMenu, QMenu qParentMenu, QMenu qGrandParentMenu, QMenu qGreatGrandParentMenu,
         List<String> roleIds, List<String> appCodes, String serviceType) {

		String serverType = CommonConstants.DB_MANAGE_TYPE_D;
		String lang = LocaleContextHolder.getLocale().getLanguage();
		BooleanBuilder predicate = new BooleanBuilder();

		// lang - ko/en check
		if (StringUtils.isBlank(lang) || StringUtils.equalsIgnoreCase(lang, "ko")) {
			lang = "ROOT";
		} else {
			lang = "ROOT_" + lang.toUpperCase();
		}

		if (StringUtils.isNotBlank(lang)) {
			predicate.and(qGreatGrandParentMenu.menuName.eq(lang));
		}

		// all Menu - displayYn, useYn = 'Y' check
		Stream
				.of(qMenu, qParentMenu, qGrandParentMenu, qGreatGrandParentMenu)
				.forEach(m -> predicate.and(m.displayYn.eq("Y").and(m.useYn.eq("Y"))));

		predicate.and(qMenu.menuUrl.ne("NAN"));

      // QRoleMenuAuth - roleIds & "R", "W" & serviceType
		predicate
				.and(qMenu.menuId
						.in(getJPAQueryFactory()
								.select(QRoleMenuAuth.roleMenuAuth.id.menuId)
								.from(QRoleMenuAuth.roleMenuAuth)
								.where(QRoleMenuAuth.roleMenuAuth.id.roleId.in(roleIds),
                              QRoleMenuAuth.roleMenuAuth.auth.in("R", "W"),
                              QRoleMenuAuth.roleMenuAuth.id.serviceType.eq(serviceType))));
		// serverType
		predicate
				.and(qGreatGrandParentMenu.parentMenuId
						.eq(getJPAQueryFactory()
								.select(qGreatGrandParentMenu.menuId)
								.from(qGreatGrandParentMenu)
								.where(qGreatGrandParentMenu.parentMenuId.eq(serverType))));

		// appCodes
		predicate
				.and(qMenu.menuId
						.in(getJPAQueryFactory()
								.select(qMenu.menuId)
								.from(qMenu)
								.where(qMenu.appCode.in(appCodes))));



		return predicate;
	}

	private Map<String, Object> convertToMap(Tuple tuple) {
		Menu menu = tuple.get(QMenu.menu);
		Menu parentMenu = tuple.get(new QMenu("qParentMenu"));
		Menu grandParentMenu = tuple.get(new QMenu("qGrandParentMenu"));

		Map<String, Object> map = new HashMap<>();
		map.put("LEV2_ID", grandParentMenu.getMenuId());
		map.put("LEV2_NAME", grandParentMenu.getMenuName());
		map.put("LEV2_IMG", grandParentMenu.getMenuImage());
		map.put("LEV2_URL", grandParentMenu.getMenuUrl());
		map.put("LEV3_ID", parentMenu.getMenuId());
		map.put("LEV3_NAME", parentMenu.getMenuName());
		map.put("LEV3_IMG", parentMenu.getMenuImage());
		map.put("LEV3_URL", parentMenu.getMenuUrl());

		if (menu != null) {
			map.put("LEV4_ID", menu.getMenuId());
			map.put("LEV4_NAME", menu.getMenuName());
			map.put("LEV4_IMG", menu.getMenuImage());
			map.put("LEV4_URL", menu.getMenuUrl());
			map.put("APPCODE", menu.getAppCode());
		}
		return map;
	}

}