package cn.myapps.runtime.menu.controller;

import cn.myapps.authtime.privilege.operation.model.OperationVO;
import cn.myapps.common.ModelSuffix;
import cn.myapps.common.auth.IUser;
import cn.myapps.common.controller.Resource;
import cn.myapps.common.data.ParamsTable;
import cn.myapps.common.model.resource.ResourceVO;
import cn.myapps.common.model.view.AbstractView;
import cn.myapps.common.model.view.constant.ViewConstant;
import cn.myapps.common.util.StringUtil;
import cn.myapps.designtime.common.cache.DesignTimeSerializableCache;
import cn.myapps.designtime.common.service.DesignTimeServiceManager;
import cn.myapps.designtime.permission.PermissionUtil;
import cn.myapps.designtime.resource.service.ResourceDesignTimeService;
import cn.myapps.designtime.view.service.ViewDesignTimeService;
import cn.myapps.runtime.common.controller.AbstractRuntimeController;
import cn.myapps.runtime.dynaform.document.ejb.Document;
import com.bcxin.components.TenantContext;
import com.bcxin.saas.core.Constants;
import com.bcxin.saas.core.enums.OrganizationLevel;
import com.bcxin.saas.core.exceptions.SaasRetryableException;
import com.bcxin.saas.core.exceptions.SaasUnAuthorizeException;
import com.bcxin.saas.core.utils.RetryUtil;
import com.bcxin.saas.domains.dtos.RbacQueryDTO;
import com.bcxin.saas.domains.readers.RbacDbReader;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.*;

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

