package com.ustadmobile.libcache.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.jdbc.PreparedStatement
import com.ustadmobile.door.jdbc.ext.getStringNonNull
import com.ustadmobile.door.jdbc.ext.mapRows
import com.ustadmobile.door.jdbc.ext.useResults
import com.ustadmobile.door.room.RoomDatabase
import com.ustadmobile.libcache.db.entities.RetentionLock
import kotlin.Boolean
import kotlin.IllegalArgumentException
import kotlin.Int
import kotlin.Long
import kotlin.String
import kotlin.collections.List

public class RetentionLockDao_JdbcImpl(
  public val _db: RoomDatabase,
) : RetentionLockDao() {
  public val _insertAdapterRetentionLock_abort: EntityInsertionAdapter<RetentionLock> = object :
      EntityInsertionAdapter<RetentionLock>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT INTO RetentionLock (lockId, lockKey, lockRemark) VALUES(?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO RetentionLock (lockId, lockKey, lockRemark) VALUES(COALESCE(?,nextval('RetentionLock_lockId_seq')), ?, ?)" + if(returnsId) { " RETURNING lockId" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: RetentionLock) {
      if(entity.lockId == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.lockId)
      }
      stmt.setString(2, entity.lockKey)
      stmt.setString(3, entity.lockRemark)
    }
  }

  public val _insertAdapterRetentionLock_upsert: EntityInsertionAdapter<RetentionLock> = object :
      EntityInsertionAdapter<RetentionLock>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT OR REPLACE INTO RetentionLock (lockId, lockKey, lockRemark) VALUES(?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO RetentionLock (lockId, lockKey, lockRemark) VALUES(COALESCE(?,nextval('RetentionLock_lockId_seq')), ?, ?) ON CONFLICT (lockId) DO UPDATE SET lockKey = excluded.lockKey,lockRemark = excluded.lockRemark" + if(returnsId) { " RETURNING lockId" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: RetentionLock) {
      if(entity.lockId == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.lockId)
      }
      stmt.setString(2, entity.lockKey)
      stmt.setString(3, entity.lockRemark)
    }
  }

  override fun insert(retentionLock: RetentionLock): Long {
    val _retVal = _insertAdapterRetentionLock_abort.insertAndReturnId(retentionLock)
    return _retVal
  }

  override fun upsertList(retentionLocks: List<RetentionLock>) {
    _insertAdapterRetentionLock_upsert.insertList(retentionLocks)
  }

  override fun delete(retentionLocks: List<RetentionLock>) {
    var _numChanges = 0
    _db.prepareAndUseStatement("DELETE FROM RetentionLock WHERE lockId = ?") {
       _stmt ->
      _stmt.getConnection().setAutoCommit(false)
      for(_entity in retentionLocks) {
        _stmt.setLong(1, _entity.lockId)
        _numChanges += _stmt.executeUpdate()
      }
      _stmt.getConnection().commit()
    }
  }

  override fun findByBatchId(batchId: Int): List<RetentionLock> =
      _db.prepareAndUseStatement(PreparedStatementConfig(
    sql = """
    |
    |        SELECT RetentionLock.*
    |          FROM RetentionLock
    |         WHERE RetentionLock.lockKey IN 
    |               (SELECT RequestedEntry.requestedKey
    |                  FROM RequestedEntry
    |                 WHERE RequestedEntry.batchId = ?)
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setInt(1,batchId)
    _stmt.executeQuery().useResults{ _result -> 
      _result.mapRows {
        val _tmp_lockId = _result.getLong("lockId")
        val _tmp_lockKey = _result.getStringNonNull("lockKey")
        val _tmp_lockRemark = _result.getStringNonNull("lockRemark")
        RetentionLock().apply {
          this.lockId = _tmp_lockId
          this.lockKey = _tmp_lockKey
          this.lockRemark = _tmp_lockRemark
        }
      }
    }
  }

  override fun findByKey(urlKey: String): List<RetentionLock> =
      _db.prepareAndUseStatement(PreparedStatementConfig(
    sql = """
    |
    |        SELECT RetentionLock.*
    |          FROM RetentionLock
    |         WHERE RetentionLock.lockKey = ? 
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setString(1,urlKey)
    _stmt.executeQuery().useResults{ _result -> 
      _result.mapRows {
        val _tmp_lockId = _result.getLong("lockId")
        val _tmp_lockKey = _result.getStringNonNull("lockKey")
        val _tmp_lockRemark = _result.getStringNonNull("lockRemark")
        RetentionLock().apply {
          this.lockId = _tmp_lockId
          this.lockKey = _tmp_lockKey
          this.lockRemark = _tmp_lockRemark
        }
      }
    }
  }
}
