package ru.yandex.wmtools.common.util.http;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.yandex.wmtools.common.util.enums.IntEnum;
import ru.yandex.wmtools.common.util.enums.IntEnumResolver;

/**
 * Yandex extended HTTP codes from library/cpp/http/fetch/exthttpcodes.h
 *
 * @author aherman
 */
public enum YandexHttpStatus implements IntEnum {
    UNKNOWN                                 (-1),
    NEVER_DOWNLOADED                        (-2),

    // Standard HTTP codes
    HTTP_100_CONTINUE                       (100),
    HTTP_101_SWITCHING_PROTOCOLS            (101),
    HTTP_1XX                                (-100),

    HTTP_200_OK                             (200),
    HTTP_201_CREATED                        (201),
    HTTP_202_ACCEPTED                       (202),
    HTTP_203_NON_AUTHORITATIVE_INFORMATION  (203),
    HTTP_204_NO_CONTENT                     (204),
    HTTP_205_RESET_CONTENT                  (205),
    HTTP_206_PARTIAL_CONTENT                (206),
    HTTP_2XX                                (-200),

    HTTP_300_MULTIPLE_CHOICES               (300),
    HTTP_301_MOVED_PERMANENTLY              (301),
    HTTP_302_FOUND                          (302),
    HTTP_303_SEE_OTHER                      (303),
    HTTP_304_NOT_MODIFIED                   (304),
    HTTP_305_USE_PROXY                      (305),
    HTTP_306_SWITCH_PROXY                   (306),
    HTTP_307_TEMPORARY_REDIRECT             (307),
    HTTP_308_PERMANENT_REDIRECT             (308),
    HTTP_3XX                                (-300),

    HTTP_400_BAD_REQUEST                    (400),
    HTTP_401_UNAUTHORIZED                   (401),
    HTTP_402_PAYMENT_REQUIRED               (402),
    HTTP_403_FORBIDDEN                      (403),
    HTTP_404_NOT_FOUND                      (404),
    HTTP_405_METHOD_NOT_ALLOWED             (405),
    HTTP_406_NOT_ACCEPTABLE                 (406),
    HTTP_407_PROXY_AUTHENTICATION_REQUIRED  (407),
    HTTP_408_REQUEST_TIMEOUT                (408),
    HTTP_409_CONFLICT                       (409),
    HTTP_410_GONE                           (410),
    HTTP_411_LENGTH_REQUIRED                (411),
    HTTP_412_PRECONDITION_FAILED            (412),
    HTTP_413_REQUEST_ENTITY_TOO_LARGE       (413),
    HTTP_414_REQUEST_URI_TOO_LONG           (414),
    HTTP_415_UNSUPPORTED_MEDIA_TYPE         (415),
    HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE(416),
    HTTP_417_EXPECTATION_FAILED             (417),
    HTTP_418_IM_A_TEAPOT                    (418),
    HTTP_419_AUTHENTICATION_TIMEOUT         (419),
    HTTP_426_UPGRADE_REQUIRED               (426),
    HTTP_428_PRECONDITION_REQUIRED          (428),
    HTTP_429_TOO_MANY_REQUESTS              (429),
    HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE(431),
    HTTP_4XX                                (-400),

    HTTP_500_INTERNAL_SERVER_ERROR          (500),
    HTTP_501_NOT_IMPLEMENTED                (501),
    HTTP_502_BAD_GATEWAY                    (502),
    HTTP_503_SERVICE_UNAVAILABLE            (503),
    HTTP_504_GATEWAY_TIMEOUT                (504),
    HTTP_505_HTTP_VERSION_NOT_SUPPORTED     (505),
    HTTP_506_VARIANT_ALSO_NEGOTIATES        (506),
    HTTP_509_BANDWIDTH_LIMIT_EXCEEDED       (509),
    HTTP_510_NOT_EXTENDED                   (510),
    HTTP_511_NETWORK_AUTHENTICATION_REQUIRED(511),
    HTTP_5XX                                (-500),

