package com.skr.mdm.authentication.user.qr;

import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.symmetric.SM4;
import com.skr.mdm.enums.MdmEncryptEnum;
import com.skr.mdm.util.SmUtils;
import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author nfq
 * @date 2020/9/16 16:23
 */
public class UsernameAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
    public static final String IMEI = "imei";
    public static final String OAID = "oaid";
    public static final String UUID = "uuid";
    public static final String LOGIN_METHOD = "loginMethod";
    public static final String COMPANY_ID = "companyId";

    public static final String SECRET_KEY = "secretKey";


    private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
    private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
    private String imeiParameter = IMEI;
    private final String oaidParameter = OAID;
    private final String uuidParameter = UUID;
    private String loginMethodParameter = LOGIN_METHOD;
    private final String companyIdParameter = COMPANY_ID;
    private final String secretKeyParameter = SECRET_KEY;
    private boolean postOnly = true;

    private SM4 sm4 = null;

    private String mdmEncrypt;

    public UsernameAuthenticationFilter() {
        super(new AntPathRequestMatcher("/user/login", HttpMethod.POST.name()));
    }


    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        if (postOnly && !request.getMethod().equals(HttpMethod.POST.name())) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }
        if (MdmEncryptEnum.SM.getMdmEncrypt().equals(mdmEncrypt)) {
            obtainSecretKey(request);
        }
        String username = obtainUsername(request);
        String password = obtainPassword(request);
        String imei = obtainImei(request);
        String oaid = obtainOaid(request);
        String uuid = obtainUuid(request);
        String loginMethod = obtainLoginMethod(request);
        String companyId = obtainCompanyId(request);

        if (username == null) {
            username = "";
        }

        if (password == null) {
            password = "";
        }

        if (imei == null) {
            imei = "";
        }

        if (oaid == null) {
            oaid = "";
        }

        if (uuid == null) {
            uuid = "";
        }

        if (loginMethod == null) {
            loginMethod = "";
        }

        if (companyId == null) {
            companyId = "";
        }
        username = username.trim();

        UsernameAuthenticationToken authRequest = new UsernameAuthenticationToken(username, password, imei, oaid, uuid, loginMethod, companyId);

        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);

        return this.getAuthenticationManager().authenticate(authRequest);
    }

    @Nullable
    protected String obtainSecretKey(HttpServletRequest request) {
        String secretKey = request.getHeader(secretKeyParameter);
        SM2 sm2 = SmUtils.sm2Sign();
        byte[] secretKeyBytes = sm2.decrypt(secretKey, KeyType.PrivateKey);
        sm4 = SmUtil.sm4(secretKeyBytes);
        return request.getHeader(secretKeyParameter);
    }

    @Nullable
    protected String obtainUsername(HttpServletRequest request) {
        if (MdmEncryptEnum.SM.getMdmEncrypt().equals(mdmEncrypt)) {
            return sm4.decryptStr(request.getParameter(usernameParameter));
        }
        return request.getParameter(usernameParameter);
    }

    @Nullable
    protected String obtainPassword(HttpServletRequest request) {
        if (MdmEncryptEnum.SM.getMdmEncrypt().equals(mdmEncrypt)) {
            return sm4.decryptStr(request.getParameter(passwordParameter));
        }
        return request.getParameter(passwordParameter);
    }

    @Nullable
    protected String obtainImei(HttpServletRequest request) {
        if (MdmEncryptEnum.SM.getMdmEncrypt().equals(mdmEncrypt)) {
            return sm4.decryptStr(request.getParameter(imeiParameter));
        }
        return request.getParameter(imeiParameter);
    }

    @Nullable
    protected String obtainOaid(HttpServletRequest request) {
        if (MdmEncryptEnum.SM.getMdmEncrypt().equals(mdmEncrypt)) {
            return sm4.decryptStr(request.getParameter(oaidParameter));
        }
        return request.getParameter(oaidParameter);
    }

    @Nullable
    protected String obtainUuid(HttpServletRequest request) {
        if (MdmEncryptEnum.SM.getMdmEncrypt().equals(mdmEncrypt)) {
            return sm4.decryptStr(request.getParameter(uuidParameter));
        }
        return request.getParameter(uuidParameter);
    }

    @Nullable
    protected String obtainCompanyId(HttpServletRequest request) {
        if (MdmEncryptEnum.SM.getMdmEncrypt().equals(mdmEncrypt)) {
            return sm4.decryptStr(request.getParameter(companyIdParameter));
        }
        return request.getParameter(companyIdParameter);
    }

    @Nullable
    protected String obtainLoginMethod(HttpServletRequest request) {
        if (MdmEncryptEnum.SM.getMdmEncrypt().equals(mdmEncrypt)) {
            return sm4.decryptStr(request.getParameter(loginMethodParameter));
        }
        return request.getParameter(loginMethodParameter);
    }

    protected void setDetails(HttpServletRequest request,
                              UsernameAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }

    public void setMdmEncrypt(String mdmEncrypt) {
        this.mdmEncrypt = mdmEncrypt;
    }
}
