package ru.yandex.webmaster3.api.http.rest.response;

import com.fasterxml.jackson.annotation.JsonIgnore;
import ru.yandex.autodoc.common.doc.annotation.Description;
import ru.yandex.webmaster3.api.http.rest.response.errors.ApiErrorCode;
import ru.yandex.webmaster3.api.http.rest.response.errors.ApiErrorResponse;
import ru.yandex.webmaster3.api.http.rest.response.meta.ResponseWithAllow;
import ru.yandex.webmaster3.api.http.rest.response.meta.ResponseWithLocation;
import ru.yandex.webmaster3.api.http.rest.response.meta.ResponseWithWWWAuthenticate;
import ru.yandex.webmaster3.api.http.rest.response.meta.ResponseWithoutEntity;

import java.util.Collection;
import java.util.Set;

/**
 * @author avhaliullin
 */
public interface ApiResponse {
    interface AnyMethodResponse extends ApiGetResponse, ApiDeleteResponse, ApiPostResponse {
        @Override
        HttpStatus getStatus();
    }

    @JsonIgnore
    HttpStatus getStatus();

    abstract class Success200 implements ApiGetResponse {
        @Override
        public final HttpStatus.Http200 getStatus() {
            return HttpStatus.HTTP_200;
        }
    }

    abstract class Created201<L> implements ApiPostResponse, ResponseWithLocation<L> {
        @Override
        public final HttpStatus.Http201 getStatus() {
            return HttpStatus.HTTP_201;
        }
    }

    abstract class Accepted202 implements ApiPostResponse {
        @Override
        public HttpStatus.Http202 getStatus() {
            return HttpStatus.HTTP_202;
        }
    }

    abstract class NoContent204 implements ApiDeleteResponse, ResponseWithoutEntity {
        @Override
        public final HttpStatus.Http204 getStatus() {
            return HttpStatus.HTTP_204;
        }
    }

    // 4XX

    abstract class BadRequest400<C extends Enum<C> & ApiErrorCode> extends ApiErrorResponse.AbstractErrorResponse<C>
            implements AnyMethodResponse {
        public BadRequest400(C code, String message) {
            super(code, message);
        }

        @Override
        public final HttpStatus.Http400 getStatus() {
            return HttpStatus.HTTP_400;
        }
    }

    abstract class Unauthorized401<C extends Enum<C> & ApiErrorCode> extends ApiErrorResponse.AbstractErrorResponse<C>
            implements ResponseWithWWWAuthenticate, AnyMethodResponse {
        public Unauthorized401(C code, String message) {
            super(code, message);
        }

        @Override
        public final HttpStatus.Http401 getStatus() {
            return HttpStatus.HTTP_401;
        }
    }

    abstract class Forbidden403<C extends Enum<C> & ApiErrorCode> extends ApiErrorResponse.AbstractErrorResponse<C>
            implements AnyMethodResponse {
        public Forbidden403(C code, String message) {
            super(code, message);
        }

        @Override
        public HttpStatus.Http403 getStatus() {
            return HttpStatus.HTTP_403;
        }
    }

    abstract class NotFound404<C extends Enum<C> & ApiErrorCode> extends ApiErrorResponse.AbstractErrorResponse<C>
            implements AnyMethodResponse {
        public NotFound404(C code, String message) {
            super(code, message);
        }

        @Override
        public final HttpStatus.Http404 getStatus() {
            return HttpStatus.HTTP_404;
        }
    }

    abstract class MethodNotAllowed405<C extends Enum<C> & ApiErrorCode> extends ApiErrorResponse.AbstractErrorResponse<C>
            implements ApiResponse.AnyMethodResponse, ResponseWithAllow {
        private final Set<String> allowedMethods;

        public MethodNotAllowed405(C code, Set<String> allowedMethods) {
            super(code, "Method not allowed");
            this.allowedMethods = allowedMethods;
        }

        @Override
        public Set<String> getAllowedMethods() {
            return allowedMethods;
        }

        @Override
        public final HttpStatus.Http405 getStatus() {
            return HttpStatus.HTTP_405;
        }
    }

    abstract class NotAcceptable406<C extends Enum<C> & ApiErrorCode> extends ApiErrorResponse.AbstractErrorResponse<C>
            implements AnyMethodResponse {
        private final Collection<String> acceptableTypes;

        public NotAcceptable406(C code, Collection<String> acceptableTypes) {
            super(code, "Not acceptable");
            this.acceptableTypes = acceptableTypes;
        }

        @Description("List of acceptable content types")
        public Collection<String> getAcceptableTypes() {
            return acceptableTypes;
        }

        @Override
        public final HttpStatus.Http406 getStatus() {
            return HttpStatus.HTTP_406;
        }
    }

    abstract class Conflict409<C extends Enum<C> & ApiErrorCode> extends ApiErrorResponse.AbstractErrorResponse<C>
            implements ApiPostResponse {
        public Conflict409(C code, String message) {
            super(code, message);
        }

        @Override
        public HttpStatus.Http409 getStatus() {
            return HttpStatus.HTTP_409;
        }
    }

    abstract class Gone410<C extends Enum<C> & ApiErrorCode> extends ApiErrorResponse.AbstractErrorResponse<C>
            implements AnyMethodResponse {
        public Gone410(C code, String message) {
            super(code, message);
        }

        @Override
        public HttpStatus.Http410 getStatus() {
            return HttpStatus.HTTP_410;
        }
    }

    abstract class UnsupportedMediaType415<C extends Enum<C> & ApiErrorCode> extends ApiErrorResponse.AbstractErrorResponse<C>
            implements ApiPostResponse {

        public UnsupportedMediaType415(C code, String message) {
            super(code, message);
        }

        @Override
        public final HttpStatus.Http415 getStatus() {
            return HttpStatus.HTTP_415;
        }
    }

    abstract class UnprocessableEntity422<C extends Enum<C> & ApiErrorCode> extends ApiErrorResponse.AbstractErrorResponse<C>
            implements ApiPostResponse {
        public UnprocessableEntity422(C code, String message) {
            super(code, message);
        }

        @Override
        public final HttpStatus.Http422 getStatus() {
            return HttpStatus.HTTP_422;
        }
    }

    abstract class TooManyRequests429<C extends Enum<C> & ApiErrorCode> extends ApiErrorResponse.AbstractErrorResponse<C>
            implements AnyMethodResponse {
        public TooManyRequests429(C code, String message) {
            super(code, message);
        }

        @Override
        public HttpStatus.Http429 getStatus() {
            return HttpStatus.HTTP_429;
        }
    }

    abstract class RequestEntityTooLarge413<C extends Enum<C> & ApiErrorCode> extends ApiErrorResponse.AbstractErrorResponse<C>
            implements ApiPostResponse {
        public RequestEntityTooLarge413(C code, String message) {
            super(code, message);
        }

        @Override
        public final HttpStatus.Http413 getStatus() {
            return HttpStatus.HTTP_413;
        }
    }

    // 5xx

    abstract class InternalServerError500<C extends Enum<C> & ApiErrorCode> extends ApiErrorResponse.AbstractErrorResponse<C>
            implements AnyMethodResponse {
        public InternalServerError500(C code, String message) {
            super(code, message);
        }

        @Override
        public final HttpStatus.Http500 getStatus() {
            return HttpStatus.HTTP_500;
        }
    }
}