    // Custom
    HTTP_1000_BAD_RESPONSE_HEADER           (1000),
    HTTP_1001_CONNECTION_LOST               (1001),
    HTTP_1002_BODY_TOO_LARGE                (1002),
    HTTP_1003_ROBOTS_TXT_DISALLOW           (1003),
    HTTP_1004_BAD_URL                       (1004),
    HTTP_1005_BAD_MIME                      (1005),
    HTTP_1006_DNS_FAILURE                   (1006),
    HTTP_1007_BAD_STATUS_CODE               (1007),
    HTTP_1008_BAD_HEADER_STRING             (1008),
    HTTP_1009_BAD_CHUNK                     (1009),
    HTTP_1010_CONNECT_FAILED                (1010),
    HTTP_1011_FILTER_DISALLOW               (1011),
    HTTP_1012_LOCAL_EIO                     (1012),
    HTTP_1013_BAD_CONTENT_LENGTH            (1013),
    HTTP_1014_BAD_ENCODING                  (1014),
    HTTP_1015_LENGTH_UNKNOWN                (1015),
    HTTP_1016_HEADER_EOF                    (1016),
    HTTP_1017_MESSAGE_EOF                   (1017),
    HTTP_1018_CHUNK_EOF                     (1018),
    HTTP_1019_PAST_EOF                      (1019),
    HTTP_1020_HEADER_TOO_LARGE              (1020),
    HTTP_1021_URL_TOO_LARGE                 (1021),
    HTTP_1022_INTERRUPTED                   (1022),
    HTTP_1023_CUSTOM_NOT_MODIFIED           (1023),
    HTTP_1024_BAD_CONTENT_ENCODING          (1024),
    HTTP_1025_NO_RESOURCES                  (1025),
    HTTP_1026_FETCHER_SHUTDOWN              (1026),
    HTTP_1027_CHUNK_TOO_LARGE               (1027),
    HTTP_1028_SERVER_BUSY                   (1028),
    HTTP_1029_SERVICE_UNKNOWN               (1029),
    HTTP_1030_PROXY_UNKNOWN                 (1030),
    HTTP_1031_PROXY_REQUEST_TIME_OUT        (1031),
    HTTP_1032_PROXY_INTERNAL_ERROR          (1032),
    HTTP_1033_PROXY_CONNECT_FAILED          (1033),
    HTTP_1034_PROXY_CONNECTION_LOST         (1034),
    HTTP_1035_PROXY_NO_PROXY                (1035),
    HTTP_1036_PROXY_ERROR                   (1036),
    HTTP_1037_SSL_ERROR                     (1037),
    HTTP_1038_CACHED_COPY_NOT_FOUND         (1038),
    HTTP_1039_TIMEDOUT_WHILE_BYTES_RECEIVING(1039),
    HTTP_1040_FETCHER_BAD_RESPONSE          (1040),
    HTTP_1041_FETCHER_MB_ERROR              (1041),
    HTTP_SSL_CERT_ERROR                     (1042),
    HTTP_PROXY_REQUEST_CANCELED             (1051),

    // Custom (replace HTTP 200/304)
    EXT_HTTP_2000_MIRRMOVE                  (2000),
    EXT_HTTP_2001_MANUAL_DELETE             (2001),
    EXT_HTTP_2002_NOTUSED2                  (2002),
    EXT_HTTP_2003_NOTUSED3                  (2003),
    EXT_HTTP_2004_REFRESH                   (2004),
    EXT_HTTP_2005_NOINDEX                   (2005),
    EXT_HTTP_2006_BADCODES                  (2006),
    EXT_HTTP_2007_SITESTAT                  (2007),
    EXT_HTTP_2008_IOERROR                   (2008),
    EXT_HTTP_2009_BASEERROR                 (2009),
    EXT_HTTP_2010_PARSERROR                 (2010),
    EXT_HTTP_2011_BAD_CHARSET               (2011),
    EXT_HTTP_2012_BAD_LANGUAGE              (2012),
    EXT_HTTP_2013_NUMERERROR                (2013),
    EXT_HTTP_2014_EMPTYDOC                  (2014),
    EXT_HTTP_2015_HUGEDOC                   (2015),
    EXT_HTTP_2016_LINKGARBAGE               (2016),
    EXT_HTTP_2017_EXDUPLICATE               (2017),
    EXT_HTTP_2018_FILTERED                  (2018),
    EXT_HTTP_2019_PARSERFAIL                (2019), // parser crashed (in this case image spider will redownload such document)
    EXT_HTTP_2020_GZIPERROR                 (2020),
    EXT_HTTP_2021_CLEANPARAM                (2021),
    EXT_HTTP_2022_MANUAL_DELETE_URL         (2022),
    EXT_HTTP_2023_CUSTOM_PARTIAL_CONTENT    (2023),
    EXT_HTTP_2024_EMPTY_RESPONSE            (2024),
    EXT_HTTP_2025_REL_CANONICAL             (2025),

