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.prepareAndUseStatementAsync
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.mapRows
import com.ustadmobile.door.jdbc.ext.useResults
import com.ustadmobile.door.room.RoomDatabase
import com.ustadmobile.lib.db.entities.CacheLockJoin
import kotlin.Boolean
import kotlin.IllegalArgumentException
import kotlin.Int
import kotlin.Long
import kotlin.String
import kotlin.collections.List

public class CacheLockJoinDao_JdbcImpl(
  public val _db: RoomDatabase,
) : CacheLockJoinDao() {
  public val _insertAdapterCacheLockJoin_abort: EntityInsertionAdapter<CacheLockJoin> = object :
      EntityInsertionAdapter<CacheLockJoin>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT INTO CacheLockJoin (cljId, cljTableId, cljEntityUid, cljUrl, cljLockId, cljStatus, cljType, cljOiUid) VALUES(?, ?, ?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO CacheLockJoin (cljId, cljTableId, cljEntityUid, cljUrl, cljLockId, cljStatus, cljType, cljOiUid) VALUES(COALESCE(?,nextval('CacheLockJoin_cljId_seq')), ?, ?, ?, ?, ?, ?, ?)" + if(returnsId) { " RETURNING cljId" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: CacheLockJoin) {
      if(entity.cljId == 0) {
        stmt.setObject(1, null)
      } else {
        stmt.setInt(1, entity.cljId)
      }
      stmt.setInt(2, entity.cljTableId)
      stmt.setLong(3, entity.cljEntityUid)
      stmt.setString(4, entity.cljUrl)
      stmt.setLong(5, entity.cljLockId)
      stmt.setInt(6, entity.cljStatus)
      stmt.setInt(7, entity.cljType)
      stmt.setLong(8, entity.cljOiUid)
    }
  }

  override suspend fun insertListAsync(list: List<CacheLockJoin>) {
    _insertAdapterCacheLockJoin_abort.insertListAsync(list)
  }

  override suspend fun deleteListAsync(list: List<CacheLockJoin>) {
    var _numChanges = 0
    _db.prepareAndUseStatementAsync("DELETE FROM CacheLockJoin WHERE cljId = ?") {
       _stmt ->
      _stmt.getConnection().setAutoCommit(false)
      for(_entity in list) {
        _stmt.setInt(1, _entity.cljId)
        _numChanges += _stmt.executeUpdateAsyncKmp()
      }
      _stmt.getConnection().commit()
    }
  }

  override suspend fun findPendingLocks(): List<CacheLockJoin> =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        SELECT CacheLockJoin.*
    |          FROM CacheLockJoin
    |         WHERE CacheLockJoin.cljStatus = 1 
    |            OR CacheLockJoin.cljStatus = 3
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapRows {
        val _tmp_cljId = _result.getInt("cljId")
        val _tmp_cljTableId = _result.getInt("cljTableId")
        val _tmp_cljEntityUid = _result.getLong("cljEntityUid")
        val _tmp_cljUrl = _result.getString("cljUrl")
        val _tmp_cljLockId = _result.getLong("cljLockId")
        val _tmp_cljStatus = _result.getInt("cljStatus")
        val _tmp_cljType = _result.getInt("cljType")
        val _tmp_cljOiUid = _result.getLong("cljOiUid")
        CacheLockJoin().apply {
          this.cljId = _tmp_cljId
          this.cljTableId = _tmp_cljTableId
          this.cljEntityUid = _tmp_cljEntityUid
          this.cljUrl = _tmp_cljUrl
          this.cljLockId = _tmp_cljLockId
          this.cljStatus = _tmp_cljStatus
          this.cljType = _tmp_cljType
          this.cljOiUid = _tmp_cljOiUid
        }
      }
    }
  }

  override suspend fun updateLockIdAndStatus(
    uid: Int,
    lockId: Long,
    status: Int,
  ) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |        UPDATE CacheLockJoin
      |           SET cljLockId = ?,
      |               cljStatus = ?
      |         WHERE cljId = ?   
      |    
      """.trimMargin(),
      readOnly = false,)
    ) { _stmt -> 
      _stmt.setLong(1,lockId)
      _stmt.setInt(2,status)
      _stmt.setInt(3,uid)
      _stmt.executeUpdateAsyncKmp()
    }
  }

  override suspend fun findByTableIdAndEntityUid(tableId: Int, entityUid: Long): List<CacheLockJoin>
      = _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        SELECT CacheLockJoin.*
    |          FROM CacheLockJoin
    |         WHERE CacheLockJoin.cljTableId = ?
    |           AND CacheLockJoin.cljEntityUid = ?
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setInt(1,tableId)
    _stmt.setLong(2,entityUid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapRows {
        val _tmp_cljId = _result.getInt("cljId")
        val _tmp_cljTableId = _result.getInt("cljTableId")
        val _tmp_cljEntityUid = _result.getLong("cljEntityUid")
        val _tmp_cljUrl = _result.getString("cljUrl")
        val _tmp_cljLockId = _result.getLong("cljLockId")
        val _tmp_cljStatus = _result.getInt("cljStatus")
        val _tmp_cljType = _result.getInt("cljType")
        val _tmp_cljOiUid = _result.getLong("cljOiUid")
        CacheLockJoin().apply {
          this.cljId = _tmp_cljId
          this.cljTableId = _tmp_cljTableId
          this.cljEntityUid = _tmp_cljEntityUid
          this.cljUrl = _tmp_cljUrl
          this.cljLockId = _tmp_cljLockId
          this.cljStatus = _tmp_cljStatus
          this.cljType = _tmp_cljType
          this.cljOiUid = _tmp_cljOiUid
        }
      }
    }
  }
}
