/*
 * Copyright (C) 2009-2017 Lightbend Inc. <https://www.lightbend.com>
 */
package scalaguide.testing.database

import java.sql.SQLException

import org.specs2.mutable.Specification

class ScalaTestingWithDatabases extends Specification {

  // We don't test this code because otherwise it would try to connect to MySQL
  class NotTested {
    {
      //#database
      import play.api.db.Databases

      val database = Databases(
        driver = "com.mysql.jdbc.Driver",
        url = "jdbc:mysql://localhost/test"
      )
      //#database
    }

    {
      //#full-config
      import play.api.db.Databases

      val database = Databases(
        driver = "com.mysql.jdbc.Driver",
        url = "jdbc:mysql://localhost/test",
        name = "mydatabase",
        config = Map(
          "username" -> "test",
          "password" -> "secret"
        )
      )
      //#full-config

      //#shutdown
      database.shutdown()
      //#shutdown
    }

    {
      //#with-database
      import play.api.db.Databases

      Databases.withDatabase(
        driver = "com.mysql.jdbc.Driver",
        url = "jdbc:mysql://localhost/test"
      ) { database =>
        val connection = database.getConnection()
        // ...
      }
      //#with-database
    }

    {
      //#custom-with-database
      import play.api.db.{Database, Databases}

      def withMyDatabase[T](block: Database => T) = {
        Databases.withDatabase(
          driver = "com.mysql.jdbc.Driver",
          url = "jdbc:mysql://localhost/test",
          name = "mydatabase",
          config = Map(
            "username" -> "test",
            "password" -> "secret"
          )
        )(block)
      }
      //#custom-with-database

      //#custom-with-database-use
      withMyDatabase { database =>
        val connection = database.getConnection()
        // ...
      }
      //#custom-with-database-use
    }

  }

  "database helpers" should {
    "allow connecting to an in memory database" in {
      //#in-memory
      import play.api.db.Databases

      val database = Databases.inMemory()
      //#in-memory

      try {
        database.getConnection().getMetaData.getDatabaseProductName must_== "H2"
      } finally {
        database.shutdown()
      }
    }

    "allow connecting to an in memory database with more options" in {
      //#in-memory-full-config
      import play.api.db.Databases

      val database = Databases.inMemory(
        name = "mydatabase",
        urlOptions = Map(
          "MODE" -> "MYSQL"
        ),
        config = Map(
          "logStatements" -> true
        )
      )
      //#in-memory-full-config

      try {
        database.getConnection().getMetaData.getDatabaseProductName must_== "H2"
      } finally {
        //#in-memory-shutdown
        database.shutdown()
        //#in-memory-shutdown
      }
    }
    
    "manage an in memory database for the user" in {
      //#with-in-memory
      import play.api.db.Databases

      Databases.withInMemory() { database =>
        val connection = database.getConnection()
        
        // ...
      }
      //#with-in-memory
      ok
    }

    "manage an in memory database for the user with custom config" in {
      //#with-in-memory-custom
      import play.api.db.{Database, Databases}

      def withMyDatabase[T](block: Database => T) = {
        Databases.withInMemory(
          name = "mydatabase",
          urlOptions = Map(
            "MODE" -> "MYSQL"
          ),
          config = Map(
            "logStatements" -> true
          )
        )(block)
      }
      //#with-in-memory-custom

      withMyDatabase(_.getConnection().getMetaData.getDatabaseProductName must_== "H2")
    }

    "allow running evolutions" in play.api.db.Databases.withInMemory() { database =>
      //#apply-evolutions
      import play.api.db.evolutions._

      Evolutions.applyEvolutions(database)
      //#apply-evolutions

      //#cleanup-evolutions
      Evolutions.cleanupEvolutions(database)
      //#cleanup-evolutions
      ok
    }

    "allow running static evolutions" in play.api.db.Databases.withInMemory() { database =>
      //#apply-evolutions-simple
      import play.api.db.evolutions._

      Evolutions.applyEvolutions(database, SimpleEvolutionsReader.forDefault(
        Evolution(
          1,
          "create table test (id bigint not null, name varchar(255));",
          "drop table test;"
        )
      ))
      //#apply-evolutions-simple

      val connection = database.getConnection()
      connection.prepareStatement("insert into test values (10, 'testing')").execute()
      
      //#cleanup-evolutions-simple
      Evolutions.cleanupEvolutions(database)
      //#cleanup-evolutions-simple
      
      connection.prepareStatement("select * from test").executeQuery() must throwAn[SQLException]
    }
    
    "allow running evolutions from a custom path" in play.api.db.Databases.withInMemory() { database =>
      //#apply-evolutions-custom-path
      import play.api.db.evolutions._

      Evolutions.applyEvolutions(database, ClassLoaderEvolutionsReader.forPrefix("testdatabase/"))
      //#apply-evolutions-custom-path
      ok
    }

    "allow play to manage evolutions for you" in play.api.db.Databases.withInMemory() { database =>
      //#with-evolutions
      import play.api.db.evolutions._

      Evolutions.withEvolutions(database) {
        val connection = database.getConnection()

        // ...
      }
      //#with-evolutions
      ok
    }

    "allow simple composition of with database and with evolutions" in {
      //#with-evolutions-custom
      import play.api.db.{Database, Databases}
      import play.api.db.evolutions._

      def withMyDatabase[T](block: Database => T) = {

        Databases.withInMemory(
          urlOptions = Map(
            "MODE" -> "MYSQL"
          ),
          config = Map(
            "logStatements" -> true
          )
        ) { database =>

          Evolutions.withEvolutions(database, SimpleEvolutionsReader.forDefault(
            Evolution(
              1,
              "create table test (id bigint not null, name varchar(255));",
              "drop table test;"
            )
          )) {

            block(database)

          }
        }
      }
      //#with-evolutions-custom

      //#with-evolutions-custom-use
      withMyDatabase { database =>
        val connection = database.getConnection()
        connection.prepareStatement("insert into test values (10, 'testing')").execute()

        connection.prepareStatement("select * from test where id = 10")
          .executeQuery().next() must_== true
      }
      //#with-evolutions-custom-use
    }

  }


}
