package ru.yandex.qe.dispenser.client.v1.impl;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

import javax.ws.rs.BadRequestException;
import javax.ws.rs.ForbiddenException;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.NotAcceptableException;
import javax.ws.rs.NotAllowedException;
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.NotSupportedException;
import javax.ws.rs.ServiceUnavailableException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;

import org.apache.cxf.helpers.IOUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.jaxrs.ext.Nullable;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.jetbrains.annotations.NotNull;

class ErrorMessageLogger extends AbstractPhaseInterceptor<Message> {
    ErrorMessageLogger() {
        super(Phase.RECEIVE);
    }

    @Override
    public void handleMessage(@NotNull final Message message) throws Fault {
        final int status = getStatus(message);
        if (status >= Response.Status.OK.getStatusCode() && status < 300) {
            return;
        }
        final String body;
        try {
            body = getBody(message);
        } catch (IOException e) {
            throw new Fault(e);
        }
        throw toException(status, body);
    }

    @Override
    public void handleFault(@NotNull final Message message) {
        final Exception ex = message.getContent(Exception.class);
        if (ex instanceof RuntimeException) {
            throw (RuntimeException) ex;
        }
        if (ex != null) {
            throw new RuntimeException(ex);
        }
    }

    private int getStatus(@NotNull final Message message) {
        return (int) message.get(Message.RESPONSE_CODE);
    }

    /**
     * Reads response body from {@param message} and sets it back for ability to be read again.
     */
    @NotNull
    private String getBody(@NotNull final Message message) throws IOException {
        final InputStream inputStream = message.getContent(InputStream.class);
        final String content = IOUtils.toString(inputStream, StandardCharsets.UTF_8.name());
        message.setContent(InputStream.class, new ByteArrayInputStream(content.getBytes()));
        return content;
    }

    @Nullable
    @SuppressWarnings({"MagicNumber", "OverlyComplexMethod"})
    public static WebApplicationException toException(final int status, @NotNull final String message) {
        switch (status) {
            case 400:
                return new BadRequestException(message);
            case 401:
                return new NotAuthorizedException(message);
            case 403:
                return new ForbiddenException(message);
            case 404:
                return new NotFoundException(message);
            case 405:
                return new NotAllowedException(message);
            case 406:
                return new NotAcceptableException(message);
            case 415:
                return new NotSupportedException(message);
            case 500:
                return new InternalServerErrorException(message);
            case 503:
                return new ServiceUnavailableException(message);
            default:
                return new WebApplicationException(message);
        }
    }
}
