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.PersonGroup
import kotlin.Boolean
import kotlin.IllegalArgumentException
import kotlin.Int
import kotlin.Long
import kotlin.String
import kotlin.collections.List
import kotlinx.coroutines.flow.Flow

public class PersonGroupDao_JdbcImpl(
  public val _db: RoomDatabase,
) : PersonGroupDao() {
  public val _insertAdapterPersonGroup_abort: EntityInsertionAdapter<PersonGroup> = object :
      EntityInsertionAdapter<PersonGroup>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT INTO PersonGroup (groupUid, groupMasterCsn, groupLocalCsn, groupLastChangedBy, groupLct, groupName, groupActive, personGroupFlag) VALUES(?, ?, ?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO PersonGroup (groupUid, groupMasterCsn, groupLocalCsn, groupLastChangedBy, groupLct, groupName, groupActive, personGroupFlag) VALUES(COALESCE(?,nextval('PersonGroup_groupUid_seq')), ?, ?, ?, ?, ?, ?, ?)" + if(returnsId) { " RETURNING groupUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: PersonGroup) {
      if(entity.groupUid == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.groupUid)
      }
      stmt.setLong(2, entity.groupMasterCsn)
      stmt.setLong(3, entity.groupLocalCsn)
      stmt.setInt(4, entity.groupLastChangedBy)
      stmt.setLong(5, entity.groupLct)
      stmt.setString(6, entity.groupName)
      stmt.setBoolean(7, entity.groupActive)
      stmt.setInt(8, entity.personGroupFlag)
    }
  }

  public override fun insert(entity: PersonGroup): Long {
    val _retVal = _insertAdapterPersonGroup_abort.insertAndReturnId(entity)
    return _retVal
  }

  public override suspend fun insertAsync(entity: PersonGroup): Long {
    val _retVal = _insertAdapterPersonGroup_abort.insertAndReturnIdAsync(entity)
    return _retVal
  }

  public override fun insertList(entityList: List<PersonGroup>) {
    _insertAdapterPersonGroup_abort.insertList(entityList)
  }

  override suspend fun updateAsync(entity: PersonGroup): Int {
    var _result = 0
    val _sql =
        "UPDATE PersonGroup SET groupMasterCsn = ?, groupLocalCsn = ?, groupLastChangedBy = ?, groupLct = ?, groupName = ?, groupActive = ?, personGroupFlag = ? WHERE groupUid = ?"
    _db.prepareAndUseStatementAsync(_sql) {
       _stmt ->
      _stmt.setLong(1, entity.groupMasterCsn)
      _stmt.setLong(2, entity.groupLocalCsn)
      _stmt.setInt(3, entity.groupLastChangedBy)
      _stmt.setLong(4, entity.groupLct)
      _stmt.setString(5, entity.groupName)
      _stmt.setBoolean(6, entity.groupActive)
      _stmt.setInt(7, entity.personGroupFlag)
      _stmt.setLong(8, entity.groupUid)
      _result += _stmt.executeUpdateAsyncKmp()
    }
    return _result
  }

  public override fun update(entity: PersonGroup) {
    val _sql =
        "UPDATE PersonGroup SET groupMasterCsn = ?, groupLocalCsn = ?, groupLastChangedBy = ?, groupLct = ?, groupName = ?, groupActive = ?, personGroupFlag = ? WHERE groupUid = ?"
    _db.prepareAndUseStatement(_sql) {
       _stmt ->
      _stmt.setLong(1, entity.groupMasterCsn)
      _stmt.setLong(2, entity.groupLocalCsn)
      _stmt.setInt(3, entity.groupLastChangedBy)
      _stmt.setLong(4, entity.groupLct)
      _stmt.setString(5, entity.groupName)
      _stmt.setBoolean(6, entity.groupActive)
      _stmt.setInt(7, entity.personGroupFlag)
      _stmt.setLong(8, entity.groupUid)
      _stmt.executeUpdate()
    }
  }

  override fun findByUid(uid: Long): PersonGroup? =
      _db.prepareAndUseStatement(PreparedStatementConfig(
    sql = "SELECT * FROM PersonGroup WHERE groupUid = ?",
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,uid)
    _stmt.executeQuery().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_groupUid = _result.getLong("groupUid")
        val _tmp_groupMasterCsn = _result.getLong("groupMasterCsn")
        val _tmp_groupLocalCsn = _result.getLong("groupLocalCsn")
        val _tmp_groupLastChangedBy = _result.getInt("groupLastChangedBy")
        val _tmp_groupLct = _result.getLong("groupLct")
        val _tmp_groupName = _result.getString("groupName")
        val _tmp_groupActive = _result.getBoolean("groupActive")
        val _tmp_personGroupFlag = _result.getInt("personGroupFlag")
        PersonGroup().apply {
          this.groupUid = _tmp_groupUid
          this.groupMasterCsn = _tmp_groupMasterCsn
          this.groupLocalCsn = _tmp_groupLocalCsn
          this.groupLastChangedBy = _tmp_groupLastChangedBy
          this.groupLct = _tmp_groupLct
          this.groupName = _tmp_groupName
          this.groupActive = _tmp_groupActive
          this.personGroupFlag = _tmp_personGroupFlag
        }
      }
    }
  }

  override suspend fun findByUidAsync(uid: Long): PersonGroup? =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = "SELECT * FROM PersonGroup WHERE groupUid = ?",
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,uid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_groupUid = _result.getLong("groupUid")
        val _tmp_groupMasterCsn = _result.getLong("groupMasterCsn")
        val _tmp_groupLocalCsn = _result.getLong("groupLocalCsn")
        val _tmp_groupLastChangedBy = _result.getInt("groupLastChangedBy")
        val _tmp_groupLct = _result.getLong("groupLct")
        val _tmp_groupName = _result.getString("groupName")
        val _tmp_groupActive = _result.getBoolean("groupActive")
        val _tmp_personGroupFlag = _result.getInt("personGroupFlag")
        PersonGroup().apply {
          this.groupUid = _tmp_groupUid
          this.groupMasterCsn = _tmp_groupMasterCsn
          this.groupLocalCsn = _tmp_groupLocalCsn
          this.groupLastChangedBy = _tmp_groupLastChangedBy
          this.groupLct = _tmp_groupLct
          this.groupName = _tmp_groupName
          this.groupActive = _tmp_groupActive
          this.personGroupFlag = _tmp_personGroupFlag
        }
      }
    }
  }

  override fun findByUidLive(uid: Long): Flow<PersonGroup?> = _db.doorFlow(arrayOf("PersonGroup")) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = "SELECT * FROM PersonGroup WHERE groupUid = ?",
      readOnly = true,)
    ) { _stmt -> 
      _stmt.setLong(1,uid)
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapNextRow(null) {
          val _tmp_groupUid = _result.getLong("groupUid")
          val _tmp_groupMasterCsn = _result.getLong("groupMasterCsn")
          val _tmp_groupLocalCsn = _result.getLong("groupLocalCsn")
          val _tmp_groupLastChangedBy = _result.getInt("groupLastChangedBy")
          val _tmp_groupLct = _result.getLong("groupLct")
          val _tmp_groupName = _result.getString("groupName")
          val _tmp_groupActive = _result.getBoolean("groupActive")
          val _tmp_personGroupFlag = _result.getInt("personGroupFlag")
          PersonGroup().apply {
            this.groupUid = _tmp_groupUid
            this.groupMasterCsn = _tmp_groupMasterCsn
            this.groupLocalCsn = _tmp_groupLocalCsn
            this.groupLastChangedBy = _tmp_groupLastChangedBy
            this.groupLct = _tmp_groupLct
            this.groupName = _tmp_groupName
            this.groupActive = _tmp_groupActive
            this.personGroupFlag = _tmp_personGroupFlag
          }
        }
      }
    }
  }

  override suspend fun findNameByGroupUid(groupUid: Long): String? =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        Select CASE
    |               WHEN Person.firstNames IS NOT NULL THEN Person.firstNames
    |               ELSE PersonGroup.groupName 
    |               END AS name
    |          FROM PersonGroup
    |               LEFT JOIN Person
    |                         ON Person.personGroupUid = PersonGroup.groupUid
    |         WHERE PersonGroup.groupUid = ?
    |         LIMIT 1
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,groupUid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapNextRow(null) {
        _result.getString(1)
      }
    }
  }
}
