package com.platform.modules.live.aspect;

import com.baomidou.mybatisplus.service.IService;
import com.platform.common.utils.R;
import com.platform.common.utils.ShiroUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.ApplicationContext;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * @author TONG
 * 2019/11/15 15:38
 **/
public abstract class BaseTenantAspect {

    private final ApplicationContext applicationContext;

    private String TENANT_METHOD_NAME = "getTenantId";

    public BaseTenantAspect(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    public Object doTenantValid(ProceedingJoinPoint pjp) throws Throwable {
        try {
            if (ShiroUtils.getUserEntity() == null || ShiroUtils.getUserEntity().getTenantId() == null) {
                return errorResponse(403, "无权限操作此记录");
            }
            Long tenantId = ShiroUtils.getUserEntity().getTenantId();
            Signature signature = pjp.getSignature();

            MethodSignature ms = (MethodSignature) signature;
            Method method = ms.getMethod();

            TenantAspectRegistry registry = buildTenantAspectRegistry(method);
            String value = null;
            Class<?> clazz = null;
            String methodName = null;
            if (registry != null) {
                value = registry.getValue();
                clazz = registry.getTenantTableClazz();
                methodName = registry.getMethod();
            }
            if (!StringUtils.isEmpty(value) && !StringUtils.isEmpty(methodName)) {

                ServletRequestAttributes sras = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                HttpServletRequest request = sras.getRequest();
                String[] ids = request.getParameterValues(value);

                if (ids == null || ids.length <= 0) {
                    Parameter[] parameters = method.getParameters();
                    if (parameters != null && parameters.length == 1) {
                        Parameter requestBodyParam = parameters[0];
                        RequestBody requestBodyAnno = requestBodyParam.getAnnotation(RequestBody.class);
                        if (requestBodyAnno != null) {
                            Object requestBodyArg = pjp.getArgs()[0];
                            Method getMethod = requestBodyArg.getClass().getDeclaredMethod("get" + value.substring(0, 1).toUpperCase() + value.substring(1));

                            Object feildValue = getMethod.invoke(requestBodyArg);
                            if (feildValue != null) {
                                Class<?> returnType = getMethod.getReturnType();
                                if (returnType.isArray()) {
                                    ids = (String[]) feildValue;
                                } else {

                                    ids = StringUtils.delimitedListToStringArray(feildValue.toString(), ",");
                                }
                            }
                        }
                    }
                }
                if (ids != null && ids.length > 0) {

                    Object tenantTableService = applicationContext.getBean(clazz);
                    Method selectMethod = tenantTableService.getClass().getDeclaredMethod(methodName, registry.getMethodParamClass());
                    for (String id : ids) {
                        String[] queryIds = StringUtils.delimitedListToStringArray(id, ",");
                        for (String queryId : queryIds) {
                            Object entity = selectMethod.invoke(tenantTableService, queryId);
                            if (entity == null) {
                                return errorResponse(404, "未找到或非当前机构记录");
                            }
                            Method getTenantIdMethod = entity.getClass().getDeclaredMethod(TENANT_METHOD_NAME);
                            Object entityTenantId = getTenantIdMethod.invoke(entity);
                            if (!tenantId.equals(entityTenantId)) {
                                return errorResponse(403, "无权限操作此记录");
                            }
                        }
                    }
                }
                return pjp.proceed();
            }
        } catch (Exception e) {
        	e.printStackTrace();
            return errorResponse(500, "租户权限处理异常错误 " + e.getMessage());
        }
        return errorResponse(500, "租户权限处理校验错误");
    }

    public abstract TenantAspectRegistry buildTenantAspectRegistry(Method method);

    public Object errorResponse(int errorCode, String errorMsg) {
        return R.error(errorCode, errorMsg);
    }

    public static class TenantAspectRegistry {
        private String value;
        private Class<? extends IService> tenantTableClazz;
        private String method;
        private Class<? extends Serializable> methodParamClass;

        public TenantAspectRegistry(String value, Class<? extends IService> tenantTableClazz, String method, Class<? extends Serializable> methodParamClass) {
            this.value = value;
            this.tenantTableClazz = tenantTableClazz;
            this.method = method;
            this.methodParamClass = methodParamClass;
        }

        public String getValue() {
            return value;
        }

        public Class<? extends IService> getTenantTableClazz() {
            return tenantTableClazz;
        }

        public String getMethod() {
            return method;
        }

        public Class<? extends Serializable> getMethodParamClass() {
            return methodParamClass;
        }
    }
}
