package com.eclipsesource.schema.internal.draft4.constraints

import com.eclipsesource.schema.{SchemaInteger, SchemaNumber, SchemaResolutionContext, SchemaType, SchemaValue}
import com.eclipsesource.schema.internal.{Keywords, SchemaUtil, ValidatorMessages}
import com.eclipsesource.schema.internal.constraints.Constraints._
import com.eclipsesource.schema.internal.validation.{Rule, VA}
import com.osinka.i18n.Lang
import play.api.libs.json.{JsNumber, JsString, JsValue}
import scalaz.Success

case class NumberConstraints4(min: Option[Minimum] = None,
                              max: Option[Maximum] = None,
                              multipleOf: Option[BigDecimal] = None,
                              format: Option[String] = None,
                              any: AnyConstraints = AnyConstraints4()
                             ) extends HasAnyConstraint with NumberConstraints {

  import com.eclipsesource.schema.internal.validators.NumberValidators._

  override def subSchemas: Set[SchemaType] = any.subSchemas

  override def resolvePath(path: String): Option[SchemaType] = path match {
    case Keywords.Number.Min => min.map(m => SchemaValue(JsNumber(m.min)))
    case Keywords.Number.Max => max.map(m => SchemaValue(JsNumber(m.max)))
    case Keywords.Number.MultipleOf => multipleOf.map(m => SchemaValue(JsNumber(m)))
    case Keywords.String.Format => format.map(f => SchemaValue(JsString(f)))
    case other => any.resolvePath(other)
  }

  def isInt(implicit lang: Lang): scalaz.Reader[SchemaResolutionContext, Rule[JsValue, JsValue]] =
    scalaz.Reader { context =>
      Rule.fromMapping {
        case json@JsNumber(number) if number.isWhole => Success(json)
        case other =>
          SchemaUtil.failure(
            Keywords.Any.Type,
            ValidatorMessages("err.expected.type", "integer", SchemaUtil.typeOfAsString(other)),
            context.schemaPath,
            context.instancePath,
            other
          )
      }
    }

  override def validate(schema: SchemaType, json: JsValue, context: SchemaResolutionContext)
                       (implicit lang: Lang): VA[JsValue] = {

    val reader = for {
      maxRule <- validateMax(max)
      minRule <- validateMin(min)
      multipleOfRule <- validateMultipleOf(multipleOf)
      format <- validateFormat(format)
    } yield maxRule |+| minRule |+| multipleOfRule |+| format

    schema match {
      case SchemaInteger(_) =>
        isInt.flatMap(x => reader.map(y => x |+| y))
          .run(context)
          .repath(_.compose(context.instancePath))
          .validate(json)
      case SchemaNumber(_) =>
        reader
          .run(context)
          .repath(_.compose(context.instancePath))
          .validate(json)
      case _ => Success(json)
    }
  }
}