package eu.xeppaka.scalalearn

import java.util.Currency

import org.bson.{BsonReader, BsonWriter}
import org.bson.codecs.{Codec, DecoderContext, EncoderContext}
import org.mongodb.scala.MongoClient
import org.mongodb.scala.bson.codecs.Macros._
import org.mongodb.scala.bson.codecs.DEFAULT_CODEC_REGISTRY
import org.bson.codecs.configuration.CodecRegistries.{fromProviders, fromRegistries, fromCodecs}

import scala.concurrent.duration._
import scala.concurrent.Await

object Main5 {
  sealed class EventData {
    def idAccount: String = ???
  }
  case class Event(pid: String, sn: Long, data: EventData)

  case class Value(v: Int)
  case class AccountV1(override val idAccount: String, amount: Int, v: Value) extends EventData
  case class AccountV2(override val idAccount: String, amount: Int) extends EventData

  case class Class23(
    field1: String = "field1",
    field2: String = "field1",
    field3: String = "field1",
    field4: String = "field1",
    field5: String = "field1",
    field6: String = "field1",
    field7: String = "field1",
    field8: String = "field1",
    field9: String = "field1",
    field10: String = "field1",
    field11: String = "field1",
    field12: String = "field1",
    field13: String = "field1",
    field14: String = "field1",
    field15: String = "field1",
    field16: String = "field1",
    field17: String = "field1",
    field18: String = "field1",
    field19: String = "field1",
    field20: String = "field1",
    field21: String = "field1",
    field22: String = "field1",
    field23: String = "field1",
    field24: String = "field1",
    field25: String = "field1",
    field26: String = "field1",
    field27: String = "field1",
    field28: String = "field1",
    field29: String = "field1",
    field30: String = "field1",
    field31: String = "field1"
  ) extends EventData

  class ValueCodec extends Codec[Value] {
    override def decode(reader: BsonReader, decoderContext: DecoderContext): Value = Value(reader.readInt32())
    override def encode(writer: BsonWriter, value: Value, encoderContext: EncoderContext): Unit = writer.writeInt32(value.v)
    override def getEncoderClass: Class[Value] = classOf[Value]
  }

  class CurrencyCodec extends Codec[Currency] {
    override def decode(reader: BsonReader, decoderContext: DecoderContext): Currency = Currency.getInstance(reader.readString())
    override def encode(writer: BsonWriter, value: Currency, encoderContext: EncoderContext): Unit = writer.writeString(value.getCurrencyCode)
    override def getEncoderClass: Class[Currency] = classOf[Currency]
  }

  class Class23Codec extends Codec[Class23] {
    override def encode(writer: BsonWriter, value: Class23, encoderContext: EncoderContext): Unit = {
      writer.writeStartDocument()
      writer.writeString("field1", value.field1)
      writer.writeEndDocument()
    }
    override def decode(reader: BsonReader, decoderContext: DecoderContext): Class23 = {
      reader.readStartDocument()
      val field1 = reader.readString("field1")
      reader.readEndDocument()
      Class23(field1 = field1)
    }
    override def getEncoderClass: Class[Class23] = classOf[Class23]
  }

  def main(args: Array[String]): Unit = {
    val codecRegistry = fromRegistries(fromCodecs(new ValueCodec, new CurrencyCodec, new Class23Codec), fromProviders(classOf[Event], classOf[EventData]), DEFAULT_CODEC_REGISTRY)
    val client = MongoClient()
    val db = client.getDatabase("hierarchy-test").withCodecRegistry(codecRegistry)
    val accounts = db.getCollection[Event]("accounts")
    val fieldColl = db.getCollection[Class23]("fields")

    Await.result(fieldColl.insertOne(Class23()).toFuture(), 1.second)
//    Await.result(accounts.insertOne(Event("001", 1, Class23())).toFuture(), 1.second)
//    Await.result(accounts.insertOne(Event("001", 1, AccountV1("000001", 30, Value(10)))).toFuture(), 1.second)
//    Await.result(accounts.insertOne(Event("001", 2, AccountV2("000002", 20, "EUR"))).toFuture(), 1.second)

    val res = Await.result(accounts.find().toFuture(), 1.second)
    res.foreach(println)

    client.close()
  }
}
