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.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.CourseGroupSet
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 CourseGroupSetDao_JdbcImpl(
  public val _db: RoomDatabase,
) : CourseGroupSetDao() {
  public val _insertAdapterCourseGroupSet_upsert: EntityInsertionAdapter<CourseGroupSet> = object :
      EntityInsertionAdapter<CourseGroupSet>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT OR REPLACE INTO CourseGroupSet (cgsUid, cgsName, cgsTotalGroups, cgsActive, cgsClazzUid, cgsLct) VALUES(?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO CourseGroupSet (cgsUid, cgsName, cgsTotalGroups, cgsActive, cgsClazzUid, cgsLct) VALUES(COALESCE(?,nextval('CourseGroupSet_cgsUid_seq')), ?, ?, ?, ?, ?) ON CONFLICT (cgsUid) DO UPDATE SET cgsName = excluded.cgsName,cgsTotalGroups = excluded.cgsTotalGroups,cgsActive = excluded.cgsActive,cgsClazzUid = excluded.cgsClazzUid,cgsLct = excluded.cgsLct" + if(returnsId) { " RETURNING cgsUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: CourseGroupSet) {
      if(entity.cgsUid == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.cgsUid)
      }
      stmt.setString(2, entity.cgsName)
      stmt.setInt(3, entity.cgsTotalGroups)
      stmt.setBoolean(4, entity.cgsActive)
      stmt.setLong(5, entity.cgsClazzUid)
      stmt.setLong(6, entity.cgsLct)
    }
  }

  public val _insertAdapterCourseGroupSet_abort: EntityInsertionAdapter<CourseGroupSet> = object :
      EntityInsertionAdapter<CourseGroupSet>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT INTO CourseGroupSet (cgsUid, cgsName, cgsTotalGroups, cgsActive, cgsClazzUid, cgsLct) VALUES(?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO CourseGroupSet (cgsUid, cgsName, cgsTotalGroups, cgsActive, cgsClazzUid, cgsLct) VALUES(COALESCE(?,nextval('CourseGroupSet_cgsUid_seq')), ?, ?, ?, ?, ?)" + if(returnsId) { " RETURNING cgsUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: CourseGroupSet) {
      if(entity.cgsUid == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.cgsUid)
      }
      stmt.setString(2, entity.cgsName)
      stmt.setInt(3, entity.cgsTotalGroups)
      stmt.setBoolean(4, entity.cgsActive)
      stmt.setLong(5, entity.cgsClazzUid)
      stmt.setLong(6, entity.cgsLct)
    }
  }

  override suspend fun upsertAsync(entity: CourseGroupSet) {
    _insertAdapterCourseGroupSet_upsert.insertAsync(entity)
  }

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

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

  public override fun insertList(entityList: List<CourseGroupSet>) {
    _insertAdapterCourseGroupSet_abort.insertList(entityList)
  }

  override suspend fun updateAsync(entity: CourseGroupSet): Int {
    var _result = 0
    val _sql =
        "UPDATE CourseGroupSet SET cgsName = ?, cgsTotalGroups = ?, cgsActive = ?, cgsClazzUid = ?, cgsLct = ? WHERE cgsUid = ?"
    _db.prepareAndUseStatementAsync(_sql) {
       _stmt ->
      _stmt.setString(1, entity.cgsName)
      _stmt.setInt(2, entity.cgsTotalGroups)
      _stmt.setBoolean(3, entity.cgsActive)
      _stmt.setLong(4, entity.cgsClazzUid)
      _stmt.setLong(5, entity.cgsLct)
      _stmt.setLong(6, entity.cgsUid)
      _result += _stmt.executeUpdateAsyncKmp()
    }
    return _result
  }

  public override fun update(entity: CourseGroupSet) {
    val _sql =
        "UPDATE CourseGroupSet SET cgsName = ?, cgsTotalGroups = ?, cgsActive = ?, cgsClazzUid = ?, cgsLct = ? WHERE cgsUid = ?"
    _db.prepareAndUseStatement(_sql) {
       _stmt ->
      _stmt.setString(1, entity.cgsName)
      _stmt.setInt(2, entity.cgsTotalGroups)
      _stmt.setBoolean(3, entity.cgsActive)
      _stmt.setLong(4, entity.cgsClazzUid)
      _stmt.setLong(5, entity.cgsLct)
      _stmt.setLong(6, entity.cgsUid)
      _stmt.executeUpdate()
    }
  }

  override fun findAllCourseGroupSetForClazz(
    clazzUid: Long,
    searchText: String,
    sortOrder: Int,
  ): PagingSource<Int, CourseGroupSet> = object : DoorLimitOffsetPagingSource<CourseGroupSet>(db =
      _db
  , tableNames = arrayOf("CourseGroupSet")
  ) {
    override suspend fun loadRows(_limit: Int, _offset: Int): List<CourseGroupSet> =
        _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |SELECT * FROM (
      |        SELECT *
      |         FROM CourseGroupSet
      |        WHERE cgsActive
      |          AND cgsClazzUid = ?
      |          AND ((? = '%') OR (cgsName LIKE ?))
      |     ORDER BY CASE(?)
      |              WHEN 1 THEN cgsName
      |              ELSE ''
      |              END ASC,
      |              CASE(?)
      |              WHEN 2 THEN cgsName
      |              ELSE ''
      |              END DESC
      |    ) AS _PagingData LIMIT ? OFFSET ?
      """.trimMargin(),
      readOnly = true,)
    ) { _stmt -> 
      _stmt.setLong(1,clazzUid)
      _stmt.setString(2,searchText)
      _stmt.setString(3,searchText)
      _stmt.setInt(4,sortOrder)
      _stmt.setInt(5,sortOrder)
      _stmt.setInt(6,_limit)
      _stmt.setInt(7,_offset)
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapRows {
          val _tmp_cgsUid = _result.getLong("cgsUid")
          val _tmp_cgsName = _result.getString("cgsName")
          val _tmp_cgsTotalGroups = _result.getInt("cgsTotalGroups")
          val _tmp_cgsActive = _result.getBoolean("cgsActive")
          val _tmp_cgsClazzUid = _result.getLong("cgsClazzUid")
          val _tmp_cgsLct = _result.getLong("cgsLct")
          CourseGroupSet().apply {
            this.cgsUid = _tmp_cgsUid
            this.cgsName = _tmp_cgsName
            this.cgsTotalGroups = _tmp_cgsTotalGroups
            this.cgsActive = _tmp_cgsActive
            this.cgsClazzUid = _tmp_cgsClazzUid
            this.cgsLct = _tmp_cgsLct
          }
        }
      }
    }

    override suspend fun countRows(): Int = _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |SELECT COUNT(*) FROM (
      |        SELECT *
      |         FROM CourseGroupSet
      |        WHERE cgsActive
      |          AND cgsClazzUid = ?
      |          AND ((? = '%') OR (cgsName LIKE ?))
      |     ORDER BY CASE(?)
      |              WHEN 1 THEN cgsName
      |              ELSE ''
      |              END ASC,
      |              CASE(?)
      |              WHEN 2 THEN cgsName
      |              ELSE ''
      |              END DESC
      |    ) AS _PagingCount
      """.trimMargin(),
      readOnly = true,)
    ) { _stmt -> 
      _stmt.setLong(1,clazzUid)
      _stmt.setString(2,searchText)
      _stmt.setString(3,searchText)
      _stmt.setInt(4,sortOrder)
      _stmt.setInt(5,sortOrder)
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapNextRow(0) {
          _result.getInt(1)
        }
      }
    }
  }

  override fun findAllCourseGroupSetForClazzList(clazzUid: Long): List<CourseGroupSet> =
      _db.prepareAndUseStatement(PreparedStatementConfig(
    sql = """
    |
    |        SELECT *
    |         FROM CourseGroupSet
    |        WHERE cgsActive
    |          AND cgsClazzUid = ?
    |     ORDER BY cgsName   
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,clazzUid)
    _stmt.executeQuery().useResults{ _result -> 
      _result.mapRows {
        val _tmp_cgsUid = _result.getLong("cgsUid")
        val _tmp_cgsName = _result.getString("cgsName")
        val _tmp_cgsTotalGroups = _result.getInt("cgsTotalGroups")
        val _tmp_cgsActive = _result.getBoolean("cgsActive")
        val _tmp_cgsClazzUid = _result.getLong("cgsClazzUid")
        val _tmp_cgsLct = _result.getLong("cgsLct")
        CourseGroupSet().apply {
          this.cgsUid = _tmp_cgsUid
          this.cgsName = _tmp_cgsName
          this.cgsTotalGroups = _tmp_cgsTotalGroups
          this.cgsActive = _tmp_cgsActive
          this.cgsClazzUid = _tmp_cgsClazzUid
          this.cgsLct = _tmp_cgsLct
        }
      }
    }
  }

  override suspend fun findAllCourseGroupSetForClazzListAsync(clazzUid: Long): List<CourseGroupSet>
      = _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        SELECT *
    |         FROM CourseGroupSet
    |        WHERE cgsActive
    |          AND cgsClazzUid = ?
    |     ORDER BY cgsName   
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,clazzUid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapRows {
        val _tmp_cgsUid = _result.getLong("cgsUid")
        val _tmp_cgsName = _result.getString("cgsName")
        val _tmp_cgsTotalGroups = _result.getInt("cgsTotalGroups")
        val _tmp_cgsActive = _result.getBoolean("cgsActive")
        val _tmp_cgsClazzUid = _result.getLong("cgsClazzUid")
        val _tmp_cgsLct = _result.getLong("cgsLct")
        CourseGroupSet().apply {
          this.cgsUid = _tmp_cgsUid
          this.cgsName = _tmp_cgsName
          this.cgsTotalGroups = _tmp_cgsTotalGroups
          this.cgsActive = _tmp_cgsActive
          this.cgsClazzUid = _tmp_cgsClazzUid
          this.cgsLct = _tmp_cgsLct
        }
      }
    }
  }

  override suspend fun findByUidAsync(uid: Long): CourseGroupSet? =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        SELECT * 
    |         FROM CourseGroupSet 
    |        WHERE cgsUid = ?
    |        
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,uid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_cgsUid = _result.getLong("cgsUid")
        val _tmp_cgsName = _result.getString("cgsName")
        val _tmp_cgsTotalGroups = _result.getInt("cgsTotalGroups")
        val _tmp_cgsActive = _result.getBoolean("cgsActive")
        val _tmp_cgsClazzUid = _result.getLong("cgsClazzUid")
        val _tmp_cgsLct = _result.getLong("cgsLct")
        CourseGroupSet().apply {
          this.cgsUid = _tmp_cgsUid
          this.cgsName = _tmp_cgsName
          this.cgsTotalGroups = _tmp_cgsTotalGroups
          this.cgsActive = _tmp_cgsActive
          this.cgsClazzUid = _tmp_cgsClazzUid
          this.cgsLct = _tmp_cgsLct
        }
      }
    }
  }

  override fun findByUidAsFlow(uid: Long): Flow<CourseGroupSet?> =
      _db.doorFlow(arrayOf("CourseGroupSet")) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |
      |        SELECT * 
      |         FROM CourseGroupSet 
      |        WHERE cgsUid = ?
      |        
      """.trimMargin(),
      readOnly = true,)
    ) { _stmt -> 
      _stmt.setLong(1,uid)
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapNextRow(null) {
          val _tmp_cgsUid = _result.getLong("cgsUid")
          val _tmp_cgsName = _result.getString("cgsName")
          val _tmp_cgsTotalGroups = _result.getInt("cgsTotalGroups")
          val _tmp_cgsActive = _result.getBoolean("cgsActive")
          val _tmp_cgsClazzUid = _result.getLong("cgsClazzUid")
          val _tmp_cgsLct = _result.getLong("cgsLct")
          CourseGroupSet().apply {
            this.cgsUid = _tmp_cgsUid
            this.cgsName = _tmp_cgsName
            this.cgsTotalGroups = _tmp_cgsTotalGroups
            this.cgsActive = _tmp_cgsActive
            this.cgsClazzUid = _tmp_cgsClazzUid
            this.cgsLct = _tmp_cgsLct
          }
        }
      }
    }
  }
}
