package net.northking.cctp.element.internal.protocol.http;

import com.alibaba.fastjson.JSONObject;
import net.northking.cctp.element.core.exception.ComponentRuntimeException;
import net.northking.cctp.element.internal.protocol.http.auth.AuthScheme;
import net.northking.cctp.element.internal.protocol.http.auth.oauth2.OAuth2Credentials;
import net.northking.cctp.element.util.Assert;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.http.auth.NTCredentials;
import org.apache.http.auth.UsernamePasswordCredentials;

import java.net.URL;
import java.util.Map;

/**
 * RequestBuilder
 *
 * @author fengxiang.zhang
 * @date 2022/4/14
 * @since 1.0
 */
public class RequestParam {

    /**
     * 请求方法
     */
    private RequestMethod method;
    /**
     * URL地址
     */
    private String url;
    /**
     * 内容编码
     */
    private String contentEncoding;
    /**
     * Query参数
     */
    private Map<String, String> queryParams;
    /**
     * 请求头设置
     */
    private Map<String, String> headers;
    /**
     * 请求超时
     */
    private int timeout;
    /**
     * 重定向类型：0（无重定向）、1（自动重定向）、2（跟随重定向）
     */
    private int redirectType;
    /**
     * 请求体色或者
     */
    private BodySet bodySet;
    /**
     * 授权认证设置
     */
    private Authentication authentication;
    /**
     * SSL证书
     */
    private SslKey sslKey;
    /**
     * 代理设置
     */
    private Proxy proxy;

    public static RequestParam build() {
        return new RequestParam();
    }

    public RequestParam setMethod(RequestMethod method) {
        this.method = method;
        return this;
    }

    public RequestParam setUrl(String url) {
        try {
            new URL(url);
        } catch (Exception e) {
            throw new ComponentRuntimeException("非法的URL:" + url);
        }
        this.url = url;
        return this;
    }

    public RequestParam setContentEncoding(String contentEncoding) {
        this.contentEncoding = contentEncoding;
        return this;
    }

    public RequestParam setQueryParams(Map<String, String> queryParams) {
        this.queryParams = queryParams;
        return this;
    }

    public RequestParam setHeaders(Map<String, String> headers) {
        this.headers = headers;
        return this;
    }

    public RequestParam setTimeout(int timeout) {
        this.timeout = timeout;
        return this;
    }

    public RequestParam setRedirectType(int redirectType) {
        this.redirectType = redirectType;
        return this;
    }

    public RequestParam setBodySet(BodySet bodySet) {
        this.bodySet = bodySet;
        return this;
    }

    public RequestParam setAuthentication(Authentication authentication) {
        this.authentication = authentication;
        return this;
    }

    public RequestParam setSslKey(SslKey sslKey) {
        this.sslKey = sslKey;
        return this;
    }

    public RequestParam setProxy(Proxy proxy) {
        this.proxy = proxy;
        return this;
    }

    public RequestMethod getMethod() {
        return method;
    }

    public String getUrl() {
        return url;
    }

    public String getContentEncoding() {
        return contentEncoding;
    }

    public Map<String, String> getQueryParams() {
        return queryParams;
    }

    public Map<String, String> getHeaders() {
        return headers;
    }

    public int getTimeout() {
        return timeout;
    }

    public int getRedirectType() {
        return redirectType;
    }

    public BodySet getBodySet() {
        return bodySet;
    }

    public Authentication getAuthentication() {
        return authentication;
    }

    public SslKey getSslKey() {
        return sslKey;
    }

    public Proxy getProxy() {
        return proxy;
    }

    public boolean isFollowingRedirect() {
        return this.redirectType == 2;
    }

    /**
     * 请求体输入参数结构定义
     */
    public static class BodySet {
        public String type = BodyType.NONE.name();
        public String content;

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public String getContent() {
            return content;
        }

        public void setContent(String content) {
            this.content = content;
        }
    }

    /**
     * Body type
     */
    public enum BodyType {
        NONE, FORM_DATA, X_WWW_FORM_URLENCODED, RAW, BINARY;

        public static BodyType from(String type) {
            for (BodyType e : BodyType.values()) {
                if (e.name().equalsIgnoreCase(type)) {
                    return e;
                }
            }
            throw new IllegalArgumentException(String.format("非法的Body类型:%s", type));
        }
    }

