/***********************************************************************
 * Copyright (c) 2013-2025 Commonwealth Computer Research, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Apache License, Version 2.0
 * which accompanies this distribution and is available at
 * http://www.opensource.org/licenses/apache2.0.php.
 ***********************************************************************/

package org.locationtech.geomesa.utils.geotools

import org.junit.runner.RunWith
import org.specs2.mutable.Specification
import org.specs2.runner.JUnitRunner

import java.io.StringWriter

@RunWith(classOf[JUnitRunner])
class GenerateRichFeatureModelsTest extends Specification {

  "GenerateRichFeatureModels" should {

    "generate implicit wrappers for simple feature types" in {
      val sft = SimpleFeatureTypes.createType("test1", "attr:String,dtg:Date,*geom:Point")

      val out = new StringWriter()
      GenerateRichFeatureModels.buildSingleClass(sft, "Test1", "com.example.geomesa", out)

      out.toString mustEqual
        """package com.example.geomesa
          |
          |import org.geotools.api.feature.simple.SimpleFeature
          |
          |// this class was generated by org.locationtech.geomesa.utils.geotools.GenerateRichFeatureModels$ for feature type test1
          |object Test1 {
          |
          |  implicit class RichFeature(val sf: SimpleFeature) extends AnyVal {
          |
          |    def getAttr: java.lang.String = sf.getAttribute(0).asInstanceOf[java.lang.String]
          |    def optAttr: Option[java.lang.String] = Option(getAttr)
          |    def setAttr(x: java.lang.String): Unit = sf.setAttribute(0, x)
          |
          |    def getDtg: java.util.Date = sf.getAttribute(1).asInstanceOf[java.util.Date]
          |    def optDtg: Option[java.util.Date] = Option(getDtg)
          |    def setDtg(x: java.util.Date): Unit = sf.setAttribute(1, x)
          |
          |    def getGeom: org.locationtech.jts.geom.Point = sf.getAttribute(2).asInstanceOf[org.locationtech.jts.geom.Point]
          |    def optGeom: Option[org.locationtech.jts.geom.Point] = Option(getGeom)
          |    def setGeom(x: org.locationtech.jts.geom.Point): Unit = sf.setAttribute(2, x)
          |
          |    def debug(): String = {
          |      import scala.collection.JavaConverters._
          |      val sb = new StringBuilder(s"${sf.getType.getTypeName}:${sf.getID}")
          |      sf.getProperties.asScala.foreach(p => sb.append(s"|${p.getName.getLocalPart}=${p.getValue}"))
          |      sb.toString()
          |    }
          |  }
          |}""".stripMargin
    }

    "support list and map types" in {
      val sft = SimpleFeatureTypes.createType("test1", "attr:List[String],dtg:Map[String,Date],*geom:Point")

      val out = new StringWriter()
      GenerateRichFeatureModels.buildSingleClass(sft, "Test1", "com.example.geomesa", out)

      out.toString mustEqual
        """package com.example.geomesa
          |
          |import org.geotools.api.feature.simple.SimpleFeature
          |
          |// this class was generated by org.locationtech.geomesa.utils.geotools.GenerateRichFeatureModels$ for feature type test1
          |object Test1 {
          |
          |  implicit class RichFeature(val sf: SimpleFeature) extends AnyVal {
          |
          |    def getAttr: java.util.List[java.lang.String] = sf.getAttribute(0).asInstanceOf[java.util.List[java.lang.String]]
          |    def optAttr: Option[java.util.List[java.lang.String]] = Option(getAttr)
          |    def setAttr(x: java.util.List[java.lang.String]): Unit = sf.setAttribute(0, x)
          |
          |    def getDtg: java.util.Map[java.lang.String,java.util.Date] = sf.getAttribute(1).asInstanceOf[java.util.Map[java.lang.String,java.util.Date]]
          |    def optDtg: Option[java.util.Map[java.lang.String,java.util.Date]] = Option(getDtg)
          |    def setDtg(x: java.util.Map[java.lang.String,java.util.Date]): Unit = sf.setAttribute(1, x)
          |
          |    def getGeom: org.locationtech.jts.geom.Point = sf.getAttribute(2).asInstanceOf[org.locationtech.jts.geom.Point]
          |    def optGeom: Option[org.locationtech.jts.geom.Point] = Option(getGeom)
          |    def setGeom(x: org.locationtech.jts.geom.Point): Unit = sf.setAttribute(2, x)
          |
          |    def debug(): String = {
          |      import scala.collection.JavaConverters._
          |      val sb = new StringBuilder(s"${sf.getType.getTypeName}:${sf.getID}")
          |      sf.getProperties.asScala.foreach(p => sb.append(s"|${p.getName.getLocalPart}=${p.getValue}"))
          |      sb.toString()
          |    }
          |  }
          |}""".stripMargin
    }

    "work with invalid attribute names" in {
      val sft = SimpleFeatureTypes.createType("test1", "a#^2:String,@dt!g:Date,*geom:Point")
      val out = new StringWriter()
      GenerateRichFeatureModels.buildSingleClass(sft, "Test1", "com.example.geomesa", out)

      out.toString mustEqual
          """package com.example.geomesa
            |
            |import org.geotools.api.feature.simple.SimpleFeature
            |
            |// this class was generated by org.locationtech.geomesa.utils.geotools.GenerateRichFeatureModels$ for feature type test1
            |object Test1 {
            |
            |  implicit class RichFeature(val sf: SimpleFeature) extends AnyVal {
            |
            |    def getA2: java.lang.String = sf.getAttribute(0).asInstanceOf[java.lang.String]
            |    def optA2: Option[java.lang.String] = Option(getA2)
            |    def setA2(x: java.lang.String): Unit = sf.setAttribute(0, x)
            |
            |    def getDtG: java.util.Date = sf.getAttribute(1).asInstanceOf[java.util.Date]
            |    def optDtG: Option[java.util.Date] = Option(getDtG)
            |    def setDtG(x: java.util.Date): Unit = sf.setAttribute(1, x)
            |
            |    def getGeom: org.locationtech.jts.geom.Point = sf.getAttribute(2).asInstanceOf[org.locationtech.jts.geom.Point]
            |    def optGeom: Option[org.locationtech.jts.geom.Point] = Option(getGeom)
            |    def setGeom(x: org.locationtech.jts.geom.Point): Unit = sf.setAttribute(2, x)
            |
            |    def debug(): String = {
            |      import scala.collection.JavaConverters._
            |      val sb = new StringBuilder(s"${sf.getType.getTypeName}:${sf.getID}")
            |      sf.getProperties.asScala.foreach(p => sb.append(s"|${p.getName.getLocalPart}=${p.getValue}"))
            |      sb.toString()
            |    }
            |  }
            |}""".stripMargin
    }
  }
}