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.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.mapRows
import com.ustadmobile.door.jdbc.ext.useResults
import com.ustadmobile.door.room.RoomDatabase
import com.ustadmobile.lib.db.entities.TransferJobItem
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 TransferJobItemDao_JdbcImpl(
  public val _db: RoomDatabase,
) : TransferJobItemDao() {
  public val _insertAdapterTransferJobItem_abort: EntityInsertionAdapter<TransferJobItem> = object :
      EntityInsertionAdapter<TransferJobItem>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT INTO TransferJobItem (tjiUid, tjiTjUid, tjTotalSize, tjTransferred, tjAttemptCount, tjiSrc, tjiDest, tjiType, tjiStatus, tjiTableId, tjiEntityUid, tjiEntityEtag, tjiLockIdToRelease, tjiPartialTmpFile) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO TransferJobItem (tjiUid, tjiTjUid, tjTotalSize, tjTransferred, tjAttemptCount, tjiSrc, tjiDest, tjiType, tjiStatus, tjiTableId, tjiEntityUid, tjiEntityEtag, tjiLockIdToRelease, tjiPartialTmpFile) VALUES(COALESCE(?,nextval('TransferJobItem_tjiUid_seq')), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + if(returnsId) { " RETURNING tjiUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: TransferJobItem) {
      if(entity.tjiUid == 0) {
        stmt.setObject(1, null)
      } else {
        stmt.setInt(1, entity.tjiUid)
      }
      stmt.setInt(2, entity.tjiTjUid)
      stmt.setLong(3, entity.tjTotalSize)
      stmt.setLong(4, entity.tjTransferred)
      stmt.setInt(5, entity.tjAttemptCount)
      stmt.setString(6, entity.tjiSrc)
      stmt.setString(7, entity.tjiDest)
      stmt.setInt(8, entity.tjiType)
      stmt.setInt(9, entity.tjiStatus)
      stmt.setInt(10, entity.tjiTableId)
      stmt.setLong(11, entity.tjiEntityUid)
      stmt.setLong(12, entity.tjiEntityEtag)
      stmt.setLong(13, entity.tjiLockIdToRelease)
      stmt.setString(14, entity.tjiPartialTmpFile)
    }
  }

  override suspend fun insertList(items: List<TransferJobItem>) {
    _insertAdapterTransferJobItem_abort.insertListAsync(items)
  }

  override suspend fun insert(item: TransferJobItem): Long {
    val _retVal = _insertAdapterTransferJobItem_abort.insertAndReturnIdAsync(item)
    return _retVal
  }

  override suspend fun findByJobUid(jobUid: Int): List<TransferJobItem> =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        SELECT TransferJobItem.*
    |          FROM TransferJobItem
    |         WHERE TransferJobItem.tjiTjUid = ?
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setInt(1,jobUid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapRows {
        val _tmp_tjiUid = _result.getInt("tjiUid")
        val _tmp_tjiTjUid = _result.getInt("tjiTjUid")
        val _tmp_tjTotalSize = _result.getLong("tjTotalSize")
        val _tmp_tjTransferred = _result.getLong("tjTransferred")
        val _tmp_tjAttemptCount = _result.getInt("tjAttemptCount")
        val _tmp_tjiSrc = _result.getString("tjiSrc")
        val _tmp_tjiDest = _result.getString("tjiDest")
        val _tmp_tjiType = _result.getInt("tjiType")
        val _tmp_tjiStatus = _result.getInt("tjiStatus")
        val _tmp_tjiTableId = _result.getInt("tjiTableId")
        val _tmp_tjiEntityUid = _result.getLong("tjiEntityUid")
        val _tmp_tjiEntityEtag = _result.getLong("tjiEntityEtag")
        val _tmp_tjiLockIdToRelease = _result.getLong("tjiLockIdToRelease")
        val _tmp_tjiPartialTmpFile = _result.getString("tjiPartialTmpFile")
        TransferJobItem().apply {
          this.tjiUid = _tmp_tjiUid
          this.tjiTjUid = _tmp_tjiTjUid
          this.tjTotalSize = _tmp_tjTotalSize
          this.tjTransferred = _tmp_tjTransferred
          this.tjAttemptCount = _tmp_tjAttemptCount
          this.tjiSrc = _tmp_tjiSrc
          this.tjiDest = _tmp_tjiDest
          this.tjiType = _tmp_tjiType
          this.tjiStatus = _tmp_tjiStatus
          this.tjiTableId = _tmp_tjiTableId
          this.tjiEntityUid = _tmp_tjiEntityUid
          this.tjiEntityEtag = _tmp_tjiEntityEtag
          this.tjiLockIdToRelease = _tmp_tjiLockIdToRelease
          this.tjiPartialTmpFile = _tmp_tjiPartialTmpFile
        }
      }
    }
  }

  override suspend fun findPendingByJobUid(jobUid: Int): List<TransferJobItem> =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        SELECT TransferJobItem.*
    |          FROM TransferJobItem
    |         WHERE TransferJobItem.tjiTjUid = ?
    |           AND TransferJobItem.tjiStatus < 21
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setInt(1,jobUid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapRows {
        val _tmp_tjiUid = _result.getInt("tjiUid")
        val _tmp_tjiTjUid = _result.getInt("tjiTjUid")
        val _tmp_tjTotalSize = _result.getLong("tjTotalSize")
        val _tmp_tjTransferred = _result.getLong("tjTransferred")
        val _tmp_tjAttemptCount = _result.getInt("tjAttemptCount")
        val _tmp_tjiSrc = _result.getString("tjiSrc")
        val _tmp_tjiDest = _result.getString("tjiDest")
        val _tmp_tjiType = _result.getInt("tjiType")
        val _tmp_tjiStatus = _result.getInt("tjiStatus")
        val _tmp_tjiTableId = _result.getInt("tjiTableId")
        val _tmp_tjiEntityUid = _result.getLong("tjiEntityUid")
        val _tmp_tjiEntityEtag = _result.getLong("tjiEntityEtag")
        val _tmp_tjiLockIdToRelease = _result.getLong("tjiLockIdToRelease")
        val _tmp_tjiPartialTmpFile = _result.getString("tjiPartialTmpFile")
        TransferJobItem().apply {
          this.tjiUid = _tmp_tjiUid
          this.tjiTjUid = _tmp_tjiTjUid
          this.tjTotalSize = _tmp_tjTotalSize
          this.tjTransferred = _tmp_tjTransferred
          this.tjAttemptCount = _tmp_tjAttemptCount
          this.tjiSrc = _tmp_tjiSrc
          this.tjiDest = _tmp_tjiDest
          this.tjiType = _tmp_tjiType
          this.tjiStatus = _tmp_tjiStatus
          this.tjiTableId = _tmp_tjiTableId
          this.tjiEntityUid = _tmp_tjiEntityUid
          this.tjiEntityEtag = _tmp_tjiEntityEtag
          this.tjiLockIdToRelease = _tmp_tjiLockIdToRelease
          this.tjiPartialTmpFile = _tmp_tjiPartialTmpFile
        }
      }
    }
  }

  override suspend fun updateTransferredProgress(jobItemUid: Int, transferred: Long) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |        UPDATE TransferJobItem
      |           SET tjTransferred = ?
      |         WHERE tjiUid = ?
      |    
      """.trimMargin(),
      readOnly = false,)
    ) { _stmt -> 
      _stmt.setLong(1,transferred)
      _stmt.setInt(2,jobItemUid)
      _stmt.executeUpdateAsyncKmp()
    }
  }

  override suspend fun updateStatus(jobItemUid: Int, status: Int) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |        UPDATE TransferJobItem
      |           SET tjiStatus = ?
      |         WHERE tjiUid = ?  
      |    
      """.trimMargin(),
      readOnly = false,)
    ) { _stmt -> 
      _stmt.setInt(1,status)
      _stmt.setInt(2,jobItemUid)
      _stmt.executeUpdateAsyncKmp()
    }
  }

  override suspend fun insertOutgoingReplicationForTransferJobItemIfDone(destNodeId: Long,
      transferJobItemUid: Int) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |        INSERT INTO OutgoingReplication(destNodeId, orTableId, orPk1, orPk2)
      |        SELECT ? AS destNodeId, 
      |              TransferJobItem.tjiTableId AS orTableId,
      |              TransferJobItem.tjiEntityUid AS orPk1,
      |              0 AS orPk2
      |        FROM TransferJobItem
      |       WHERE TransferJobItem.tjiUid = ?
      |         AND TransferJobItem.tjiTableId != 0
      |         AND TransferJobItem.tjiStatus = 21
      |         AND NOT EXISTS(
      |             SELECT OtherJob.tjiUid
      |               FROM TransferJobItem OtherJob
      |              WHERE OtherJob.tjiTableId = TransferJobItem.tjiTableId
      |                AND OtherJob.tjiEntityUid = TransferJobItem.tjiEntityUid
      |                AND OtherJob.tjiEntityEtag = TransferJobItem.tjiEntityEtag
      |                AND OtherJob.tjiStatus != 21)
      |    
      """.trimMargin(),
      readOnly = false,)
    ) { _stmt -> 
      _stmt.setLong(1,destNodeId)
      _stmt.setInt(2,transferJobItemUid)
      _stmt.executeUpdateAsyncKmp()
    }
  }

  override suspend fun updateStatusIfNotCompleteForAllInJob(jobUid: Int, status: Int) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |        UPDATE TransferJobItem
      |           SET tjiStatus = ?
      |         WHERE tjiTjUid = ?
      |           AND tjiStatus != 21 
      |    
      """.trimMargin(),
      readOnly = false,)
    ) { _stmt -> 
      _stmt.setInt(1,status)
      _stmt.setInt(2,jobUid)
      _stmt.executeUpdateAsyncKmp()
    }
  }

  override suspend fun findNumberJobItemsNotComplete(jobUid: Int): Int =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |       SELECT COUNT(*)
    |         FROM TransferJobItem
    |        WHERE TransferJobItem.tjiTjUid = ?
    |          AND TransferjobItem.tjiStatus != 21
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setInt(1,jobUid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapNextRow(0) {
        _result.getInt(1)
      }
    }
  }

  override fun findByEntityUidList(entityUidList: List<Long>, tableId: Int):
      Flow<List<TransferJobItem>> = _db.doorFlow(arrayOf("TransferJobItem")) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |        SELECT TransferJobItem.*
      |          FROM TransferJobItem
      |         WHERE TransferJobItem.tjiTableId = ?
      |           AND TransferJobItem.tjiEntityUid IN (?)
      |    
      """.trimMargin(),
      hasListParams = true,
      readOnly = true,)
    ) { _stmt -> 
      _stmt.setInt(1,tableId)
      _stmt.setArray(2, _stmt.getConnection().createArrayOrProxyArrayOf("BIGINT",
          entityUidList.toTypedArray()))
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapRows {
          val _tmp_tjiUid = _result.getInt("tjiUid")
          val _tmp_tjiTjUid = _result.getInt("tjiTjUid")
          val _tmp_tjTotalSize = _result.getLong("tjTotalSize")
          val _tmp_tjTransferred = _result.getLong("tjTransferred")
          val _tmp_tjAttemptCount = _result.getInt("tjAttemptCount")
          val _tmp_tjiSrc = _result.getString("tjiSrc")
          val _tmp_tjiDest = _result.getString("tjiDest")
          val _tmp_tjiType = _result.getInt("tjiType")
          val _tmp_tjiStatus = _result.getInt("tjiStatus")
          val _tmp_tjiTableId = _result.getInt("tjiTableId")
          val _tmp_tjiEntityUid = _result.getLong("tjiEntityUid")
          val _tmp_tjiEntityEtag = _result.getLong("tjiEntityEtag")
          val _tmp_tjiLockIdToRelease = _result.getLong("tjiLockIdToRelease")
          val _tmp_tjiPartialTmpFile = _result.getString("tjiPartialTmpFile")
          TransferJobItem().apply {
            this.tjiUid = _tmp_tjiUid
            this.tjiTjUid = _tmp_tjiTjUid
            this.tjTotalSize = _tmp_tjTotalSize
            this.tjTransferred = _tmp_tjTransferred
            this.tjAttemptCount = _tmp_tjAttemptCount
            this.tjiSrc = _tmp_tjiSrc
            this.tjiDest = _tmp_tjiDest
            this.tjiType = _tmp_tjiType
            this.tjiStatus = _tmp_tjiStatus
            this.tjiTableId = _tmp_tjiTableId
            this.tjiEntityUid = _tmp_tjiEntityUid
            this.tjiEntityEtag = _tmp_tjiEntityEtag
            this.tjiLockIdToRelease = _tmp_tjiLockIdToRelease
            this.tjiPartialTmpFile = _tmp_tjiPartialTmpFile
          }
        }
      }
    }
  }
}
