/* UtilLib - Copyright (c) 2022 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.mapeadores.util.http;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;


/**
 *
 * @author Vincent Calame
 */
public final class UrlTestEngine {

    private final static int LIMIT = 10;
    private final static int TIMEOUT = 1000;

    public static UrlStatus test(String urlString) {
        Object firstTest = testUrl(null, urlString);
        if (firstTest instanceof UrlStatus) {
            return (UrlStatus) firstTest;
        } else {
            return testConnection((URL) firstTest, 0);
        }
    }

    private static Object testUrl(URL context, String urlString) {
        try {
            URL url;
            if (context != null) {
                url = new URL(context, urlString);
            } else {
                url = new URL(urlString);
            }
            if (!testProtocol(url)) {
                return UrlStatusUtils.UNKNOWN_PROTOCOL;
            }
            return url;
        } catch (MalformedURLException mue) {
            try {
                URI uri = new URI(urlString);
                if (uri.isAbsolute()) {
                    return UrlStatusUtils.UNKNOWN_PROTOCOL;
                } else {
                    return UrlStatusUtils.RELATIVE;
                }
            } catch (URISyntaxException urise) {
                return UrlStatusUtils.MALFORMED;
            }

        }
    }

    private static UrlStatus testConnection(URL url, int redirectionCount) {
        UrlStatus urlStatus = null;
        HttpURLConnection urlConnection = null;
        try {
            urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.setInstanceFollowRedirects(false);
            urlConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0");
            urlConnection.setConnectTimeout(TIMEOUT);
            urlStatus = build(url, urlConnection, redirectionCount);
        } catch (IOException ioe) {
            urlStatus = UrlStatusUtils.UNREACHABLE;
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
        }
        return urlStatus;
    }

    private static boolean testProtocol(URL url) {
        String protocol = url.getProtocol();
        if ((protocol == null) || (protocol.isEmpty())) {
            return false;
        }
        switch (protocol) {
            case "http":
            case "https":
                return true;
            default:
                return false;
        }
    }

    private static UrlStatus build(URL context, HttpURLConnection urlConnection, int redirectionCount) throws IOException {
        RedirectionUrl redirectionUrl = null;
        try {
            int code = urlConnection.getResponseCode();
            if (code == - 1) {
                return UrlStatusUtils.BAD_HTTP;
            }
            String responseMessage = urlConnection.getResponseMessage();
            String status;
            switch (code) {
                case 200:
                    return UrlStatusUtils.OK;
                case 404:
                    status = UrlStatus.NOT_FOUND;
                    break;
                default:
                    if ((code >= 300) && (code < 400)) {
                        status = UrlStatus.REDIRECTION;
                        redirectionUrl = getRedirectionUrl(context, urlConnection, redirectionCount);
                    } else {
                        status = UrlStatus.HTTP_ERROR;
                    }
            }
            return new UrlStatus(status, code, responseMessage, redirectionUrl);
        } catch (SocketTimeoutException ste) {
            return UrlStatusUtils.TIMEOUT;
        }
    }

    private static RedirectionUrl getRedirectionUrl(URL context, HttpURLConnection urlConnection, int redirectionCount) {
        String redirectionLocation = urlConnection.getHeaderField("Location");
        if ((redirectionLocation == null) || (redirectionLocation.isEmpty())) {
            return new RedirectionUrl("", UrlStatusUtils.MISSING_REDIRECTION);
        }
        UrlStatus urlStatus;
        if (redirectionCount >= LIMIT) {
            urlStatus = UrlStatusUtils.REDIRECTION_LIMIT;
        } else {
            Object redirectionTest = testUrl(context, redirectionLocation);
            if (redirectionTest instanceof UrlStatus) {
                urlStatus = (UrlStatus) redirectionTest;
            } else {
                URL redirectionUrl = (URL) redirectionTest;
                urlStatus = testConnection(redirectionUrl, redirectionCount + 1);
                redirectionLocation = redirectionUrl.toString();
            }
        }
        return new RedirectionUrl(redirectionLocation, urlStatus);
    }


}
