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.HolidayCalendar
import com.ustadmobile.lib.db.entities.HolidayCalendarWithNumEntries
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 HolidayCalendarDao_JdbcImpl(
  public val _db: RoomDatabase,
) : HolidayCalendarDao() {
  public val _insertAdapterHolidayCalendar_upsert: EntityInsertionAdapter<HolidayCalendar> = object
      : EntityInsertionAdapter<HolidayCalendar>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT OR REPLACE INTO HolidayCalendar (umCalendarUid, umCalendarName, umCalendarCategory, umCalendarActive, umCalendarMasterChangeSeqNum, umCalendarLocalChangeSeqNum, umCalendarLastChangedBy, umCalendarLct) VALUES(?, ?, ?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO HolidayCalendar (umCalendarUid, umCalendarName, umCalendarCategory, umCalendarActive, umCalendarMasterChangeSeqNum, umCalendarLocalChangeSeqNum, umCalendarLastChangedBy, umCalendarLct) VALUES(COALESCE(?,nextval('HolidayCalendar_umCalendarUid_seq')), ?, ?, ?, ?, ?, ?, ?) ON CONFLICT (umCalendarUid) DO UPDATE SET umCalendarName = excluded.umCalendarName,umCalendarCategory = excluded.umCalendarCategory,umCalendarActive = excluded.umCalendarActive,umCalendarMasterChangeSeqNum = excluded.umCalendarMasterChangeSeqNum,umCalendarLocalChangeSeqNum = excluded.umCalendarLocalChangeSeqNum,umCalendarLastChangedBy = excluded.umCalendarLastChangedBy,umCalendarLct = excluded.umCalendarLct" + if(returnsId) { " RETURNING umCalendarUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: HolidayCalendar) {
      if(entity.umCalendarUid == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.umCalendarUid)
      }
      stmt.setString(2, entity.umCalendarName)
      stmt.setInt(3, entity.umCalendarCategory)
      stmt.setBoolean(4, entity.umCalendarActive)
      stmt.setLong(5, entity.umCalendarMasterChangeSeqNum)
      stmt.setLong(6, entity.umCalendarLocalChangeSeqNum)
      stmt.setInt(7, entity.umCalendarLastChangedBy)
      stmt.setLong(8, entity.umCalendarLct)
    }
  }

  public val _insertAdapterHolidayCalendar_abort: EntityInsertionAdapter<HolidayCalendar> = object :
      EntityInsertionAdapter<HolidayCalendar>(_db) {
    override fun makeSql(returnsId: Boolean): String = when(dbType) {
      DoorDbType.SQLITE -> {
        "INSERT INTO HolidayCalendar (umCalendarUid, umCalendarName, umCalendarCategory, umCalendarActive, umCalendarMasterChangeSeqNum, umCalendarLocalChangeSeqNum, umCalendarLastChangedBy, umCalendarLct) VALUES(?, ?, ?, ?, ?, ?, ?, ?)"
      }
      DoorDbType.POSTGRES ->  {
        "INSERT INTO HolidayCalendar (umCalendarUid, umCalendarName, umCalendarCategory, umCalendarActive, umCalendarMasterChangeSeqNum, umCalendarLocalChangeSeqNum, umCalendarLastChangedBy, umCalendarLct) VALUES(COALESCE(?,nextval('HolidayCalendar_umCalendarUid_seq')), ?, ?, ?, ?, ?, ?, ?)" + if(returnsId) { " RETURNING umCalendarUid" } else "" 
      }
      else -> {
        throw IllegalArgumentException("Unsupported db type")
      }
    }

    override fun bindPreparedStmtToEntity(stmt: PreparedStatement, entity: HolidayCalendar) {
      if(entity.umCalendarUid == 0L) {
        stmt.setObject(1, null)
      } else {
        stmt.setLong(1, entity.umCalendarUid)
      }
      stmt.setString(2, entity.umCalendarName)
      stmt.setInt(3, entity.umCalendarCategory)
      stmt.setBoolean(4, entity.umCalendarActive)
      stmt.setLong(5, entity.umCalendarMasterChangeSeqNum)
      stmt.setLong(6, entity.umCalendarLocalChangeSeqNum)
      stmt.setInt(7, entity.umCalendarLastChangedBy)
      stmt.setLong(8, entity.umCalendarLct)
    }
  }

  override fun replaceList(list: List<HolidayCalendar>) {
    _insertAdapterHolidayCalendar_upsert.insertList(list)
  }

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

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

  public override fun insertList(entityList: List<HolidayCalendar>) {
    _insertAdapterHolidayCalendar_abort.insertList(entityList)
  }

  override suspend fun updateAsync(entity: HolidayCalendar): Int {
    var _result = 0
    val _sql =
        "UPDATE HolidayCalendar SET umCalendarName = ?, umCalendarCategory = ?, umCalendarActive = ?, umCalendarMasterChangeSeqNum = ?, umCalendarLocalChangeSeqNum = ?, umCalendarLastChangedBy = ?, umCalendarLct = ? WHERE umCalendarUid = ?"
    _db.prepareAndUseStatementAsync(_sql) {
       _stmt ->
      _stmt.setString(1, entity.umCalendarName)
      _stmt.setInt(2, entity.umCalendarCategory)
      _stmt.setBoolean(3, entity.umCalendarActive)
      _stmt.setLong(4, entity.umCalendarMasterChangeSeqNum)
      _stmt.setLong(5, entity.umCalendarLocalChangeSeqNum)
      _stmt.setInt(6, entity.umCalendarLastChangedBy)
      _stmt.setLong(7, entity.umCalendarLct)
      _stmt.setLong(8, entity.umCalendarUid)
      _result += _stmt.executeUpdateAsyncKmp()
    }
    return _result
  }

  public override fun update(entity: HolidayCalendar) {
    val _sql =
        "UPDATE HolidayCalendar SET umCalendarName = ?, umCalendarCategory = ?, umCalendarActive = ?, umCalendarMasterChangeSeqNum = ?, umCalendarLocalChangeSeqNum = ?, umCalendarLastChangedBy = ?, umCalendarLct = ? WHERE umCalendarUid = ?"
    _db.prepareAndUseStatement(_sql) {
       _stmt ->
      _stmt.setString(1, entity.umCalendarName)
      _stmt.setInt(2, entity.umCalendarCategory)
      _stmt.setBoolean(3, entity.umCalendarActive)
      _stmt.setLong(4, entity.umCalendarMasterChangeSeqNum)
      _stmt.setLong(5, entity.umCalendarLocalChangeSeqNum)
      _stmt.setInt(6, entity.umCalendarLastChangedBy)
      _stmt.setLong(7, entity.umCalendarLct)
      _stmt.setLong(8, entity.umCalendarUid)
      _stmt.executeUpdate()
    }
  }

  override fun findAllHolidaysWithEntriesCount(): PagingSource<Int, HolidayCalendarWithNumEntries> =
      object : DoorLimitOffsetPagingSource<HolidayCalendarWithNumEntries>(db = _db
  , tableNames = arrayOf("Holiday", "HolidayCalendar")
  ) {
    override suspend fun loadRows(_limit: Int, _offset: Int): List<HolidayCalendarWithNumEntries> =
        _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |SELECT * FROM (SELECT HolidayCalendar.* ,
      |            (SELECT COUNT(*) FROM Holiday 
      |               WHERE holHolidayCalendarUid = HolidayCalendar.umCalendarUid 
      |               AND CAST(holActive AS INTEGER) = 1) AS numEntries 
      |             FROM HolidayCalendar WHERE CAST(umCalendarActive AS INTEGER) = 1 AND 
      |             umCalendarCategory = 1) AS _PagingData LIMIT ? OFFSET ?
      """.trimMargin(),
      readOnly = true,)
    ) { _stmt -> 
      _stmt.setInt(1,_limit)
      _stmt.setInt(2,_offset)
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapRows {
          val _tmp_numEntries = _result.getInt("numEntries")
          val _tmp_umCalendarUid = _result.getLong("umCalendarUid")
          val _tmp_umCalendarName = _result.getString("umCalendarName")
          val _tmp_umCalendarCategory = _result.getInt("umCalendarCategory")
          val _tmp_umCalendarActive = _result.getBoolean("umCalendarActive")
          val _tmp_umCalendarMasterChangeSeqNum = _result.getLong("umCalendarMasterChangeSeqNum")
          val _tmp_umCalendarLocalChangeSeqNum = _result.getLong("umCalendarLocalChangeSeqNum")
          val _tmp_umCalendarLastChangedBy = _result.getInt("umCalendarLastChangedBy")
          val _tmp_umCalendarLct = _result.getLong("umCalendarLct")
          HolidayCalendarWithNumEntries().apply {
            this.numEntries = _tmp_numEntries
            this.umCalendarUid = _tmp_umCalendarUid
            this.umCalendarName = _tmp_umCalendarName
            this.umCalendarCategory = _tmp_umCalendarCategory
            this.umCalendarActive = _tmp_umCalendarActive
            this.umCalendarMasterChangeSeqNum = _tmp_umCalendarMasterChangeSeqNum
            this.umCalendarLocalChangeSeqNum = _tmp_umCalendarLocalChangeSeqNum
            this.umCalendarLastChangedBy = _tmp_umCalendarLastChangedBy
            this.umCalendarLct = _tmp_umCalendarLct
          }
        }
      }
    }

    override suspend fun countRows(): Int = _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql = """
      |SELECT COUNT(*) FROM (SELECT HolidayCalendar.* ,
      |            (SELECT COUNT(*) FROM Holiday 
      |               WHERE holHolidayCalendarUid = HolidayCalendar.umCalendarUid 
      |               AND CAST(holActive AS INTEGER) = 1) AS numEntries 
      |             FROM HolidayCalendar WHERE CAST(umCalendarActive AS INTEGER) = 1 AND 
      |             umCalendarCategory = 1) AS _PagingCount
      """.trimMargin(),
      readOnly = true,)
    ) { _stmt -> 
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapNextRow(0) {
          _result.getInt(1)
        }
      }
    }
  }

  override fun findAllHolidaysLiveData(): Flow<List<HolidayCalendar>> =
      _db.doorFlow(arrayOf("HolidayCalendar")) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql =
          "SELECT * FROM HolidayCalendar WHERE CAST(umCalendarActive AS INTEGER) = 1 AND umCalendarCategory = 1",
      readOnly = true,)
    ) { _stmt -> 
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapRows {
          val _tmp_umCalendarUid = _result.getLong("umCalendarUid")
          val _tmp_umCalendarName = _result.getString("umCalendarName")
          val _tmp_umCalendarCategory = _result.getInt("umCalendarCategory")
          val _tmp_umCalendarActive = _result.getBoolean("umCalendarActive")
          val _tmp_umCalendarMasterChangeSeqNum = _result.getLong("umCalendarMasterChangeSeqNum")
          val _tmp_umCalendarLocalChangeSeqNum = _result.getLong("umCalendarLocalChangeSeqNum")
          val _tmp_umCalendarLastChangedBy = _result.getInt("umCalendarLastChangedBy")
          val _tmp_umCalendarLct = _result.getLong("umCalendarLct")
          HolidayCalendar().apply {
            this.umCalendarUid = _tmp_umCalendarUid
            this.umCalendarName = _tmp_umCalendarName
            this.umCalendarCategory = _tmp_umCalendarCategory
            this.umCalendarActive = _tmp_umCalendarActive
            this.umCalendarMasterChangeSeqNum = _tmp_umCalendarMasterChangeSeqNum
            this.umCalendarLocalChangeSeqNum = _tmp_umCalendarLocalChangeSeqNum
            this.umCalendarLastChangedBy = _tmp_umCalendarLastChangedBy
            this.umCalendarLct = _tmp_umCalendarLct
          }
        }
      }
    }
  }

  override fun findByUidLive(uid: Long): Flow<HolidayCalendar?> =
      _db.doorFlow(arrayOf("HolidayCalendar")) {
    _db.prepareAndUseStatementAsync(PreparedStatementConfig(
      sql =
          "SELECT * FROM HolidayCalendar WHERE umCalendarUid = ? AND CAST(umCalendarActive AS INTEGER) = 1",
      readOnly = true,)
    ) { _stmt -> 
      _stmt.setLong(1,uid)
      _stmt.executeQueryAsyncKmp().useResults{ _result -> 
        _result.mapNextRow(null) {
          val _tmp_umCalendarUid = _result.getLong("umCalendarUid")
          val _tmp_umCalendarName = _result.getString("umCalendarName")
          val _tmp_umCalendarCategory = _result.getInt("umCalendarCategory")
          val _tmp_umCalendarActive = _result.getBoolean("umCalendarActive")
          val _tmp_umCalendarMasterChangeSeqNum = _result.getLong("umCalendarMasterChangeSeqNum")
          val _tmp_umCalendarLocalChangeSeqNum = _result.getLong("umCalendarLocalChangeSeqNum")
          val _tmp_umCalendarLastChangedBy = _result.getInt("umCalendarLastChangedBy")
          val _tmp_umCalendarLct = _result.getLong("umCalendarLct")
          HolidayCalendar().apply {
            this.umCalendarUid = _tmp_umCalendarUid
            this.umCalendarName = _tmp_umCalendarName
            this.umCalendarCategory = _tmp_umCalendarCategory
            this.umCalendarActive = _tmp_umCalendarActive
            this.umCalendarMasterChangeSeqNum = _tmp_umCalendarMasterChangeSeqNum
            this.umCalendarLocalChangeSeqNum = _tmp_umCalendarLocalChangeSeqNum
            this.umCalendarLastChangedBy = _tmp_umCalendarLastChangedBy
            this.umCalendarLct = _tmp_umCalendarLct
          }
        }
      }
    }
  }

  override suspend fun findByUid(uid: Long): HolidayCalendar? =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = "SELECT * FROM HolidayCalendar WHERE umCalendarUid = ?",
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,uid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_umCalendarUid = _result.getLong("umCalendarUid")
        val _tmp_umCalendarName = _result.getString("umCalendarName")
        val _tmp_umCalendarCategory = _result.getInt("umCalendarCategory")
        val _tmp_umCalendarActive = _result.getBoolean("umCalendarActive")
        val _tmp_umCalendarMasterChangeSeqNum = _result.getLong("umCalendarMasterChangeSeqNum")
        val _tmp_umCalendarLocalChangeSeqNum = _result.getLong("umCalendarLocalChangeSeqNum")
        val _tmp_umCalendarLastChangedBy = _result.getInt("umCalendarLastChangedBy")
        val _tmp_umCalendarLct = _result.getLong("umCalendarLct")
        HolidayCalendar().apply {
          this.umCalendarUid = _tmp_umCalendarUid
          this.umCalendarName = _tmp_umCalendarName
          this.umCalendarCategory = _tmp_umCalendarCategory
          this.umCalendarActive = _tmp_umCalendarActive
          this.umCalendarMasterChangeSeqNum = _tmp_umCalendarMasterChangeSeqNum
          this.umCalendarLocalChangeSeqNum = _tmp_umCalendarLocalChangeSeqNum
          this.umCalendarLastChangedBy = _tmp_umCalendarLastChangedBy
          this.umCalendarLct = _tmp_umCalendarLct
        }
      }
    }
  }

  override suspend fun findByUidAsync(uid: Long): HolidayCalendar? =
      _db.prepareAndUseStatementAsync(PreparedStatementConfig(
    sql = "SELECT * FROM HolidayCalendar WHERE umCalendarUid = ?",
    readOnly = true,)
  ) { _stmt -> 
    _stmt.setLong(1,uid)
    _stmt.executeQueryAsyncKmp().useResults{ _result -> 
      _result.mapNextRow(null) {
        val _tmp_umCalendarUid = _result.getLong("umCalendarUid")
        val _tmp_umCalendarName = _result.getString("umCalendarName")
        val _tmp_umCalendarCategory = _result.getInt("umCalendarCategory")
        val _tmp_umCalendarActive = _result.getBoolean("umCalendarActive")
        val _tmp_umCalendarMasterChangeSeqNum = _result.getLong("umCalendarMasterChangeSeqNum")
        val _tmp_umCalendarLocalChangeSeqNum = _result.getLong("umCalendarLocalChangeSeqNum")
        val _tmp_umCalendarLastChangedBy = _result.getInt("umCalendarLastChangedBy")
        val _tmp_umCalendarLct = _result.getLong("umCalendarLct")
        HolidayCalendar().apply {
          this.umCalendarUid = _tmp_umCalendarUid
          this.umCalendarName = _tmp_umCalendarName
          this.umCalendarCategory = _tmp_umCalendarCategory
          this.umCalendarActive = _tmp_umCalendarActive
          this.umCalendarMasterChangeSeqNum = _tmp_umCalendarMasterChangeSeqNum
          this.umCalendarLocalChangeSeqNum = _tmp_umCalendarLocalChangeSeqNum
          this.umCalendarLastChangedBy = _tmp_umCalendarLastChangedBy
          this.umCalendarLct = _tmp_umCalendarLct
        }
      }
    }
  }
}