    EXT_HTTP_3001_HOSTFILTER                (3001),
    EXT_HTTP_3002_URLFILTER                 (3002),
    EXT_HTTP_3003_SUFFIXFILTER              (3003),
    EXT_HTTP_3004_DOMAINFILTER              (3004),
    EXT_HTTP_3005_EXTDOMAINFILTER           (3005),
    EXT_HTTP_3006_PORTFILTER                (3006),
    EXT_HTTP_3007_MIRROR                    (3007),
    EXT_HTTP_3008_DEEPDIR                   (3008),
    EXT_HTTP_3009_DUPDIRS                   (3009),
    EXT_HTTP_3010_REGEXP                    (3010),
    EXT_HTTP_3012_OLDDELETED                (3012),
    EXT_HTTP_3013_PENALTY                   (3013),
    EXT_HTTP_3015_POLICY                    (3015),
    EXT_HTTP_3016_TOOOLD                    (3016),
    EXT_HTTP_3017_GARBAGE                   (3017),
    EXT_HTTP_3018_FOREIGN                   (3018),
    EXT_HTTP_3019_EXT_REGEXP                (3019),
    EXT_HTTP_3020_HOPS                      (3020),
    EXT_HTTP_3021_SELRANK                   (3021),
    EXT_HTTP_3022_NOLINKS                   (3022),
    EXT_HTTP_3023_WRONGMULTILANG            (3023),
    EXT_HTTP_3024_SOFTMIRRORS               (3024),
    EXT_HTTP_3025_BIGLEVEL                  (3025),

    // fast robot codes
    EXT_HTTP_4000_FASTHOPS                  (4000),
    EXT_HTTP_4001_NODOC                     (4001),
    ;

    private static final Logger log = LoggerFactory.getLogger(YandexHttpStatus.class);

    private final int code;

    private YandexHttpStatus(int code) {
        this.code = code;
    }

    @Override
    public int value() {
        return code;
    }

    public int getCode() {
        return code;
    }

    public static YandexHttpStatus parseCode(int statusCode) {
        YandexHttpStatus result = R.fromValueOrUnknown(statusCode);
        if (result != UNKNOWN) {
            return result;
        }
        log.warn("Unknown sita http status: " + statusCode);
        if (statusCode < 100) {
            return UNKNOWN;
        }

        if (statusCode < 200) {
            return HTTP_1XX;
        }
        if (statusCode < 300) {
            return HTTP_2XX;
        }
        if (statusCode < 400) {
            return HTTP_3XX;
        }
        if (statusCode < 500) {
            return HTTP_4XX;
        }
        if (statusCode < 600) {
            return HTTP_5XX;
        }
        return UNKNOWN;
    }

    public static boolean isStandardHttpCode(YandexHttpStatus status) {
        return status != UNKNOWN && status.code >= 100 && status.code < 600;
    }

    public static boolean is3XX(YandexHttpStatus status) {
        return status == HTTP_3XX || (status.code >= 300 && status.code < 400);
    }

    public static boolean is4XX(YandexHttpStatus status) {
        return status == HTTP_4XX || (status.getCode() >= 400 && status.code < 500);
    }

    public static boolean is5XX(YandexHttpStatus status) {
        return status == HTTP_5XX || (status.getCode() >= 500 && status.code < 600);
    }

    public static boolean isExtErrors(YandexHttpStatus status) {
        return status.code >= 1000 && status.code < 2000;
    }

    public static boolean is200Or2XXX(YandexHttpStatus status) {
        return status == HTTP_200_OK || (status.code > 2000 && status.code < 3000);
    }

