package patchless

import cats.syntax.either._
import io.circe.Decoder.Result
import io.circe.{Decoder, HCursor, Json, JsonObject}
import io.circe.generic.decoding.{DerivedDecoder, ReprDecoder}
import io.circe.generic.encoding.{DerivedObjectEncoder, ReprObjectEncoder}
import shapeless.{HList, LabelledGeneric}

package object circe {

  /**
    * This is necessary to allow patching optional fields: https://github.com/circe/circe/issues/304
    */
  implicit def decodeOptionOption[T](
    implicit decodeOpt: Decoder[Option[T]]
  ) : Decoder[Option[Option[T]]] = {
    Decoder.instance {
      cursor => if(cursor.focus == Json.Null) {
        Right(Some(None))
      } else decodeOpt.apply(cursor).map(Some(_))
    }
  }

  implicit def decodePatch[T, U <: HList](implicit
    patchable: Patchable.Aux[T, U],
    decodeU: ReprDecoder[U]
  ): DerivedDecoder[Patch[T]] = new DerivedDecoder[Patch[T]] {
    def apply(c: HCursor): Result[Patch[T]] = decodeU(c).map {
      updates =>
        Patch.ofUpdates[T, U](updates)
    }
  }

  implicit def encodePatch[T, U <: HList](implicit
    patchable: Patchable.Aux[T, U],
    encodeU: ReprObjectEncoder[U]
  ): DerivedObjectEncoder[Patch[T]] = new DerivedObjectEncoder[Patch[T]] {
    def encodeObject(a: Patch[T]): JsonObject = encodeU.encodeObject(a.patchUpdates)
  }

}