package ru.yandex.wmconsole.util;

import org.apache.commons.lang.StringUtils;
import ru.yandex.wmtools.common.SupportedProtocols;
import ru.yandex.wmtools.common.error.InternalException;
import ru.yandex.wmtools.common.error.InternalProblem;
import ru.yandex.wmtools.common.util.uri.URI2;

import java.net.IDN;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;

/**
 * @author aherman
 */
public class BackportedIdUtils {
    public static final BackportedWebmasterHostId.Schema DEFAULT_SCHEMA = BackportedWebmasterHostId.Schema.HTTP;

    public static String hostIdToUrl(BackportedWebmasterHostId hostId) {
        StringBuilder sb = new StringBuilder();
        sb.append(hostId.getSchema().getSchemaPrefix());
        sb.append(hostId.getPunycodeHostname());
        if (!hostId.isDefaultPort()) {
            sb.append(':').append(hostId.getPort());
        }
        return sb.toString();
    }

    public static BackportedWebmasterHostId urlToHostId(String urlString) throws InternalException {
        try {
            return urlToHostId(SupportedProtocols.getURL(urlString));
        } catch (MalformedURLException | URISyntaxException | SupportedProtocols.UnsupportedProtocolException e) {
            throw new InternalException(InternalProblem.INTERNAL_PROBLEM, "Unable to parse url: " + urlString);
        }
    }

    public static BackportedWebmasterHostId urlToHostId(URL url) throws InternalException {
        String protocol = url.getProtocol();
        String host = url.getHost();
        int port = url.getPort();
        BackportedWebmasterHostId.Schema schema;
        if (BackportedWebmasterHostId.Schema.HTTP.getSchemaName().equalsIgnoreCase(protocol)) {
            schema = BackportedWebmasterHostId.Schema.HTTP;
        } else if (BackportedWebmasterHostId.Schema.HTTPS.getSchemaName().equalsIgnoreCase(protocol)) {
            schema = BackportedWebmasterHostId.Schema.HTTPS;
        } else {
            throw new InternalException(InternalProblem.INTERNAL_PROBLEM,"Unknown schema: " + url);
        }
        if (port < 0) {
            port = schema.getDefaultPort();
        }
        host = IDN.toASCII(host);
        return new BackportedWebmasterHostId(schema, host, port);
    }

    /**
     * Checks scheme and www prefix of url and converts according to given hostId.
     * <p/>
     * Example: hostId = https:www.site.ru:443, url = site.ru, result = https://www.site.ru
     *
     * @param hostId webmaster host id
     * @param url    normalizing url
     * @return
     */
    public static URL normalizeUrl(BackportedWebmasterHostId hostId, URL url) throws MalformedURLException {
        String newHostName = url.getHost();
        if (url.getHost() != null) {
            boolean isWwwUrl = url.getHost().startsWith("www.");
            if (isWwwUrl ^ hostId.getPunycodeHostname().startsWith("www.")) {
                if (isWwwUrl) {
                    newHostName = newHostName.substring("www.".length());
                } else {
                    newHostName = "www." + url.getHost();
                }
            }
        }
        String protocol = hostId.getSchema().getSchemaName();
        return new URL(protocol, newHostName, url.getPort(), url.getFile());
    }

    public static String hostIdToWebIdString(BackportedWebmasterHostId hostId) {
        BackportedWebmasterHostId.Schema schema = hostId.getSchema();
        String readableHostname = hostId.getReadableHostname();
        int port = hostId.getPort();

        return schema.getSchemaName() + ":" + readableHostname + ":" + port;
    }

