package com.qy.sso.bamboocloud.filter.authentication;

import com.kingdee.bos.Context;
import com.kingdee.bos.dao.IObjectPK;
import com.kingdee.bos.dao.IObjectValue;
import com.kingdee.bos.sso.client.util.FilterUtil;
import com.kingdee.eas.base.common.DCInfo;
import com.kingdee.eas.base.usermonitor.IUserMonitor;
import com.kingdee.eas.base.usermonitor.UserMonitorFactory;
import com.kingdee.eas.base.usermonitor.UserMonitorSessionInfo;
import com.kingdee.eas.cp.eip.sso.util.LoginUtil;
import com.kingdee.shr.guilogin.web.wechart.WeChartUtil;
import com.kingdee.util.StringUtils;
import org.jasig.cas.client.Protocol;
import org.jasig.cas.client.authentication.*;
import org.jasig.cas.client.configuration.ConfigurationKeys;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.validation.Assertion;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Locale;

/**
 * @Description 竹云单点认证过滤器
 * @Date 2024/9/23 15:45
 * @Created by Heyuan
 */
public class ZYPortalAuthenticationFilter extends AbstractCasFilter {
    private boolean getServerNameFromRequest;
    private boolean getServerLoginUrlFromRequest;
    private String serverLoginUrl;
    private String casServerLoginUrl;
    private String clientId;
    private String responseType;


    private boolean renew;
    private boolean gateway;
    private String method;
    private GatewayResolver gatewayStorage;
    private AuthenticationRedirectStrategy authenticationRedirectStrategy;
    //private static final Map<String, Class<? extends UrlPatternMatcherStrategy>> PATTERN_MATCHER_TYPES = new HashMap();

    public ZYPortalAuthenticationFilter() {
        this(Protocol.ZY);
    }


    protected ZYPortalAuthenticationFilter(Protocol protocol) {
        super(protocol);
        this.renew = false;
        this.gateway = false;
        this.gatewayStorage = new DefaultGatewayResolverImpl();
        this.authenticationRedirectStrategy = new DefaultAuthenticationRedirectStrategy();
    }

    /**
     * 初始化过滤器
     */
    public void init() {
        super.init();
        String message = String.format("one of %s and %s must not be null.",
                ConfigurationKeys.CAS_SERVER_LOGIN_URL.getName(),
                ConfigurationKeys.CAS_SERVER_URL_PREFIX.getName());
        //
        CommonUtils.assertNotNull(this.casServerLoginUrl, message);
    }