    /**
     * Param type
     */
    public enum ParamType {
        TEXT, FILE;

        public static ParamType from(String type) {
            for (ParamType e : ParamType.values()) {
                if (e.name().equalsIgnoreCase(type)) {
                    return e;
                }
            }
            throw new IllegalArgumentException(String.format("非法的类型:%s, 只支持TEXT或FILE", type));
        }
    }

    /**
     * parameter
     */
    public static class Parameter {
        /**
         * 参数名称
         */
        private String key;
        /**
         * 参数值
         */
        private String value;
        /**
         * URL Encoded
         */
        private boolean encoded = true;
        /**
         * 参数类型
         */
        private String type = ParamType.TEXT.name();
        /**
         * 文件路径
         */
        private String path;

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }

        public boolean isEncoded() {
            return encoded;
        }

        public void setEncoded(boolean encoded) {
            this.encoded = encoded;
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public String getPath() {
            return path;
        }

        public void setPath(String path) {
            this.path = path;
        }
    }

    /**
     * 授权认证输入参数结构定义
     */
    public static class Authentication {
        public String scheme = AuthScheme.NONE.name();
        private JSONObject schemeParam;

        public UsernamePasswordCredentials toUsernamePasswordCredentials() {
            Assert.notNull(schemeParam, String.format("认证方案[%s]的参数不能为空", scheme));
            String username = schemeParam.getString("username");
            String password = schemeParam.getString("password");
            return new UsernamePasswordCredentials(username, password);
        }

        public NTCredentials toNTCredentials() {
            Assert.notNull(schemeParam, String.format("认证方案[%s]的参数不能为空", scheme));
            String username = schemeParam.getString("username");
            String password = schemeParam.getString("password");
            String workstation = schemeParam.getString("workstation");
            String domain = schemeParam.getString("domain");
            return new NTCredentials(username, password, workstation, domain);
        }

        public OAuth2Credentials toOAuth2Credentials() {
            Assert.notNull(schemeParam, String.format("认证方案[%s]的参数不能为空", scheme));
            return schemeParam.toJavaObject(OAuth2Credentials.class);
        }

        public String getScheme() {
            return scheme;
        }

        public void setScheme(String scheme) {
            this.scheme = scheme;
        }

        public JSONObject getSchemeParam() {
            return schemeParam;
        }

        public void setSchemeParam(JSONObject schemeParam) {
            this.schemeParam = schemeParam;
        }
    }

    /**
     * SSL证书
     */
    public static class SslKey {
        private String type;
        private String fileId;
        private String storePassword;
        private String keyPassword;

        public char[] getStorePasswordChar() {
            return storePassword == null ? "".toCharArray() : storePassword.toCharArray();
        }

        public char[] getKeyPasswordChar() {
            return keyPassword == null ? "".toCharArray() : keyPassword.toCharArray();
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public String getFileId() {
            return fileId;
        }

        public void setFileId(String fileId) {
            this.fileId = fileId;
        }

        public String getStorePassword() {
            return storePassword;
        }

        public void setStorePassword(String storePassword) {
            this.storePassword = storePassword;
        }

        public String getKeyPassword() {
            return keyPassword;
        }

        public void setKeyPassword(String keyPassword) {
            this.keyPassword = keyPassword;
        }
    }

    /**
     * 代理认证
     */
    public static class Proxy {
        private String host;
        private int port = 80;
        private String username;
        private String password;

        public String getHost() {
            return host;
        }

        public void setHost(String host) {
            this.host = host;
        }

        public int getPort() {
            return port;
        }

        public void setPort(int port) {
            this.port = port;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;

            if (o == null || getClass() != o.getClass()) return false;

            Proxy proxy = (Proxy) o;

            return new EqualsBuilder()
                    .append(port, proxy.port)
                    .append(host, proxy.host)
                    .append(username, proxy.username)
                    .append(password, proxy.password)
                    .isEquals();

        }

        @Override
        public int hashCode() {
            return new HashCodeBuilder(17, 37)
                    .append(host)
                    .append(port)
                    .append(username)
                    .append(password)
                    .toHashCode();
        }
    }
}
