package org.zalando.problem;

import org.apiguardian.api.API;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.util.Arrays;
import java.util.Collections;
import java.util.Map;

import static java.util.function.Function.identity;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toMap;
import static org.apiguardian.api.API.Status.MAINTAINED;

/**
 * Commonly used status codes defined by HTTP, see <a href=
 * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10">HTTP/1.1
 * documentation</a> for the complete list. Additional status codes can be added
 * by applications by creating an implementation of StatusType.
 */
@API(status = MAINTAINED)
public enum Status implements StatusType {

    /**
     * @see <a href="http://tools.ietf.org/html/rfc7231#section-6.2.1">HTTP/1.1:
     *      Semantics and Content, section 6.2.1</a>
     */
    CONTINUE(100, "Continue"),
    /**
     * @see <a href="http://tools.ietf.org/html/rfc7231#section-6.2.2">HTTP/1.1:
     *      Semantics and Content, section 6.2.2</a>
     */
    SWITCHING_PROTOCOLS(101, "Switching Protocols"),
    /**
     * @see <a href="http://tools.ietf.org/html/rfc2518#section-10.1">WebDAV</a>
     */
    PROCESSING(102, "Processing"),
    /**
     * @see <a href=
     *      "http://code.google.com/p/gears/wiki/ResumableHttpRequestsProposal">A
     *      proposal for supporting resumable POST/PUT HTTP requests in HTTP/1.0</a>
     */
    CHECKPOINT(103, "Checkpoint"),
    /**
     * 200 OK, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">HTTP/1.1
     * documentation</a>.
     */
    OK(200, "OK"),
    /**
     * 201 Created, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.2">HTTP/1.1
     * documentation</a>.
     */
    CREATED(201, "Created"),
    /**
     * 202 Accepted, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.3">HTTP/1.1
     * documentation</a>.
     */
    ACCEPTED(202, "Accepted"),
    /**
     * @see <a href="http://tools.ietf.org/html/rfc7231#section-6.3.4">HTTP/1.1:
     *      Semantics and Content, section 6.3.4</a>
     */
    NON_AUTHORITATIVE_INFORMATION(203, "Non-Authoritative Information"),
    /**
     * 204 No Content, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.5">HTTP/1.1
     * documentation</a>.
     */
    NO_CONTENT(204, "No Content"),
    /**
     * 205 Reset Content, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.6">HTTP/1.1
     * documentation</a>.
     */
    RESET_CONTENT(205, "Reset Content"),
    /**
     * 206 Reset Content, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.7">HTTP/1.1
     * documentation</a>.
     */
    PARTIAL_CONTENT(206, "Partial Content"),
    /**
     * @see <a href="http://tools.ietf.org/html/rfc4918#section-13">WebDAV</a>
     */
    MULTI_STATUS(207, "Multi-Status"),
    /**
     * @see <a href="http://tools.ietf.org/html/rfc5842#section-7.1">WebDAV Binding
     *      Extensions</a>
     */
    ALREADY_REPORTED(208, "Already Reported"),
    /**
     * @see <a href="http://tools.ietf.org/html/rfc3229#section-10.4.1">Delta
     *      encoding in HTTP</a>
     */
    IM_USED(226, "IM Used"),
    /**
     * @see <a href="http://tools.ietf.org/html/rfc7231#section-6.4.1">HTTP/1.1:
     *      Semantics and Content, section 6.4.1</a>
     */
    MULTIPLE_CHOICES(300, "Multiple Choices"),
    /**
     * 301 Moved Permanently, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.2">HTTP/1.1
     * documentation</a>.
     */
    MOVED_PERMANENTLY(301, "Moved Permanently"),
    /**
     * 302 Found, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.3">HTTP/1.1
     * documentation</a>.
     */
    FOUND(302, "Found"),
    /**
     * 303 See Other, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4">HTTP/1.1
     * documentation</a>.
     */
    SEE_OTHER(303, "See Other"),
    /**
     * 304 Not Modified, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5">HTTP/1.1
     * documentation</a>.
     */
    NOT_MODIFIED(304, "Not Modified"),
    /**
     * 305 Use Proxy, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.6">HTTP/1.1
     * documentation</a>.
     */
    USE_PROXY(305, "Use Proxy"),
    /**
     * 307 Temporary Redirect, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.8">HTTP/1.1
     * documentation</a>.
     */
    TEMPORARY_REDIRECT(307, "Temporary Redirect"),
    /**
     * @see <a href="http://tools.ietf.org/html/rfc7238">RFC 7238</a>
     */
    PERMANENT_REDIRECT(308, "Permanent Redirect"),
    /**
     * 400 Bad Request, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">HTTP/1.1
     * documentation</a>.
     */
    BAD_REQUEST(400, "Bad Request"),
    /**
     * 401 Unauthorized, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">HTTP/1.1
     * documentation</a>.
     */
    UNAUTHORIZED(401, "Unauthorized"),
    /**
     * 402 Payment Required, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.3">HTTP/1.1
     * documentation</a>.
     */
    PAYMENT_REQUIRED(402, "Payment Required"),
    /**
     * 403 Forbidden, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4">HTTP/1.1
     * documentation</a>.
     */
    FORBIDDEN(403, "Forbidden"),
    /**
     * 404 Not Found, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5">HTTP/1.1
     * documentation</a>.
     */
    NOT_FOUND(404, "Not Found"),
    /**
     * 405 Method Not Allowed, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.6">HTTP/1.1
     * documentation</a>.
     */
    METHOD_NOT_ALLOWED(405, "Method Not Allowed"),
    /**
     * 406 Not Acceptable, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7">HTTP/1.1
     * documentation</a>.
     */
    NOT_ACCEPTABLE(406, "Not Acceptable"),
    /**
     * 407 Proxy Authentication Required, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.8">HTTP/1.1
     * documentation</a>.
     */
    PROXY_AUTHENTICATION_REQUIRED(407, "Proxy Authentication Required"),
    /**
     * 408 Request Timeout, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.9">HTTP/1.1
     * documentation</a>.
     */
    REQUEST_TIMEOUT(408, "Request Timeout"),
    /**
     * 409 Conflict, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10">HTTP/1.1
     * documentation</a>.
     */
    CONFLICT(409, "Conflict"),
    /**
     * 410 Gone, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.11">HTTP/1.1
     * documentation</a>.
     */
    GONE(410, "Gone"),
    /**
     * 411 Length Required, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.12">HTTP/1.1
     * documentation</a>.
     */
    LENGTH_REQUIRED(411, "Length Required"),
    /**
     * 412 Precondition Failed, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.13">HTTP/1.1
     * documentation</a>.
     */
    PRECONDITION_FAILED(412, "Precondition Failed"),
    /**
     * 413 Request Entity Too Large, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.14">HTTP/1.1
     * documentation</a>.
     */
    REQUEST_ENTITY_TOO_LARGE(413, "Request Entity Too Large"),
    /**
     * 414 Request-URI Too Long, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.15">HTTP/1.1
     * documentation</a>.
     */
    REQUEST_URI_TOO_LONG(414, "Request-URI Too Long"),
    /**
     * 415 Unsupported Media Type, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.16">HTTP/1.1
     * documentation</a>.
     */
    UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"),
    /**
     * 416 Requested Range Not Satisfiable, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.17">HTTP/1.1
     * documentation</a>.
     */
    REQUESTED_RANGE_NOT_SATISFIABLE(416, "Requested Range Not Satisfiable"),
    /**
     * 417 Expectation Failed, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.18">HTTP/1.1
     * documentation</a>.
     */
    EXPECTATION_FAILED(417, "Expectation Failed"),
    /**
     * @see <a href=
     *      "http://tools.ietf.org/html/rfc2324#section-2.3.2">HTCPCP/1.0</a>
     */
    I_AM_A_TEAPOT(418, "I'm a teapot"),
    /**
     * @see <a href="http://tools.ietf.org/html/rfc4918#section-11.2">WebDAV</a>
     */
    UNPROCESSABLE_ENTITY(422, "Unprocessable Entity"),
    /**
     * @see <a href="http://tools.ietf.org/html/rfc4918#section-11.3">WebDAV</a>
     */
    LOCKED(423, "Locked"),
    /**
     * @see <a href="http://tools.ietf.org/html/rfc4918#section-11.4">WebDAV</a>
     */
    FAILED_DEPENDENCY(424, "Failed Dependency"),
    /**
     * @see <a href="http://tools.ietf.org/html/rfc2817#section-6">Upgrading to TLS
     *      Within HTTP/1.1</a>
     */
    UPGRADE_REQUIRED(426, "Upgrade Required"),
    /**
     * @see <a href="http://tools.ietf.org/html/rfc6585#section-3">Additional HTTP
     *      Status Codes</a>
     */
    PRECONDITION_REQUIRED(428, "Precondition Required"),
    /**
     * @see <a href="http://tools.ietf.org/html/rfc6585#section-4">Additional HTTP
     *      Status Codes</a>
     */
    TOO_MANY_REQUESTS(429, "Too Many Requests"),
    /**
     * @see <a href="http://tools.ietf.org/html/rfc6585#section-5">Additional HTTP
     *      Status Codes</a>
     */
    REQUEST_HEADER_FIELDS_TOO_LARGE(431, "Request Header Fields Too Large"),
    /**
     * @see <a href="https://tools.ietf.org/html/rfc7725#section-3">Unavailable For
     *      Legal Reasons</a>
     */
    UNAVAILABLE_FOR_LEGAL_REASONS(451, "Unavailable For Legal Reasons"),
    /**
     * 500 Internal Server Error, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1">HTTP/1.1
     * documentation</a>.
     */
    INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
    /**
     * 501 Not Implemented, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.2">HTTP/1.1
     * documentation</a>.
     */
    NOT_IMPLEMENTED(501, "Not Implemented"),
    /**
     * 502 Bad Gateway, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.3">HTTP/1.1
     * documentation</a>.
     */
    BAD_GATEWAY(502, "Bad Gateway"),
    /**
     * 503 Service Unavailable, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4">HTTP/1.1
     * documentation</a>.
     */
    SERVICE_UNAVAILABLE(503, "Service Unavailable"),
    /**
     * 504 Gateway Timeout, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.5">HTTP/1.1
     * documentation</a>.
     */
    GATEWAY_TIMEOUT(504, "Gateway Timeout"),
    /**
     * 505 HTTP Version Not Supported, see <a href=
     * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.6">HTTP/1.1
     * documentation</a>.
     */
    HTTP_VERSION_NOT_SUPPORTED(505, "HTTP Version Not Supported"),
    /**
     * @see <a href="http://tools.ietf.org/html/rfc2295#section-8.1">Transparent
     *      Content Negotiation</a>
     */
    VARIANT_ALSO_NEGOTIATES(506, "Variant Also Negotiates"),
    /**
     * @see <a href="http://tools.ietf.org/html/rfc4918#section-11.5">WebDAV</a>
     */
    INSUFFICIENT_STORAGE(507, "Insufficient Storage"),
    /**
     * @see <a href="http://tools.ietf.org/html/rfc5842#section-7.2">WebDAV Binding
     *      Extensions</a>
     */
    LOOP_DETECTED(508, "Loop Detected"),
    /**
     * {@code 509 Bandwidth Limit Exceeded}
     */
    BANDWIDTH_LIMIT_EXCEEDED(509, "Bandwidth Limit Exceeded"),
    /**
     * @see <a href="http://tools.ietf.org/html/rfc2774#section-7">HTTP Extension
     *      Framework</a>
     */
    NOT_EXTENDED(510, "Not Extended"),
    /**
     * @see <a href="http://tools.ietf.org/html/rfc6585#section-6">Additional HTTP
     *      Status Codes</a>
     */
    NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required");

