package com.ustadmobile.core.db.dao

import androidx.paging.PagingSource
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.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.paging.DoorLimitOffsetPagingSource
import com.ustadmobile.door.room.RoomDatabase
import com.ustadmobile.lib.db.entities.LeavingReason
import com.ustadmobile.lib.db.entities.UidAndLabel
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 LeavingReasonDao_JdbcImpl(
  public val _db: RoomDatabase,
) : LeavingReasonDao() {
  public val _insertAdapterLeavingReason_upsert: EntityInsertionAdapter<LeavingReason> = object :
      EntityInsertionAdapter<LeavingReason>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT OR REPLACE INTO LeavingReason (leavingReasonUid, leavingReasonTitle, leavingReasonMCSN, leavingReasonCSN, leavingReasonLCB, leavingReasonLct) VALUES(?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO LeavingReason (leavingReasonUid, leavingReasonTitle, leavingReasonMCSN, leavingReasonCSN, leavingReasonLCB, leavingReasonLct) VALUES(COALESCE(?,nextval('LeavingReason_leavingReasonUid_seq')), ?, ?, ?, ?, ?) ON CONFLICT (leavingReasonUid) DO UPDATE SET leavingReasonTitle = excluded.leavingReasonTitle,leavingReasonMCSN = excluded.leavingReasonMCSN,leavingReasonCSN = excluded.leavingReasonCSN,leavingReasonLCB = excluded.leavingReasonLCB,leavingReasonLct = excluded.leavingReasonLct" + if(returnsId) { " RETURNING leavingReasonUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: LeavingReason) {
      if(entity.leavingReasonUid == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.leavingReasonUid)
      }
      stmt.setString(2, entity.leavingReasonTitle)
      stmt.setLong(3, entity.leavingReasonMCSN)
      stmt.setLong(4, entity.leavingReasonCSN)
      stmt.setInt(5, entity.leavingReasonLCB)
      stmt.setLong(6, entity.leavingReasonLct)
    }
  }

  public val _insertAdapterLeavingReason_abort: EntityInsertionAdapter<LeavingReason> = object :
      EntityInsertionAdapter<LeavingReason>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT INTO LeavingReason (leavingReasonUid, leavingReasonTitle, leavingReasonMCSN, leavingReasonCSN, leavingReasonLCB, leavingReasonLct) VALUES(?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO LeavingReason (leavingReasonUid, leavingReasonTitle, leavingReasonMCSN, leavingReasonCSN, leavingReasonLCB, leavingReasonLct) VALUES(COALESCE(?,nextval('LeavingReason_leavingReasonUid_seq')), ?, ?, ?, ?, ?)" + if(returnsId) { " RETURNING leavingReasonUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: LeavingReason) {
      if(entity.leavingReasonUid == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.leavingReasonUid)
      }
      stmt.setString(2, entity.leavingReasonTitle)
      stmt.setLong(3, entity.leavingReasonMCSN)
      stmt.setLong(4, entity.leavingReasonCSN)
      stmt.setInt(5, entity.leavingReasonLCB)
      stmt.setLong(6, entity.leavingReasonLct)
    }
  }

  override suspend fun replaceList(entityList: List<LeavingReason>) {
    _insertAdapterLeavingReason_upsert.insertListAsync(entityList)
  }

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

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

  public override fun insertList(entityList: List<LeavingReason>) {
    _insertAdapterLeavingReason_abort.insertList(entityList)
  }

  override suspend fun updateAsync(entity: LeavingReason): Int {
    var _result = 0
    val _sql =
        "UPDATE LeavingReason SET leavingReasonTitle = ?, leavingReasonMCSN = ?, leavingReasonCSN = ?, leavingReasonLCB = ?, leavingReasonLct = ? WHERE leavingReasonUid = ?"
    _db.prepareAndUseStatementAsync(_sql) {
       _stmt ->
      _stmt.setString(1, entity.leavingReasonTitle)
      _stmt.setLong(2, entity.leavingReasonMCSN)
      _stmt.setLong(3, entity.leavingReasonCSN)
      _stmt.setInt(4, entity.leavingReasonLCB)
      _stmt.setLong(5, entity.leavingReasonLct)
      _stmt.setLong(6, entity.leavingReasonUid)
      _result += _stmt.executeUpdateAsyncKmp()
    }
    return _result
  }

  public override fun update(entity: LeavingReason) {
    val _sql =
        "UPDATE LeavingReason SET leavingReasonTitle = ?, leavingReasonMCSN = ?, leavingReasonCSN = ?, leavingReasonLCB = ?, leavingReasonLct = ? WHERE leavingReasonUid = ?"
    _db.prepareAndUseStatement(_sql) {
       _stmt ->
      _stmt.setString(1, entity.leavingReasonTitle)
      _stmt.setLong(2, entity.leavingReasonMCSN)
      _stmt.setLong(3, entity.leavingReasonCSN)
      _stmt.setInt(4, entity.leavingReasonLCB)
      _stmt.setLong(5, entity.leavingReasonLct)
      _stmt.setLong(6, entity.leavingReasonUid)
      _stmt.executeUpdate()
    }
  }

  override fun findAllReasons(): PagingSource<Int, LeavingReason> = object :
      DoorLimitOffsetPagingSource<LeavingReason>(db = _db
  , tableNames = arrayOf("LeavingReason")
  ) {
    override suspend fun loadRows(_limit: Int, _offset: Int): List<LeavingReason> =
        _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = "SELECT * FROM (SELECT * FROM LeavingReason) AS _PagingData LIMIT ? OFFSET ?",
      readOnly = true,)
    ) { _stmt -> 
      _stmt.setInt(1,_limit)
      _stmt.setInt(2,_offset)
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapRows {
          val _tmp_leavingReasonUid = _result.getLong("leavingReasonUid")
          val _tmp_leavingReasonTitle = _result.getString("leavingReasonTitle")
          val _tmp_leavingReasonMCSN = _result.getLong("leavingReasonMCSN")
          val _tmp_leavingReasonCSN = _result.getLong("leavingReasonCSN")
          val _tmp_leavingReasonLCB = _result.getInt("leavingReasonLCB")
          val _tmp_leavingReasonLct = _result.getLong("leavingReasonLct")
          LeavingReason().apply {
            this.leavingReasonUid = _tmp_leavingReasonUid
            this.leavingReasonTitle = _tmp_leavingReasonTitle
            this.leavingReasonMCSN = _tmp_leavingReasonMCSN
            this.leavingReasonCSN = _tmp_leavingReasonCSN
            this.leavingReasonLCB = _tmp_leavingReasonLCB
            this.leavingReasonLct = _tmp_leavingReasonLct
          }
        }
      }
    }

    override suspend fun countRows(): Int = _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = "SELECT COUNT(*) FROM (SELECT * FROM LeavingReason) AS _PagingCount",
      readOnly = true,)
    ) { _stmt -> 
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapNextRow(0) {
          _result.getInt(1)
        }
      }
    }
  }

  override fun findAllReasonsLive(): List<LeavingReason> =
      _db.prepareAndUseStatement(PreparedStatementConfig(
    sql = "SELECT * FROM LeavingReason",
    readOnly = true,)
  ) { _stmt -> 
    _stmt.executeQuery().useResults{ _result -> 
      _result.mapRows {
        val _tmp_leavingReasonUid = _result.getLong("leavingReasonUid")
        val _tmp_leavingReasonTitle = _result.getString("leavingReasonTitle")
        val _tmp_leavingReasonMCSN = _result.getLong("leavingReasonMCSN")
        val _tmp_leavingReasonCSN = _result.getLong("leavingReasonCSN")
        val _tmp_leavingReasonLCB = _result.getInt("leavingReasonLCB")
        val _tmp_leavingReasonLct = _result.getLong("leavingReasonLct")
        LeavingReason().apply {
          this.leavingReasonUid = _tmp_leavingReasonUid
          this.leavingReasonTitle = _tmp_leavingReasonTitle
          this.leavingReasonMCSN = _tmp_leavingReasonMCSN
          this.leavingReasonCSN = _tmp_leavingReasonCSN
          this.leavingReasonLCB = _tmp_leavingReasonLCB
          this.leavingReasonLct = _tmp_leavingReasonLct
        }
      }
    }
  }

  override suspend fun findAllReasonsAsync(): List<LeavingReason> =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = "SELECT * FROM LeavingReason",
    readOnly = true,)
  ) { _stmt -> 
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapRows {
        val _tmp_leavingReasonUid = _result.getLong("leavingReasonUid")
        val _tmp_leavingReasonTitle = _result.getString("leavingReasonTitle")
        val _tmp_leavingReasonMCSN = _result.getLong("leavingReasonMCSN")
        val _tmp_leavingReasonCSN = _result.getLong("leavingReasonCSN")
        val _tmp_leavingReasonLCB = _result.getInt("leavingReasonLCB")
        val _tmp_leavingReasonLct = _result.getLong("leavingReasonLct")
        LeavingReason().apply {
          this.leavingReasonUid = _tmp_leavingReasonUid
          this.leavingReasonTitle = _tmp_leavingReasonTitle
          this.leavingReasonMCSN = _tmp_leavingReasonMCSN
          this.leavingReasonCSN = _tmp_leavingReasonCSN
          this.leavingReasonLCB = _tmp_leavingReasonLCB
          this.leavingReasonLct = _tmp_leavingReasonLct
        }
      }
    }
  }

  override suspend fun findByUidAsync(uid: Long): LeavingReason? =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = "SELECT * FROM LeavingReason WHERE leavingReasonUid = ?",
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,uid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_leavingReasonUid = _result.getLong("leavingReasonUid")
        val _tmp_leavingReasonTitle = _result.getString("leavingReasonTitle")
        val _tmp_leavingReasonMCSN = _result.getLong("leavingReasonMCSN")
        val _tmp_leavingReasonCSN = _result.getLong("leavingReasonCSN")
        val _tmp_leavingReasonLCB = _result.getInt("leavingReasonLCB")
        val _tmp_leavingReasonLct = _result.getLong("leavingReasonLct")
        LeavingReason().apply {
          this.leavingReasonUid = _tmp_leavingReasonUid
          this.leavingReasonTitle = _tmp_leavingReasonTitle
          this.leavingReasonMCSN = _tmp_leavingReasonMCSN
          this.leavingReasonCSN = _tmp_leavingReasonCSN
          this.leavingReasonLCB = _tmp_leavingReasonLCB
          this.leavingReasonLct = _tmp_leavingReasonLct
        }
      }
    }
  }

  override suspend fun findByUidList(uidList: List<Long>): List<Long> =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = "SELECT leavingReasonUid FROM LeavingReason WHERE leavingReasonUid IN (?)",
    hasListParams = true,
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setArray(1, _stmt.getConnection().createArrayOrProxyArrayOf("BIGINT",
        uidList.toTypedArray()))
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapRows {
        _result.getLong(1)
      }
    }
  }

  override fun findByUidLive(uid: Long): Flow<LeavingReason?> =
      _db.doorFlow(arrayOf("LeavingReason")) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = "SELECT * FROM LeavingReason WHERE leavingReasonUid = ?",
      readOnly = true,)
    ) { _stmt -> 
      _stmt.setLong(1,uid)
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapNextRow(null) {
          val _tmp_leavingReasonUid = _result.getLong("leavingReasonUid")
          val _tmp_leavingReasonTitle = _result.getString("leavingReasonTitle")
          val _tmp_leavingReasonMCSN = _result.getLong("leavingReasonMCSN")
          val _tmp_leavingReasonCSN = _result.getLong("leavingReasonCSN")
          val _tmp_leavingReasonLCB = _result.getInt("leavingReasonLCB")
          val _tmp_leavingReasonLct = _result.getLong("leavingReasonLct")
          LeavingReason().apply {
            this.leavingReasonUid = _tmp_leavingReasonUid
            this.leavingReasonTitle = _tmp_leavingReasonTitle
            this.leavingReasonMCSN = _tmp_leavingReasonMCSN
            this.leavingReasonCSN = _tmp_leavingReasonCSN
            this.leavingReasonLCB = _tmp_leavingReasonLCB
            this.leavingReasonLct = _tmp_leavingReasonLct
          }
        }
      }
    }
  }

  override suspend fun getReasonsFromUids(uidList: List<Long>): List<UidAndLabel> =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |SELECT LeavingReason.leavingReasonUid AS uid, 
    |            LeavingReason.leavingReasonTitle As labelName  
    |            FROM LeavingReason WHERE leavingReasonUid IN (?)
    """.trimMargin(),
    hasListParams = true,
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setArray(1, _stmt.getConnection().createArrayOrProxyArrayOf("BIGINT",
        uidList.toTypedArray()))
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapRows {
        val _tmp_uid = _result.getLong("uid")
        val _tmp_labelName = _result.getString("labelName")
        UidAndLabel().apply {
          this.uid = _tmp_uid
          this.labelName = _tmp_labelName
        }
      }
    }
  }
}
