package ru.yandex.atom.frontend.error

import spray.routing._
import spray.routing.directives.RouteDirectives._
import spray.http.StatusCodes._
import spray.routing.AuthenticationFailedRejection.{CredentialsRejected, CredentialsMissing}
import spray.http.HttpHeaders.Allow
import spray.routing.UnacceptedResponseContentTypeRejection
import spray.routing.MissingFormFieldRejection
import spray.routing.UnsupportedRequestContentTypeRejection
import spray.routing.MalformedHeaderRejection
import spray.routing.UnacceptedResponseEncodingRejection
import spray.routing.MalformedRequestContentRejection
import spray.routing.SchemeRejection
import spray.routing.MalformedQueryParamRejection
import spray.routing.UnsupportedRequestEncodingRejection
import spray.routing.CorruptRequestEncodingRejection
import spray.routing.MethodRejection
import spray.routing.MalformedFormFieldRejection
import spray.routing.MissingQueryParamRejection
import spray.httpx.marshalling.ToResponseMarshaller

/**
 * @author avhaliullin
 */
object Rejection {
  def Handler(implicit errorDescMarshaller: ToResponseMarshaller[ErrorDesc]) = RejectionHandler {
    case Nil => complete(ErrorDesc.UnknownResource())

    case AuthenticationFailedRejection(cause, challengeHeaders) :: _ =>
      val desc = cause match {
        case CredentialsMissing => ErrorDesc.AuthenticationRequired()
        case CredentialsRejected => ErrorDesc.AuthenticationFailed()
      }
      ctx => ctx.complete(desc)

    //    case AuthorizationFailedRejection :: _ =>

    case CorruptRequestEncodingRejection(msg) :: _ =>
      complete(ErrorDesc.BadRequest("The requests encoding is corrupt:\n" + msg))

    case MalformedFormFieldRejection(name, msg, _) :: _ =>
      complete(ErrorDesc.BadRequest("The form field '" + name + "' was malformed:\n" + msg))

    case MalformedHeaderRejection(headerName, msg, _) :: _ =>
      complete(ErrorDesc.BadRequest(s"The value of HTTP header '$headerName' was malformed:\n" + msg))

    case MalformedQueryParamRejection(name, msg, _) :: _ =>
      complete(ErrorDesc.BadRequest("The query parameter '" + name + "' was malformed:\n" + msg))

    case MalformedRequestContentRejection(msg, _) :: _ =>
      complete(ErrorDesc.BadRequest("The request content was malformed:\n" + msg))

    case rejections@(MethodRejection(_) :: _) =>
      val methods = rejections.collect {
        case MethodRejection(method) => method
      }
      complete(ErrorDesc.MethodNotAllowed(methods))

    //    case rejections@(SchemeRejection(_) :: _) =>

    //    case MissingCookieRejection(cookieName) :: _ =>

    case MissingFormFieldRejection(fieldName) :: _ =>
      complete(ErrorDesc.RequiredParamMissed(fieldName))

    //    case MissingHeaderRejection(headerName) :: _ =>

    case MissingQueryParamRejection(paramName) :: _ =>
      complete(ErrorDesc.RequiredParamMissed(paramName))

    case RequestEntityExpectedRejection :: _ =>
      complete(ErrorDesc.BadRequest("Request entity expected but not supplied"))

    //    case TooManyRangesRejection(_) :: _ =>

    //    case UnsatisfiableRangeRejection(unsatisfiableRanges, actualEntityLength) :: _ =>

    case rejections@(UnacceptedResponseContentTypeRejection(_) :: _) =>
      val supported = rejections.flatMap {
        case UnacceptedResponseContentTypeRejection(types) => types
        case _ => Nil
      }
      complete(ErrorDesc.UnacceptedResponseContentType(supported))

    case rejections@(UnacceptedResponseEncodingRejection(_) :: _) =>
      val supported = rejections.collect {
        case UnacceptedResponseEncodingRejection(encodings) => encodings
      }
      complete(ErrorDesc.UnacceptedResponseContentEncoding(supported))

    case rejections@(UnsupportedRequestContentTypeRejection(_) :: _) =>
      val supported = rejections.collect {
        case UnsupportedRequestContentTypeRejection(types) => types
      }
      complete(ErrorDesc.UnsupportedRequestContentType(supported))

    case rejections@(UnsupportedRequestEncodingRejection(_) :: _) =>
      val supported = rejections.collect {
        case UnsupportedRequestEncodingRejection(encodings) => encodings
      }
      complete(ErrorDesc.UnsupportedRequestContentEncoding(supported))

    //    case ValidationRejection(msg, _) :: _ =>
  }
}