    private static final Map<Integer, Status> STATUSES = Arrays.stream(values())
            .collect(collectingAndThen(toMap(Status::getStatusCode, identity()), Collections::unmodifiableMap));

    private final int code;
    private final String reason;

    Status(final int statusCode, final String reasonPhrase) {
        this.code = statusCode;
        this.reason = reasonPhrase;
    }

    /**
     * Get the associated status code.
     *
     * @return the status code.
     */
    @Override
    public int getStatusCode() {
        return code;
    }

    /**
     * Get the reason phrase.
     *
     * @return the reason phrase.
     */
    @Override
    @NonNull
    public String getReasonPhrase() {
        return reason;
    }

    /**
     * Get the Status String representation.
     *
     * @return the status code and reason.
     */
    @Override
    public String toString() {
        return getStatusCode() + " " + getReasonPhrase();
    }

    /**
     * Creates a Status instance from the given code.
     *
     * @param code the HTTP code as a number
     * @return the correct enum value for this status code.
     * @throws IllegalArgumentException if the given code does not correspond to a known HTTP status.
     */
    public static Status valueOf(final int code) {
        @Nullable final Status status = STATUSES.get(code);

        if (status == null) {
            throw new IllegalArgumentException("There is no known status for this code (" + code + ").");
        }

        return status;
    }

}
