package ru.yandex.webmaster3.viewer.util;

import org.apache.commons.lang3.StringUtils;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.util.IdUtils;

/**
 * @author aherman
 */
public class HostnameFilter {
    private final WebmasterHostId.Schema schema;
    private final String hostnamePart;
    private final int port;
    private final boolean prefix;

    private HostnameFilter(WebmasterHostId.Schema schema, String hostnamePart, int port, boolean prefix) {
        this.schema = schema;
        this.hostnamePart = hostnamePart;
        this.port = port;
        this.prefix = prefix;
    }

    public boolean accept(WebmasterHostId hostId) {
        if (schema != null) {
            if (schema != hostId.getSchema()) {
                return false;
            }
        }

        if (port > 0) {
            if (port != hostId.getPort()) {
                return false;
            }
        }

        if (hostnamePart == null) {
            return true;
        }

        if (prefix) {
            return hostId.getReadableHostname().startsWith(hostnamePart);
        } else {
            return hostId.getReadableHostname().contains(hostnamePart);
        }
    }

    /**
     *
     * @param hostnameFilterString filter string

     * @return host filter or null if filter is wrong
     */
    public static HostnameFilter create(String hostnameFilterString) {
        hostnameFilterString = StringUtils.trimToEmpty(hostnameFilterString).toLowerCase();
        if (hostnameFilterString.isEmpty()) {
            return acceptAll();
        }

        if (hostnameFilterString.length() > 255) {
            return null;
        }

        if ("https".contains(hostnameFilterString)) {
            return or(
                    new HostnameFilter(WebmasterHostId.Schema.HTTPS, null, 0, false),
                    new HostnameFilter(null, hostnameFilterString, 0, false)
            );
        }

        boolean prefix = false;

        // Skip two "://" in filter start
        // This can happen if user copied hostname from browser incorrectly
        if (hostnameFilterString.startsWith(":")) {
            hostnameFilterString = StringUtils.substring(hostnameFilterString, 1);
        }
        if (hostnameFilterString.startsWith("/")) {
            hostnameFilterString = StringUtils.substring(hostnameFilterString, 1);
            prefix = true;
        }
        if (hostnameFilterString.startsWith("/")) {
            hostnameFilterString = StringUtils.substring(hostnameFilterString, 1);
        }

        int colon = hostnameFilterString.indexOf(':');
        if (colon < 0) {
            if (StringUtils.isNumeric(hostnameFilterString)) {
                try {
                    int port = Integer.parseInt(hostnameFilterString);
                    if (port != WebmasterHostId.DEFAULT_HTTP_PORT && port != WebmasterHostId.DEFAULT_HTTPS_PORT) {
                        return or(
                                new HostnameFilter(null, hostnameFilterString, 0, prefix),
                                new HostnameFilter(null, null, port, false)
                        );
                    }
                } catch (NumberFormatException e) {
                    // Ignore
                }
            }
            return new HostnameFilter(null, hostnameFilterString, 0, prefix);
        }
        if (colon == 0) {
            return null;
        }

        String beforeColonPart = hostnameFilterString.substring(0, colon);
        String afterColon = StringUtils.substring(hostnameFilterString, colon + 1);
        WebmasterHostId.Schema schema = null;
        boolean firstPartIsHostname = true;
        String hostnamePart = null;
        int port = 0;

        // ttp://
        if ("http".endsWith(beforeColonPart)) {
            firstPartIsHostname = false;
            prefix = true;
            schema = WebmasterHostId.Schema.HTTP;
            if (afterColon.startsWith("//") || afterColon.equals("/")) {
                afterColon = StringUtils.substring(afterColon, 2);
            }
            // ttps://
        } else if ("https".endsWith(beforeColonPart)) {
            firstPartIsHostname = false;
            prefix = true;
            schema = WebmasterHostId.Schema.HTTPS;
            if (afterColon.startsWith("//") || afterColon.equals("/")) {
                afterColon = StringUtils.substring(afterColon, 2);
            }
        }

        if (firstPartIsHostname) {
            if (StringUtils.isNumeric(afterColon)) {
                try {
                    port = Integer.parseInt(afterColon);
                    return new HostnameFilter(null, beforeColonPart, port, prefix);
                } catch (NumberFormatException e) {
                    return null;
                }
            } else {
                return null;
            }
        }

        int secondColon = afterColon.indexOf(':');
        if (secondColon == 0) {
            return null;
        }
        if (secondColon > 0) {
            String maybePort = StringUtils.substring(afterColon, secondColon + 1);
            if (!StringUtils.isNumeric(maybePort)) {
                return null;
            }
            try {
                port = Integer.parseInt(maybePort);
            } catch (NumberFormatException e) {
                return null;
            }
            hostnamePart = afterColon.substring(0, secondColon);
        } else {
            hostnamePart = afterColon;
        }
        if (hostnamePart.contains(WebmasterHostId.IDN_FLAG)) {
            hostnamePart = IdUtils.IDN.toUnicode(hostnamePart);
        }

        return new HostnameFilter(schema, hostnamePart, port, prefix);
    }

    private static HostnameFilter acceptAll() {
        return new HostnameFilter(null, null, 0, false) {
            @Override
            public boolean accept(WebmasterHostId hostId) {
                return true;
            }
        };
    }

    private static HostnameFilter or(final HostnameFilter f1, final HostnameFilter f2) {
        return new HostnameFilter(null, null, 0, false) {
            @Override
            public boolean accept(WebmasterHostId hostId) {
                return f1.accept(hostId) || f2.accept(hostId);
            }
        };
    }
}