@Api(tags = "菜单执行模块")
@Component
@RequestMapping(path = "/api/runtime/{applicationId}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class MenuController extends AbstractRuntimeController {

	private static final Logger LOG = LoggerFactory.getLogger(MenuController.class);

	private ResourceDesignTimeService process;
	private RbacDbReader rbacDbReader;

	public MenuController(){
		try {
			process = DesignTimeServiceManager.resourceDesignTimeService();
		} catch (Exception e) {
			LOG.error(e.getMessage());
		}
	}

	@Autowired
	public void setRbacDbReader(RbacDbReader rbacDbReader) {
		this.rbacDbReader = rbacDbReader;
	}

	/**
	 * 获取菜单
	 * @param applicationId 软件id
	 * @return
	 * @throws Exception
	 */
	@GetMapping(path = "/menus")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "获取菜单", notes = "获取菜单")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
			@ApiImplicitParam(name = "isMobile",value = "是否移动端",required = false,paramType = "query",dataType = "string")
	})
	public Resource menuOfApplication(@PathVariable String applicationId,
									  @RequestParam(required=false) boolean isMobile, HttpServletResponse res ) throws Exception {
		StringBuilder sb = new StringBuilder(String.format("二级缓存情况:%s", DesignTimeSerializableCache.getIsEnabledRefreshCached()));
		StopWatch stopWatch = new StopWatch();
		stopWatch.start("开始跟踪;获取用户信息");
		res.setCharacterEncoding("UTF-8");
		IUser user = getUser();
		stopWatch.stop();
		sb.append(String.format("开始-用户:" + stopWatch.getTotalTimeMillis()));
		//公司是否使用高级版，默认否
		boolean advancedLevel=false;
		//如果公司层级是高级，则走自定义角色授权
		TenantContext.EmployeeOrgBasicInfo userInfo = TenantContext.getInstance().getCurrentEmployee();
		if(userInfo==null) {
			throw new SaasUnAuthorizeException("系统异常，当前用户无效");
		}

		advancedLevel = userInfo.getOrganizationLevels().contains(OrganizationLevel.Premium);

		stopWatch.start("开始获取菜单");
		List<ResourceVO> tempmenus = new ArrayList<>();
		if (isMobile) {
			tempmenus = process.list(applicationId, ModelSuffix.MOBILE_MENU_PATH_SUFFIX, ModelSuffix.MOBILE_MENU_FILE_SUFFIX, null);
		} else {
			tempmenus = process.menulist(applicationId);
		}
		stopWatch.stop();
		sb.append(String.format(
				"完成菜单获取: %s;advancedLevel=%s;applicationId=%s;isMobile=%s;menus=%s;",
				stopWatch.getTotalTimeMillis(),
				advancedLevel,
				applicationId,
				isMobile,
				CollectionUtils.isEmpty(tempmenus)?"EMPTY":tempmenus.stream().map(ii->ii.getId()).collect(Collectors.joining(","))
				)
		);

		List<ResourceVO> menus = new ArrayList<ResourceVO>();
		ViewDesignTimeService vProcess = DesignTimeServiceManager.viewDesignTimeService();

		stopWatch.start("开始轮询菜单");
		String[] userRoleIds = null;
		for (ResourceVO resourceVO : tempmenus) {
			ResourceVO menu = ResourceVO.clone(resourceVO);
			if (!menu.getIsMobile().equals(String.valueOf(isMobile))) {
				continue;
			}
			if (isMobile && ResourceVO.ACTIONTYPE_VIEW.equals(menu.getLinkType())) {
				AbstractView view = vProcess.doView(menu.getActionContent());
				if (view != null) {
					if (view.getIntValue() == ViewConstant.VIEW_TYPE_COLLAPSIBLE ||
							view.getIntValue() == ViewConstant.VIEW_TYPE_GANTT) {
						continue;
					}
				}
			}
			if (ResourceVO.SHOW_TYPE_FLOW_CENTER == menu.getShowType()) {
				// 只在流程中心显示
				continue;
			}
			if (ResourceVO.SHOW_TYPE_BOTH == menu.getStatus()) {
				//状态失效
				continue;
			}

			if(!advancedLevel){
				// 公共的菜单不做权限校验
				if (!user.isDomainAdmin() && !ResourceVO.PERMISSION_TYPE_PUBLIC.equals(menu.getPermissionType())) {
					// 检查菜单的权限
					String id = menu.getId();
					if (userRoleIds == null) {
						String userRole = user.getRolelist(applicationId);
						userRoleIds = userRole == null ? null : userRole.split(",");
					}

					boolean permission = PermissionUtil.check(userRoleIds, id, id, OperationVO.MENU_INVISIBLE);
					if (!permission) {
						continue;
					}
				}
			}

			//先判断菜单的链接类型是不是为null,如果是，就不可打开
			if (!StringUtil.isBlank(menu.getLinkType())) {
				if (menu.getLinkType().equals("07")) {
					RetryUtil.execute(() -> {
						//脚本连接
						ParamsTable paramsTable = getParams();
						paramsTable.setParameter("application", applicationId);
						String url = menu.toScriptUrl(new Document(), paramsTable, user);
						if (!StringUtil.isBlank(url)) {
							menu.setActionContent(url);
						} else {
							LOG.error("应用({})的组件{}的菜单地址执行失败(result={})", applicationId, menu.getId(), url);

							throw new SaasRetryableException(String.format("应用(%s)的菜单(%s)不应该为空", applicationId, menu.getId()));
						}
						return true;
					}, 10);
				}
			}

			// 多语言设置
			// 设置应用ID，会在MultiLanguageFilter里用到
			String multiLangTag = menu.getName();
			String multiLanguageLabel = menu.getMultiLanguageLabel();
			if (!StringUtil.isBlank(multiLanguageLabel)) {
				// 因为在Filter里中文会因为乱码问题导致解析错误，暂未找到解决方案，因此urlencode将中文进行编码
				String labelHex = URLEncoder.encode(multiLanguageLabel);
				String nameHex = URLEncoder.encode(multiLangTag);
				multiLangTag = "{*[" + labelHex + "^" + nameHex + "^true]*}";
			}
			menu.setDescription(multiLangTag);

			menu.setNewIco(menu.getIco());
			if (!StringUtil.isBlank(menu.getShowtotalrow()) && Boolean.valueOf(menu.getShowtotalrow())) {
				menu.setTotalRow(getTotalRowByResource(menu));
			}
			menus.add(menu);
		}
		stopWatch.stop();
		sb.append(String.format("完成菜单信息:" + stopWatch.getTotalTimeMillis()));

		stopWatch.start("开始轮询菜单2:");
		String totalRow = "";
		ResourceUtil resourceUtil = new ResourceUtil();
		List<ResourceVO> parentMenu = new ArrayList<ResourceVO>();
		for (Iterator<ResourceVO> iterator = menus.iterator(); iterator.hasNext(); ) {
			ResourceVO menu = (ResourceVO) iterator.next();
			if (!StringUtil.isBlank(menu.getShowtotalrow()) && menu.getShowtotalrow().equals("true")) {
				int count = Integer.valueOf(resourceUtil.getTotalRowByResourceid(menu.getId(), request));
				if (count > 99) {
					totalRow = "99+";
				} else {
					totalRow = String.valueOf(count);
				}
				menu.setTotalRow(totalRow);
			}
			if (StringUtil.isBlank(menu.getSuperior())) {
				parentMenu.add(menu);
			}
		}

		List<ResourceVO> treeMenus = getTreeMenus(parentMenu, menus, parentMenu);
		stopWatch.stop();
		sb.append(String.format("完成菜单信息2:%s;menus=%s;" ,stopWatch.getTotalTimeMillis(),(CollectionUtils.isEmpty(menus)?"EMPTY":menus.stream().map(ix->ix.getId()).collect(Collectors.joining(",")))));

		//智能人事和机构用户管理应用菜单根据是否为集团版特殊处理
		if(Constants.getOaApplicationId().equals(applicationId) || Constants.getUserManagementApplicationId().equals(applicationId)){
			if (advancedLevel){
				treeMenus = treeMenus.stream()
						.filter(ix->!Arrays.asList(Constants.getPremiumOaAppMenusExclude()).contains(ix.getId()))
						.collect(Collectors.toList());
			}else{
				treeMenus = treeMenus.stream()
						.filter(ix->!Arrays.asList(Constants.getBasicOaAppMenusExclude()).contains(ix.getId()))
						.collect(Collectors.toList());
			}
		}
		//高级按照自定义角色来过滤
		if(advancedLevel){
			RbacQueryDTO rbacQueryDTO = RbacQueryDTO.create(user.getId(),user.getDomainid(),user.isDomainAdmin(),isMobile,applicationId);
			Set<String> permitOptions = rbacDbReader.getPermitOptions(rbacQueryDTO).getOptions();
			if(CollectionUtils.isEmpty(permitOptions)){
				treeMenus.clear();
			}else{
				treeMenus = treeMenus.stream().filter(ix->permitOptions.contains(ix.getId())).collect(Collectors.toList());
			}
		}

		Resource r = success("ok", treeMenus);
		r.setErrmsg(String.format("d:[%s]:%s", DesignTimeSerializableCache.getNodeLabel(), sb));
		return r;
	}


	/**
	 * 获取菜单树结构
	 * @param result
	 * 		树结构菜单数据
	 * @param menus
	 * 		所有菜单数据
	 * @param parentMenus
	 * 		父级菜单数据
	 * @return
	 */
	private List<ResourceVO> getTreeMenus(List<ResourceVO> result,List<ResourceVO> menus,List<ResourceVO> parentMenus) {
		for (Iterator<ResourceVO> iterator = parentMenus.iterator(); iterator.hasNext();) {
			List<ResourceVO> parentList = new ArrayList<ResourceVO>();
			ResourceVO parentMenu = (ResourceVO) iterator.next();
			for (Iterator<ResourceVO> iterator2 = menus.iterator(); iterator2.hasNext();) {
				ResourceVO childMenu = (ResourceVO) iterator2.next();
				if (parentMenu.getId().equals(childMenu.getSuperior())) {
					parentList.add(childMenu);
				}
			}
			parentMenu.setChildren(parentList);

			if (parentList.isEmpty()) {
				continue;
			}

			getTreeMenus(result, menus,parentList);
		}

		return result;
	}

	private String getTotalRowByResource(ResourceVO resource){
		try {
			if(resource.getLinkType().equals(ResourceVO.LinkType.VIEW.getCode())){
				if(resource.getActionContent()==null || resource.getActionContent().equals("")){
					return "0";
				}
				ViewDesignTimeService viewProcess = DesignTimeServiceManager.viewDesignTimeService();
				AbstractView view = viewProcess.doView(resource.getActionContent());
				if (view != null) {
					ParamsTable params = getParams();
					String queryString = resource.getQueryString();
					if(!StringUtil.isBlank(queryString)){
						JSONArray jsonArray = cn.myapps.util.json.JsonTmpUtil.fromObject(queryString);
						for(Object obj : jsonArray){
							JSONObject jsonObject = (JSONObject)obj;
							String paramKey = jsonObject.getString("paramKey");
							String paramValue = jsonObject.getString("paramValue");
							params.setParameter(paramKey, paramValue);
						}
					}
					long count = view.getViewTypeImpl().countViewDatas(params, getUser(), new Document());
					return String.valueOf(count);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "0";
	}
}
