package ru.yandex.atom.frontend.error

import spray.http._
import spray.json.{JsString, JsArray, JsNumber, JsObject}

/**
 * @author avhaliullin
 */
case class ErrorDesc private(status: StatusCode, errorCode: String, message: String, params: JsObject, headers: List[HttpHeader] = Nil)

object ErrorDesc {

  // 5XX

  def ServiceUnavailable() = ErrorDesc(StatusCodes.ServiceUnavailable, "SERVICE_UNAVAILABLE",
    "Service temporarily unavailable", JsObject())

  def InternalServerError() = ErrorDesc(StatusCodes.InternalServerError, "INTERNAL_SERVER_ERROR",
    "Internal server error", JsObject())

  // 4XX

  def UrlListNotFound(id: Long) = ErrorDesc(StatusCodes.Forbidden, "URL_LIST_NOT_FOUND",
    s"Url list with id = $id not owned by current user",
    JsObject("listId" -> JsNumber(id)))

  def InvalidListSize(size: Long) = ErrorDesc(StatusCodes.UnprocessableEntity, "INVALID_URL_LIST_SIZE",
    s"Url list cannot be empty or contain more than 100 urls",
    JsObject("size" -> JsNumber(size)))

  def MalformedUrls(urls: Iterable[String]) = ErrorDesc(StatusCodes.UnprocessableEntity, "MALFORMED_URLS",
    s"Malformed urls found in request",
    JsObject("urls" -> JsArray(urls.map(JsString(_)).toList)))

  def UnknownResource() = ErrorDesc(StatusCodes.NotFound, "RESOURCE_NOT_FOUND",
    "Requested resource not found", JsObject())

  def AuthenticationRequired() = ErrorDesc(StatusCodes.Unauthorized, "AUTHENTICATION_REQUIRED",
    "Authentication required", JsObject())

  def AuthenticationFailed() = ErrorDesc(StatusCodes.Unauthorized, "AUTHENTICATION_FAILED",
    "Authentication failed", JsObject())

  def BadRequest(message: String) = ErrorDesc(StatusCodes.BadRequest, "BAD_REQUEST", message, JsObject())

  def ApplicationBadRequest(message: String) = ErrorDesc(StatusCodes.UnprocessableEntity, "UNPROCESSABLE_ENTITY", message, JsObject())

  def MethodNotAllowed(allowed: List[HttpMethod]) = ErrorDesc(StatusCodes.MethodNotAllowed, "METHOD_NOT_ALLOWED", "HTTP method not allowed",
    JsObject("allowed" -> JsArray(allowed.map(method => JsString(method.name)))), List(HttpHeaders.Allow(allowed: _*)))

  def RequiredParamMissed(param: String) = ErrorDesc(StatusCodes.BadRequest, "REQUIRED_PARAM_MISSED", s"Required param '$param' is missing",
    JsObject("param" -> JsString(param)))

  def UnacceptedResponseContentType(types: List[ContentType]) = ErrorDesc(
    StatusCodes.NotAcceptable, "UNACCEPTABLE_RESPONSE_CONTENT_TYPE",
    "Resource representation is only available with these Content-Types:\n" + types.map(_.value).mkString("\n"),
    JsObject("types" -> JsArray(types.map(tpe => JsString(tpe.value))))
  )

  def UnacceptedResponseContentEncoding(encodings: List[HttpEncoding]) = ErrorDesc(
    StatusCodes.NotAcceptable, "UNACCEPTABLE_RESPONSE_CONTENT_ENCODING",
    "Resource representation is only available with these Content-Encodings:\n" + encodings.map(_.value).mkString("\n"),
    JsObject("encodings" -> JsArray(encodings.map(encoding => JsString(encoding.value))))
  )

  def UnsupportedRequestContentType(types: List[String]) = ErrorDesc(
    StatusCodes.BadRequest, "UNSUPPORTED_REQUEST_CONTENT_TYPE",
    "Allowed request Content-Type: " + types.mkString(", "),
    JsObject("types" -> JsArray(types.map(JsString(_))))
  )

  def UnsupportedRequestContentEncoding(encodings: List[HttpEncoding]) = ErrorDesc(
    StatusCodes.BadRequest, "UNSUPPORTED_REQUEST_CONTENT_ENCODING",
    "The requests Content-Encoding must be one the following: " + encodings.map(_.value).mkString(", "),
    JsObject("encodings" -> JsArray(encodings.map(encoding => JsString(encoding.value))))
  )
}