package com.ustadmobile.core.db.dao

import com.ustadmobile.door.DoorDbType
import com.ustadmobile.door.EntityInsertionAdapter
import com.ustadmobile.door.PreparedStatementConfig
import com.ustadmobile.door.ext.prepareAndUseStatement
import com.ustadmobile.door.ext.prepareAndUseStatementAsync
import com.ustadmobile.door.flow.doorFlow
import com.ustadmobile.door.jdbc.PreparedStatement
import com.ustadmobile.door.jdbc.ext.executeQueryAsyncKmp
import com.ustadmobile.door.jdbc.ext.executeUpdateAsyncKmp
import com.ustadmobile.door.jdbc.ext.mapNextRow
import com.ustadmobile.door.jdbc.ext.useResults
import com.ustadmobile.door.room.RoomDatabase
import com.ustadmobile.lib.db.entities.Site
import kotlin.Boolean
import kotlin.IllegalArgumentException
import kotlin.Long
import kotlin.String
import kotlinx.coroutines.flow.Flow

public class SiteDao_JdbcImpl(
  public val _db: RoomDatabase,
) : SiteDao() {
  public val _insertAdapterSite_upsert: EntityInsertionAdapter<Site> = object :
      EntityInsertionAdapter<Site>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT OR REPLACE INTO Site (siteUid, sitePcsn, siteLcsn, siteLcb, siteLct, siteName, guestLogin, registrationAllowed, authSalt) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO Site (siteUid, sitePcsn, siteLcsn, siteLcb, siteLct, siteName, guestLogin, registrationAllowed, authSalt) VALUES(COALESCE(?,nextval('Site_siteUid_seq')), ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT (siteUid) DO UPDATE SET sitePcsn = excluded.sitePcsn,siteLcsn = excluded.siteLcsn,siteLcb = excluded.siteLcb,siteLct = excluded.siteLct,siteName = excluded.siteName,guestLogin = excluded.guestLogin,registrationAllowed = excluded.registrationAllowed,authSalt = excluded.authSalt" + if(returnsId) { " RETURNING siteUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: Site) {
      if(entity.siteUid == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.siteUid)
      }
      stmt.setLong(2, entity.sitePcsn)
      stmt.setLong(3, entity.siteLcsn)
      stmt.setInt(4, entity.siteLcb)
      stmt.setLong(5, entity.siteLct)
      stmt.setString(6, entity.siteName)
      stmt.setBoolean(7, entity.guestLogin)
      stmt.setBoolean(8, entity.registrationAllowed)
      stmt.setString(9, entity.authSalt)
    }
  }

  public val _insertAdapterSite_abort: EntityInsertionAdapter<Site> = object :
      EntityInsertionAdapter<Site>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT INTO Site (siteUid, sitePcsn, siteLcsn, siteLcb, siteLct, siteName, guestLogin, registrationAllowed, authSalt) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO Site (siteUid, sitePcsn, siteLcsn, siteLcb, siteLct, siteName, guestLogin, registrationAllowed, authSalt) VALUES(COALESCE(?,nextval('Site_siteUid_seq')), ?, ?, ?, ?, ?, ?, ?, ?)" + if(returnsId) { " RETURNING siteUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: Site) {
      if(entity.siteUid == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.siteUid)
      }
      stmt.setLong(2, entity.sitePcsn)
      stmt.setLong(3, entity.siteLcsn)
      stmt.setInt(4, entity.siteLcb)
      stmt.setLong(5, entity.siteLct)
      stmt.setString(6, entity.siteName)
      stmt.setBoolean(7, entity.guestLogin)
      stmt.setBoolean(8, entity.registrationAllowed)
      stmt.setString(9, entity.authSalt)
    }
  }

  override suspend fun replaceAsync(site: Site): Long {
    val _retVal = _insertAdapterSite_upsert.insertAndReturnIdAsync(site)
    return _retVal
  }

  override fun insert(site: Site): Long {
    val _retVal = _insertAdapterSite_abort.insertAndReturnId(site)
    return _retVal
  }

  override suspend fun insertAsync(site: Site): Long {
    val _retVal = _insertAdapterSite_abort.insertAndReturnIdAsync(site)
    return _retVal
  }

  override suspend fun updateAsync(workspace: Site) {
    val _sql =
        "UPDATE Site SET sitePcsn = ?, siteLcsn = ?, siteLcb = ?, siteLct = ?, siteName = ?, guestLogin = ?, registrationAllowed = ?, authSalt = ? WHERE siteUid = ?"
    _db.prepareAndUseStatementAsync(_sql) {
       _stmt ->
      _stmt.setLong(1, workspace.sitePcsn)
      _stmt.setLong(2, workspace.siteLcsn)
      _stmt.setInt(3, workspace.siteLcb)
      _stmt.setLong(4, workspace.siteLct)
      _stmt.setString(5, workspace.siteName)
      _stmt.setBoolean(6, workspace.guestLogin)
      _stmt.setBoolean(7, workspace.registrationAllowed)
      _stmt.setString(8, workspace.authSalt)
      _stmt.setLong(9, workspace.siteUid)
      _stmt.executeUpdateAsyncKmp()
    }
  }

  override fun getSite(): Site? = _db.prepareAndUseStatement(PreparedStatementConfig(
    sql = "SELECT * FROM Site LIMIT 1",
    readOnly = true,)
  ) { _stmt -> 
    _stmt.executeQuery().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_siteUid = _result.getLong("siteUid")
        val _tmp_sitePcsn = _result.getLong("sitePcsn")
        val _tmp_siteLcsn = _result.getLong("siteLcsn")
        val _tmp_siteLcb = _result.getInt("siteLcb")
        val _tmp_siteLct = _result.getLong("siteLct")
        val _tmp_siteName = _result.getString("siteName")
        val _tmp_guestLogin = _result.getBoolean("guestLogin")
        val _tmp_registrationAllowed = _result.getBoolean("registrationAllowed")
        val _tmp_authSalt = _result.getString("authSalt")
        Site().apply {
          this.siteUid = _tmp_siteUid
          this.sitePcsn = _tmp_sitePcsn
          this.siteLcsn = _tmp_siteLcsn
          this.siteLcb = _tmp_siteLcb
          this.siteLct = _tmp_siteLct
          this.siteName = _tmp_siteName
          this.guestLogin = _tmp_guestLogin
          this.registrationAllowed = _tmp_registrationAllowed
          this.authSalt = _tmp_authSalt
        }
      }
    }
  }

  override suspend fun getSiteAsync(): Site? =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = "SELECT * FROM Site LIMIT 1",
    readOnly = true,)
  ) { _stmt -> 
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_siteUid = _result.getLong("siteUid")
        val _tmp_sitePcsn = _result.getLong("sitePcsn")
        val _tmp_siteLcsn = _result.getLong("siteLcsn")
        val _tmp_siteLcb = _result.getInt("siteLcb")
        val _tmp_siteLct = _result.getLong("siteLct")
        val _tmp_siteName = _result.getString("siteName")
        val _tmp_guestLogin = _result.getBoolean("guestLogin")
        val _tmp_registrationAllowed = _result.getBoolean("registrationAllowed")
        val _tmp_authSalt = _result.getString("authSalt")
        Site().apply {
          this.siteUid = _tmp_siteUid
          this.sitePcsn = _tmp_sitePcsn
          this.siteLcsn = _tmp_siteLcsn
          this.siteLcb = _tmp_siteLcb
          this.siteLct = _tmp_siteLct
          this.siteName = _tmp_siteName
          this.guestLogin = _tmp_guestLogin
          this.registrationAllowed = _tmp_registrationAllowed
          this.authSalt = _tmp_authSalt
        }
      }
    }
  }

  override suspend fun getSiteAuthSaltAsync(): String? =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = "SELECT authSalt FROM Site LIMIT 1",
    readOnly = true,)
  ) { _stmt -> 
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapNextRow(null) {
        _result.getString(1)
      }
    }
  }

  override fun getSiteAsFlow(): Flow<Site?> = _db.doorFlow(arrayOf("Site")) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = "SELECT * FROM Site LIMIT 1",
      readOnly = true,)
    ) { _stmt -> 
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapNextRow(null) {
          val _tmp_siteUid = _result.getLong("siteUid")
          val _tmp_sitePcsn = _result.getLong("sitePcsn")
          val _tmp_siteLcsn = _result.getLong("siteLcsn")
          val _tmp_siteLcb = _result.getInt("siteLcb")
          val _tmp_siteLct = _result.getLong("siteLct")
          val _tmp_siteName = _result.getString("siteName")
          val _tmp_guestLogin = _result.getBoolean("guestLogin")
          val _tmp_registrationAllowed = _result.getBoolean("registrationAllowed")
          val _tmp_authSalt = _result.getString("authSalt")
          Site().apply {
            this.siteUid = _tmp_siteUid
            this.sitePcsn = _tmp_sitePcsn
            this.siteLcsn = _tmp_siteLcsn
            this.siteLcb = _tmp_siteLcb
            this.siteLct = _tmp_siteLct
            this.siteName = _tmp_siteName
            this.guestLogin = _tmp_guestLogin
            this.registrationAllowed = _tmp_registrationAllowed
            this.authSalt = _tmp_authSalt
          }
        }
      }
    }
  }
}