    public static Integer toStandardHttpCode(YandexHttpStatus status) {
        if (status == UNKNOWN) {
            return null;
        }
        switch (status) {
            case HTTP_1XX:
            case HTTP_2XX:
            case HTTP_3XX:
            case HTTP_4XX:
            case HTTP_5XX:
                return null;
        }
        if (status.code < 600) {
            return status.code;
        }
        if (status.code >= 2000 && status.code < 3000) {
            return HTTP_200_OK.code;
        }

        return null;
    }

    public static String getStatusString(YandexHttpStatus status) {
        switch (status) {
            case HTTP_100_CONTINUE:
                return "Continue";
            case HTTP_101_SWITCHING_PROTOCOLS:
                return "Switching Protocols";

            case HTTP_200_OK:
                return "OK";
            case HTTP_201_CREATED:
                return "Created";
            case HTTP_202_ACCEPTED:
                return "Accepted";
            case HTTP_203_NON_AUTHORITATIVE_INFORMATION:
                return "Non-Authoritative Information";
            case HTTP_204_NO_CONTENT:
                return "No Content";
            case HTTP_205_RESET_CONTENT:
                return "Reset Content";
            case HTTP_206_PARTIAL_CONTENT:
                return "Partial Content";

            case HTTP_300_MULTIPLE_CHOICES:
                return "Multiple Choices";
            case HTTP_301_MOVED_PERMANENTLY:
                return "Moved Permanently";
            case HTTP_302_FOUND:
                return "Found";
            case HTTP_303_SEE_OTHER:
                return "See Other";
            case HTTP_304_NOT_MODIFIED:
                return "Not Modified";
            case HTTP_305_USE_PROXY:
                return "Use Proxy";
            case HTTP_307_TEMPORARY_REDIRECT:
                return "Temporary Redirect";

            case HTTP_400_BAD_REQUEST:
                return "Bad Request";
            case HTTP_401_UNAUTHORIZED:
                return "Unauthorized";
            case HTTP_402_PAYMENT_REQUIRED:
                return "Payment Required";
            case HTTP_403_FORBIDDEN:
                return "Forbidden";
            case HTTP_404_NOT_FOUND:
                return "Not Found";
            case HTTP_405_METHOD_NOT_ALLOWED:
                return "Method Not Allowed";
            case HTTP_406_NOT_ACCEPTABLE:
                return "Not Acceptable";
            case HTTP_407_PROXY_AUTHENTICATION_REQUIRED:
                return "Proxy Authentication Required";
            case HTTP_408_REQUEST_TIMEOUT:
                return "Request Timeout";
            case HTTP_409_CONFLICT:
                return "Conflict";
            case HTTP_410_GONE:
                return "Gone";
            case HTTP_411_LENGTH_REQUIRED:
                return "Length Required";
            case HTTP_412_PRECONDITION_FAILED:
                return "Precondition Failed";
            case HTTP_413_REQUEST_ENTITY_TOO_LARGE:
                return "Request Entity Too Large";
            case HTTP_414_REQUEST_URI_TOO_LONG:
                return "Request-URI Too Long";
            case HTTP_415_UNSUPPORTED_MEDIA_TYPE:
                return "Unsupported Media Type";
            case HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE:
                return "Requested Range Not Satisfiable";
            case HTTP_417_EXPECTATION_FAILED:
                return "Expectation Failed";

            case HTTP_500_INTERNAL_SERVER_ERROR:
                return "Internal Server Error";
            case HTTP_501_NOT_IMPLEMENTED:
                return "Not Implemented";
            case HTTP_502_BAD_GATEWAY:
                return "Bad Gateway";
            case HTTP_503_SERVICE_UNAVAILABLE:
                return "Service Unavailable";
            case HTTP_504_GATEWAY_TIMEOUT:
                return "Gateway Timeout";
            case HTTP_505_HTTP_VERSION_NOT_SUPPORTED:
                return "HTTP Version Not Supported";
        }
        return null;
    }

    private static final IntEnumResolver<YandexHttpStatus> R = IntEnumResolver.r(YandexHttpStatus.class);
}
