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.createArrayOrProxyArrayOf
import com.ustadmobile.door.ext.prepareAndUseStatement
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.PeerReviewerAllocation
import kotlin.Boolean
import kotlin.IllegalArgumentException
import kotlin.Long
import kotlin.String
import kotlin.collections.List

public class PeerReviewerAllocationDao_JdbcImpl(
  public val _db: RoomDatabase,
) : PeerReviewerAllocationDao() {
  public val _insertAdapterPeerReviewerAllocation_upsert:
      EntityInsertionAdapter<PeerReviewerAllocation> = object :
      EntityInsertionAdapter<PeerReviewerAllocation>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT OR REPLACE INTO PeerReviewerAllocation (praUid, praMarkerSubmitterUid, praToMarkerSubmitterUid, praAssignmentUid, praActive, praLct) VALUES(?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO PeerReviewerAllocation (praUid, praMarkerSubmitterUid, praToMarkerSubmitterUid, praAssignmentUid, praActive, praLct) VALUES(COALESCE(?,nextval('PeerReviewerAllocation_praUid_seq')), ?, ?, ?, ?, ?) ON CONFLICT (praUid) DO UPDATE SET praMarkerSubmitterUid = excluded.praMarkerSubmitterUid,praToMarkerSubmitterUid = excluded.praToMarkerSubmitterUid,praAssignmentUid = excluded.praAssignmentUid,praActive = excluded.praActive,praLct = excluded.praLct" + if(returnsId) { " RETURNING praUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: PeerReviewerAllocation) {
      if(entity.praUid == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.praUid)
      }
      stmt.setLong(2, entity.praMarkerSubmitterUid)
      stmt.setLong(3, entity.praToMarkerSubmitterUid)
      stmt.setLong(4, entity.praAssignmentUid)
      stmt.setBoolean(5, entity.praActive)
      stmt.setLong(6, entity.praLct)
    }
  }

  public val _insertAdapterPeerReviewerAllocation_abort:
      EntityInsertionAdapter<PeerReviewerAllocation> = object :
      EntityInsertionAdapter<PeerReviewerAllocation>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT INTO PeerReviewerAllocation (praUid, praMarkerSubmitterUid, praToMarkerSubmitterUid, praAssignmentUid, praActive, praLct) VALUES(?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO PeerReviewerAllocation (praUid, praMarkerSubmitterUid, praToMarkerSubmitterUid, praAssignmentUid, praActive, praLct) VALUES(COALESCE(?,nextval('PeerReviewerAllocation_praUid_seq')), ?, ?, ?, ?, ?)" + if(returnsId) { " RETURNING praUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: PeerReviewerAllocation) {
      if(entity.praUid == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.praUid)
      }
      stmt.setLong(2, entity.praMarkerSubmitterUid)
      stmt.setLong(3, entity.praToMarkerSubmitterUid)
      stmt.setLong(4, entity.praAssignmentUid)
      stmt.setBoolean(5, entity.praActive)
      stmt.setLong(6, entity.praLct)
    }
  }

  override suspend fun replaceListAsync(entries: List<PeerReviewerAllocation>) {
    _insertAdapterPeerReviewerAllocation_upsert.insertListAsync(entries)
  }

  override suspend fun upsertList(entityList: List<PeerReviewerAllocation>) {
    _insertAdapterPeerReviewerAllocation_upsert.insertListAsync(entityList)
  }

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

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

  public override fun insertList(entityList: List<PeerReviewerAllocation>) {
    _insertAdapterPeerReviewerAllocation_abort.insertList(entityList)
  }

  public override fun update(entity: PeerReviewerAllocation) {
    val _sql =
        "UPDATE PeerReviewerAllocation SET praMarkerSubmitterUid = ?, praToMarkerSubmitterUid = ?, praAssignmentUid = ?, praActive = ?, praLct = ? WHERE praUid = ?"
    _db.prepareAndUseStatement(_sql) {
       _stmt ->
      _stmt.setLong(1, entity.praMarkerSubmitterUid)
      _stmt.setLong(2, entity.praToMarkerSubmitterUid)
      _stmt.setLong(3, entity.praAssignmentUid)
      _stmt.setBoolean(4, entity.praActive)
      _stmt.setLong(5, entity.praLct)
      _stmt.setLong(6, entity.praUid)
      _stmt.executeUpdate()
    }
  }

  override suspend fun getAllPeerReviewerAllocations(assignmentUid: List<Long>):
      List<PeerReviewerAllocation> = _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        SELECT *
    |         FROM PeerReviewerAllocation
    |        WHERE praAssignmentUid IN (?)
    |          AND praActive 
    |    
    """.trimMargin(),
    hasListParams = true,
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setArray(1, _stmt.getConnection().createArrayOrProxyArrayOf("BIGINT",
        assignmentUid.toTypedArray()))
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapRows {
        val _tmp_praUid = _result.getLong("praUid")
        val _tmp_praMarkerSubmitterUid = _result.getLong("praMarkerSubmitterUid")
        val _tmp_praToMarkerSubmitterUid = _result.getLong("praToMarkerSubmitterUid")
        val _tmp_praAssignmentUid = _result.getLong("praAssignmentUid")
        val _tmp_praActive = _result.getBoolean("praActive")
        val _tmp_praLct = _result.getLong("praLct")
        PeerReviewerAllocation().apply {
          this.praUid = _tmp_praUid
          this.praMarkerSubmitterUid = _tmp_praMarkerSubmitterUid
          this.praToMarkerSubmitterUid = _tmp_praToMarkerSubmitterUid
          this.praAssignmentUid = _tmp_praAssignmentUid
          this.praActive = _tmp_praActive
          this.praLct = _tmp_praLct
        }
      }
    }
  }

  override suspend fun getAllPeerReviewerAllocationsByClazzUid(clazzUid: Long,
      includeInactive: Boolean): List<PeerReviewerAllocation> =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        SELECT PeerReviewerAllocation.*
    |          FROM PeerReviewerAllocation
    |         WHERE PeerReviewerAllocation.praAssignmentUid IN
    |               (SELECT CourseBlock.cbEntityUid
    |                  FROM CourseBlock
    |                 WHERE CourseBlock.cbClazzUid = ?
    |                   AND CourseBlock.cbType = 103
    |                   AND (CAST(? AS INTEGER) = 1 OR CourseBlock.cbActive))
    |           AND (CAST(? AS INTEGER) = 1 OR PeerReviewerAllocation.praActive)
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,clazzUid)
    _stmt.setBoolean(2,includeInactive)
    _stmt.setBoolean(3,includeInactive)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapRows {
        val _tmp_praUid = _result.getLong("praUid")
        val _tmp_praMarkerSubmitterUid = _result.getLong("praMarkerSubmitterUid")
        val _tmp_praToMarkerSubmitterUid = _result.getLong("praToMarkerSubmitterUid")
        val _tmp_praAssignmentUid = _result.getLong("praAssignmentUid")
        val _tmp_praActive = _result.getBoolean("praActive")
        val _tmp_praLct = _result.getLong("praLct")
        PeerReviewerAllocation().apply {
          this.praUid = _tmp_praUid
          this.praMarkerSubmitterUid = _tmp_praMarkerSubmitterUid
          this.praToMarkerSubmitterUid = _tmp_praToMarkerSubmitterUid
          this.praAssignmentUid = _tmp_praAssignmentUid
          this.praActive = _tmp_praActive
          this.praLct = _tmp_praLct
        }
      }
    }
  }

  override suspend fun updateActiveByUid(
    cbUid: Long,
    active: Boolean,
    changeTime: Long,
  ) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |        UPDATE PeerReviewerAllocation 
      |           SET praActive = ?, 
      |               praLct = ?
      |         WHERE praUid = ?
      """.trimMargin(),
      readOnly = false,)
    ) { _stmt -> 
      _stmt.setBoolean(1,active)
      _stmt.setLong(2,changeTime)
      _stmt.setLong(3,cbUid)
      _stmt.executeUpdateAsyncKmp()
    }
  }
}