    /**
     * <p>
     * Format: <code>schema:punycodeHostname:port</code>
     * </p>
     *
     * @param hostIdStr
     * @return
     */
    public static BackportedWebmasterHostId stringToHostId(String hostIdStr) {
        int schemaEnd = hostIdStr.indexOf(':');
        if (schemaEnd < 0) {
            throw new IllegalArgumentException("Unknown hostId: " + hostIdStr);
        }
        BackportedWebmasterHostId.Schema schema = null;
        String schemaStr = hostIdStr.substring(0, schemaEnd);
        for (BackportedWebmasterHostId.Schema sch : BackportedWebmasterHostId.Schema.values()) {
            if (sch.getSchemaName().equals(schemaStr)) {
                schema = sch;
                break;
            }
        }
        if (schema == null) {
            throw new IllegalArgumentException("Unknown hostId: " + hostIdStr);
        }

        int hostnameEnd = hostIdStr.indexOf(':', schemaEnd + 1);
        if (hostnameEnd < 0) {
            throw new IllegalArgumentException("Unknown hostId: " + hostIdStr);
        }
        String punycodeHostname = hostIdStr.substring(schemaEnd + 1, hostnameEnd);
        String port = hostIdStr.substring(hostnameEnd + 1);
        if (StringUtils.isEmpty(punycodeHostname) || StringUtils.isEmpty(port)) {
            throw new IllegalArgumentException("Unknown hostId: " + hostIdStr);
        }

        return new BackportedWebmasterHostId(schema, punycodeHostname, Integer.valueOf(port));
    }

    public static BackportedWebmasterHostId webIdStringToHostId(String hostIdStr) {
        int schemaEnd = hostIdStr.indexOf(':');
        if (schemaEnd < 0) {
            throw new IllegalArgumentException("Unknown hostId: " + hostIdStr);
        }
        BackportedWebmasterHostId.Schema schema = null;
        String schemaStr = hostIdStr.substring(0, schemaEnd);
        for (BackportedWebmasterHostId.Schema sch : BackportedWebmasterHostId.Schema.values()) {
            if (sch.getSchemaName().equals(schemaStr)) {
                schema = sch;
                break;
            }
        }
        if (schema == null) {
            throw new IllegalArgumentException("Unknown hostId: " + hostIdStr);
        }

        int hostnameEnd = hostIdStr.indexOf(':', schemaEnd + 1);
        if (hostnameEnd < 0) {
            throw new IllegalArgumentException("Unknown hostId: " + hostIdStr);
        }
        String punycodeHostname = IDN.toASCII(hostIdStr.substring(schemaEnd + 1, hostnameEnd));
        String port = hostIdStr.substring(hostnameEnd + 1);
        if (StringUtils.isEmpty(punycodeHostname) || StringUtils.isEmpty(port)) {
            throw new IllegalArgumentException("Unknown hostId: " + hostIdStr);
        }

        return new BackportedWebmasterHostId(schema, punycodeHostname, Integer.valueOf(port));
    }

    public static String toHostString(BackportedWebmasterHostId hostId, boolean showDefaultSchema, boolean unicodeHost, boolean showDefaultPort) {
        StringBuilder sb = new StringBuilder();
        if (showDefaultSchema || hostId.getSchema() != DEFAULT_SCHEMA) {
            sb.append(hostId.getSchema().getSchemaPrefix());
        }
        if (unicodeHost) {
            sb.append(hostId.getReadableHostname());
        } else {
            sb.append(hostId.getPunycodeHostname());
        }

        if (showDefaultPort || !hostId.isDefaultPort()) {
            sb.append(":").append(hostId.getPort());
        }
        return sb.toString();
    }

    public static String toRobotHostString(BackportedWebmasterHostId hostId) {
        return toHostString(hostId, false, false, false);
    }

    public static BackportedWebmasterHostId fromUri2(URI2 uri2) throws IllegalArgumentException {
        final BackportedWebmasterHostId.Schema schema;
        final int defaultPort;
        if (BackportedWebmasterHostId.Schema.HTTP.getSchemaName().equals(uri2.getScheme())) {
            schema = BackportedWebmasterHostId.Schema.HTTP;
            defaultPort = BackportedWebmasterHostId.DEFAULT_HTTP_PORT;
        } else if (BackportedWebmasterHostId.Schema.HTTPS.getSchemaName().equals(uri2.getScheme())) {
            schema = BackportedWebmasterHostId.Schema.HTTPS;
            defaultPort = BackportedWebmasterHostId.DEFAULT_HTTPS_PORT;
        } else {
            throw new IllegalArgumentException("unsupported scheme " + uri2.getScheme());
        }
        int explicitPort = uri2.getPort() != -1 ? uri2.getPort() : defaultPort;
        String hostName = uri2.getHost();
        if (!(hostName.startsWith("xn--") || hostName.contains(".xn--"))) {
            hostName = IDN.toASCII(hostName);
        }
        return new BackportedWebmasterHostId(schema, hostName, explicitPort);
    }
}