    public final void doFilter(ServletRequest servletRequest,
                               ServletResponse servletResponse,
                               FilterChain filterChain) throws IOException, ServletException {
        //1.判断使用是否有登录会话，校验登录人和会话登录人一致性
        //2.没有登录会话，判断是否有参数code，有就调用获取token方法和获取用户信息，登录
        //3.没有code,就返回竹云登录页面
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        if (this.isRequestUrlExcluded(request)) {
            //请求路径不在匹配规则路径内,忽略
            this.logger.debug("Request is ignored.");
            filterChain.doFilter(request, response);
        } else {
            //是否重置会话
            boolean sessionRenew = false;
            String username = request.getParameter("kdacccode");
            if (StringUtils.isEmpty(username)) {
                username = request.getParameter("username");
            }
            if (StringUtils.isEmpty(username)) {
                username = request.getParameter("usern");
            }
            this.logger.debug("username  \"{}\"", username);
            if (username != null) {
                HttpSession session = request.getSession(false);
                //会话
                Assertion assertion = session != null ? (Assertion) session.getAttribute("_const_cas_assertion_") : null;
                if (assertion != null) {
                    username = URLDecoder.decode(username, "UTF-8");
                    //当前登录用户人
                    String ctxUsername = assertion.getPrincipal().getName();
                    if (ctxUsername != null && !ctxUsername.equalsIgnoreCase(username)) {
                        //用户名不等于当前用户,会话失效
                        session.setAttribute("_const_cas_assertion_", (Object) null);
                        session.setAttribute("KD_PORTAL_IS_INIT_CONTEXT", (Object) null);
                        session.invalidate();
                        sessionRenew = true;
                    }
                    if (session.getAttribute("userMonitorSessionInfo") != null) {
                        UserMonitorSessionInfo userMonitorSessionInfo =
                                (UserMonitorSessionInfo) session
                                        .getAttribute("userMonitorSessionInfo");
                        try {
                            IUserMonitor iUserMonitor = UserMonitorFactory.getRemoteInstance();
                            IObjectValue info = iUserMonitor.getValue(
                                    userMonitorSessionInfo.getId());
                            if (info == null) {
                                session.setAttribute("_const_cas_assertion_", (Object) null);
                                session.setAttribute("KD_PORTAL_IS_INIT_CONTEXT", (Object) null);
                                session.invalidate();
                                sessionRenew = true;
                            }
                        } catch (Exception var16) {
                            this.logger.error("checkUserBeTicked err!", var16);
                        }
                    }
                }
            }
            this.setEncodeServiceUrl(true);
            //构建登录地址
            this.constructLoginUrl(servletRequest, sessionRenew);
            //构建服务ip和端口地址
            String constructServerName = this.constructServerName(servletRequest);
            HttpSession session = request.getSession(false);
            Assertion assertion = session != null ? (Assertion) session.getAttribute("_const_cas_assertion_") : null;
            String serviceUrl;
            String constructServiceUrl;
            if (assertion != null) {
                filterChain.doFilter(request, response);
            } else {
                //跳转竹云认证
                serviceUrl = com.kingdee.bos.sso.client.util.CommonUtils.constructServiceUrl(request, response, null, this.getServerName(constructServerName), this.getProtocol().getServiceParameterName(), this.getProtocol().getArtifactParameterName(), true);
                constructServiceUrl = this.retrieveTicketFromRequest(request);
                boolean wasGatewayed = this.gateway && this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
                if (!CommonUtils.isNotBlank(constructServiceUrl) && !wasGatewayed) {
                    this.logger.debug("no ticket and no assertion found");
                    String modifiedServiceUrl;
                    if (this.gateway) {
                        this.logger.debug("setting gateway attribute in session");
                        modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
                    } else {
                        modifiedServiceUrl = serviceUrl;
                    }
                    this.logger.debug("Constructed service url: {}", modifiedServiceUrl);
                    //生成重定向地址
                    String urlToRedirectTo = constructRedirectUrl(this.casServerLoginUrl, this.getProtocol().getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway, this.method);
                    this.logger.debug("redirecting to \"{}\"", urlToRedirectTo);
                    this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo);
                } else {
                    filterChain.doFilter(request, response);
                }
            }
        }
    }


    public String constructRedirectUrl(String casServerLoginUrl, String serviceParameterName, String serviceUrl, boolean renew, boolean gateway, String method) {
        return casServerLoginUrl + (casServerLoginUrl.contains("?") ? "&" : "?") + serviceParameterName + "=" + serviceUrl + (renew ? "&renew=true" : "") + (gateway ? "&gateway=true" : "") + (method != null ? "&method=" + method : "");
    }


    protected static Context initRPC(String dataCenter, String easSessionId) {
        DCInfo dcInfo = WeChartUtil.getDCInfo(dataCenter);
        Context ctx = new Context((IObjectPK) null, dcInfo.getSolutionName(), dcInfo.getNumber(), (Locale) null);
        LoginUtil.initRpcConfig(ctx, easSessionId);
        return ctx;
    }

    /**
     * 获取服务地址 ip:port
     *
     * @param constructServerName
     * @return
     */
    private String getServerName(String constructServerName) {
        if (constructServerName != null) {
            return constructServerName;
        } else {
            try {
                Class clazz = this.getClass().getSuperclass();
                Field field = clazz.getDeclaredField("serverName");
                field.setAccessible(true);
                return (String) field.get(this);
            } catch (Exception var4) {
                return null;
            }
        }
    }

    /**
     * 构建服务地址   ip:port
     *
     * @param request
     * @return
     */
    private String constructServerName(ServletRequest request) {
        //是否从请求中获取服务地址
        if (this.getServerNameFromRequest) {
            StringBuilder serverName = new StringBuilder();
            serverName.append(request.getServerName());
            serverName.append(":");
            serverName.append(request.getServerPort());
            this.setServerName(serverName.toString());
            return serverName.toString();
        } else {
            return null;
        }
    }

    /**
     * 构建登录地址
     *
     * @param request
     * @param sessionRenew 是否重置会话
     */
    private void constructLoginUrl(ServletRequest request, boolean sessionRenew) {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        //获取httpSession,为空时不创建
        HttpSession session = httpRequest.getSession(false);
        Assertion assertion = session != null ? (Assertion) session.getAttribute("_const_cas_assertion_") : null;

        try {
            URL contextURL = new URL(this.serverLoginUrl);
            URL destURL = contextURL;
            //
            if (this.getServerLoginUrlFromRequest) {
                destURL = new URL(request.getScheme(), request.getServerName(), request.getServerPort(), contextURL.getFile());
            }

            if (assertion != null) {
                this.setCasServerLoginUrl(destURL.toString());
            } else {
                String renew = request.getParameter("renew");
                if (sessionRenew) {
                    renew = "true";
                }
                StringBuilder optionParam = new StringBuilder();
                optionParam.append("?client_id=").append(this.clientId)
                        .append("&response_type=").append(this.responseType)
                        .append("&state=").append(System.currentTimeMillis());
                this.setCasServerLoginUrl(destURL.toString() + optionParam);
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }

    public final void setRenew(boolean renew) {
        this.renew = renew;
    }

    public final void setGateway(boolean gateway) {
        this.gateway = gateway;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public final void setCasServerUrlPrefix(String casServerUrlPrefix) {
        this.setCasServerLoginUrl(CommonUtils.addTrailingSlash(casServerUrlPrefix) + "login");
    }

    public final void setCasServerLoginUrl(String casServerLoginUrl) {
        this.casServerLoginUrl = casServerLoginUrl;
    }

    public final void setGatewayStorage(GatewayResolver gatewayStorage) {
        this.gatewayStorage = gatewayStorage;
    }

    private boolean isRequestUrlExcluded(HttpServletRequest request) throws MalformedURLException {
        return FilterUtil.isUnFilter(request);
    }

    public final void setIgnoreUrlPatternMatcherStrategyClass(UrlPatternMatcherStrategy ignoreUrlPatternMatcherStrategyClass) {
    }

    public final void setServerLoginUrl(String serverLoginUrl) {
        this.serverLoginUrl = serverLoginUrl;
        this.setCasServerLoginUrl(this.serverLoginUrl);
    }

    public final void setGetServerNameFromRequest(boolean getServerNameFromRequest) {
        this.getServerNameFromRequest = getServerNameFromRequest;
    }

    public final void setGetServerLoginUrlFromRequest(boolean getServerLoginUrlFromRequest) {
        this.getServerLoginUrlFromRequest = getServerLoginUrlFromRequest;
    }

    public void setClientId(String clientId) {
        this.clientId = clientId;
    }

    public void setResponseType(String responseType) {
        this.responseType = responseType;
    }

    public String getClientId() {
        return clientId;
    }

//    static {
//        PATTERN_MATCHER_TYPES.put("CONTAINS", ContainsPatternUrlPatternMatcherStrategy.class);
//        PATTERN_MATCHER_TYPES.put("REGEX", RegexUrlPatternMatcherStrategy.class);
//        PATTERN_MATCHER_TYPES.put("FULL_REGEX", EntireRegionRegexUrlPatternMatcherStrategy.class);
//        PATTERN_MATCHER_TYPES.put("EXACT", ExactUrlPatternMatcherStrategy.class);
//    }
}