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.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.CourseTerminology
import kotlin.Boolean
import kotlin.IllegalArgumentException
import kotlin.Int
import kotlin.Long
import kotlin.String
import kotlin.collections.List

public class CourseTerminologyDao_JdbcImpl(
  public val _db: RoomDatabase,
) : CourseTerminologyDao() {
  public val _insertAdapterCourseTerminology_upsert: EntityInsertionAdapter<CourseTerminology> =
      object : EntityInsertionAdapter<CourseTerminology>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT OR REPLACE INTO CourseTerminology (ctUid, ctTitle, ctTerminology, ctLct) VALUES(?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO CourseTerminology (ctUid, ctTitle, ctTerminology, ctLct) VALUES(COALESCE(?,nextval('CourseTerminology_ctUid_seq')), ?, ?, ?) ON CONFLICT (ctUid) DO UPDATE SET ctTitle = excluded.ctTitle,ctTerminology = excluded.ctTerminology,ctLct = excluded.ctLct" + if(returnsId) { " RETURNING ctUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: CourseTerminology) {
      if(entity.ctUid == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.ctUid)
      }
      stmt.setString(2, entity.ctTitle)
      stmt.setString(3, entity.ctTerminology)
      stmt.setLong(4, entity.ctLct)
    }
  }

  public val _insertAdapterCourseTerminology_abort: EntityInsertionAdapter<CourseTerminology> =
      object : EntityInsertionAdapter<CourseTerminology>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT INTO CourseTerminology (ctUid, ctTitle, ctTerminology, ctLct) VALUES(?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO CourseTerminology (ctUid, ctTitle, ctTerminology, ctLct) VALUES(COALESCE(?,nextval('CourseTerminology_ctUid_seq')), ?, ?, ?)" + if(returnsId) { " RETURNING ctUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: CourseTerminology) {
      if(entity.ctUid == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.ctUid)
      }
      stmt.setString(2, entity.ctTitle)
      stmt.setString(3, entity.ctTerminology)
      stmt.setLong(4, entity.ctLct)
    }
  }

  override suspend fun upsertAsync(entity: CourseTerminology): Long {
    val _retVal = _insertAdapterCourseTerminology_upsert.insertAndReturnIdAsync(entity)
    return _retVal
  }

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

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

  public override fun insertList(entityList: List<CourseTerminology>) {
    _insertAdapterCourseTerminology_abort.insertList(entityList)
  }

  override suspend fun updateAsync(entity: CourseTerminology): Int {
    var _result = 0
    val _sql =
        "UPDATE CourseTerminology SET ctTitle = ?, ctTerminology = ?, ctLct = ? WHERE ctUid = ?"
    _db.prepareAndUseStatementAsync(_sql) {
       _stmt ->
      _stmt.setString(1, entity.ctTitle)
      _stmt.setString(2, entity.ctTerminology)
      _stmt.setLong(3, entity.ctLct)
      _stmt.setLong(4, entity.ctUid)
      _result += _stmt.executeUpdateAsyncKmp()
    }
    return _result
  }

  public override fun update(entity: CourseTerminology) {
    val _sql =
        "UPDATE CourseTerminology SET ctTitle = ?, ctTerminology = ?, ctLct = ? WHERE ctUid = ?"
    _db.prepareAndUseStatement(_sql) {
       _stmt ->
      _stmt.setString(1, entity.ctTitle)
      _stmt.setString(2, entity.ctTerminology)
      _stmt.setLong(3, entity.ctLct)
      _stmt.setLong(4, entity.ctUid)
      _stmt.executeUpdate()
    }
  }

  override fun findAllCourseTerminologyPagingSource(): PagingSource<Int, CourseTerminology> = object
      : DoorLimitOffsetPagingSource<CourseTerminology>(db = _db
  , tableNames = arrayOf("CourseTerminology")
  ) {
    override suspend fun loadRows(_limit: Int, _offset: Int): List<CourseTerminology> =
        _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |SELECT * FROM (
      |        SELECT *
      |         FROM CourseTerminology
      |     ORDER BY ctTitle   
      |    ) AS _PagingData LIMIT ? OFFSET ?
      """.trimMargin(),
      readOnly = true,)
    ) { _stmt -> 
      _stmt.setInt(1,_limit)
      _stmt.setInt(2,_offset)
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapRows {
          val _tmp_ctUid = _result.getLong("ctUid")
          val _tmp_ctTitle = _result.getString("ctTitle")
          val _tmp_ctTerminology = _result.getString("ctTerminology")
          val _tmp_ctLct = _result.getLong("ctLct")
          CourseTerminology().apply {
            this.ctUid = _tmp_ctUid
            this.ctTitle = _tmp_ctTitle
            this.ctTerminology = _tmp_ctTerminology
            this.ctLct = _tmp_ctLct
          }
        }
      }
    }

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

  override fun findAllCourseTerminologyList(): List<CourseTerminology> =
      _db.prepareAndUseStatement(PreparedStatementConfig(
    sql = """
    |
    |        SELECT *
    |         FROM CourseTerminology
    |     ORDER BY ctTitle   
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.executeQuery().useResults{ _result -> 
      _result.mapRows {
        val _tmp_ctUid = _result.getLong("ctUid")
        val _tmp_ctTitle = _result.getString("ctTitle")
        val _tmp_ctTerminology = _result.getString("ctTerminology")
        val _tmp_ctLct = _result.getLong("ctLct")
        CourseTerminology().apply {
          this.ctUid = _tmp_ctUid
          this.ctTitle = _tmp_ctTitle
          this.ctTerminology = _tmp_ctTerminology
          this.ctLct = _tmp_ctLct
        }
      }
    }
  }

  override suspend fun getTerminologyForClazz(clazzUid: Long): CourseTerminology? =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        SELECT *
    |          FROM CourseTerminology
    |               JOIN Clazz 
    |               ON Clazz.clazzTerminologyUid = CourseTerminology.ctUid
    |         WHERE Clazz.clazzUid = ?
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,clazzUid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_ctUid = _result.getLong("ctUid")
        val _tmp_ctTitle = _result.getString("ctTitle")
        val _tmp_ctTerminology = _result.getString("ctTerminology")
        val _tmp_ctLct = _result.getLong("ctLct")
        CourseTerminology().apply {
          this.ctUid = _tmp_ctUid
          this.ctTitle = _tmp_ctTitle
          this.ctTerminology = _tmp_ctTerminology
          this.ctLct = _tmp_ctLct
        }
      }
    }
  }

  override suspend fun getTerminologyForAssignment(assignmentUid: Long): CourseTerminology? =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        SELECT CourseTerminology.*
    |          FROM ClazzAssignment
    |               JOIN Clazz 
    |                    ON Clazz.clazzUid = ClazzAssignment.caClazzUid
    |               JOIN CourseTerminology
    |                    ON CourseTerminology.ctUid = Clazz.clazzTerminologyUid
    |         WHERE ClazzAssignment.caUid = ? 
    |         LIMIT 1
    |    
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,assignmentUid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_ctUid = _result.getLong("ctUid")
        val _tmp_ctTitle = _result.getString("ctTitle")
        val _tmp_ctTerminology = _result.getString("ctTerminology")
        val _tmp_ctLct = _result.getLong("ctLct")
        CourseTerminology().apply {
          this.ctUid = _tmp_ctUid
          this.ctTitle = _tmp_ctTitle
          this.ctTerminology = _tmp_ctTerminology
          this.ctLct = _tmp_ctLct
        }
      }
    }
  }

  override suspend fun findByUidAsync(uid: Long): CourseTerminology? =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = """
    |
    |        SELECT * 
    |         FROM CourseTerminology 
    |        WHERE ctUid = ?
    |        
    """.trimMargin(),
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,uid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_ctUid = _result.getLong("ctUid")
        val _tmp_ctTitle = _result.getString("ctTitle")
        val _tmp_ctTerminology = _result.getString("ctTerminology")
        val _tmp_ctLct = _result.getLong("ctLct")
        CourseTerminology().apply {
          this.ctUid = _tmp_ctUid
          this.ctTitle = _tmp_ctTitle
          this.ctTerminology = _tmp_ctTerminology
          this.ctLct = _tmp_ctLct
        }
      }
    }
  }
}
