package com.ustadmobile.core.db.dao

import androidx.paging.PagingSource
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.ustadmobile.lib.db.entities.ClazzLog
import kotlin.Any
import kotlin.Boolean
import kotlin.Int
import kotlin.Long
import kotlin.collections.List
import kotlinx.coroutines.flow.Flow

@Dao
public actual abstract class ClazzLogDao : BaseDao<ClazzLog> {
  @Insert(
    onConflict = 1,
    entity = Any::class,
  )
  public actual abstract fun replace(entity: ClazzLog): Long

  @Query(`value` = "SELECT * FROM ClazzLog WHERE clazzLogUid = :uid")
  public actual abstract fun findByUid(uid: Long): ClazzLog?

  @Query(`value` = "SELECT * FROM ClazzLog WHERE clazzLogUid = :uid")
  public actual abstract suspend fun findByUidAsync(uid: Long): ClazzLog?

  @Query(`value` = "SELECT * FROM ClazzLog WHERE clazzLogUid = :uid")
  public actual abstract fun findByUidLive(uid: Long): Flow<ClazzLog?>

  @Query(`value` =
      "\n        SELECT ClazzLog.* \n          FROM ClazzLog \n         WHERE clazzLogClazzUid = :clazzUid\n           AND clazzLog.clazzLogStatusFlag != :excludeStatus\n      ORDER BY ClazzLog.logDate DESC\n    ")
  public actual abstract fun findByClazzUidAsFactory(clazzUid: Long, excludeStatus: Int):
      PagingSource<Int, ClazzLog>

  @Query(`value` =
      "\n        SELECT ClazzLog.* \n          FROM ClazzLog \n         WHERE ClazzLog.clazzLogClazzUid = :clazzUid\n           AND clazzLog.clazzLogStatusFlag != :excludeStatus\n      ORDER BY ClazzLog.logDate ASC\n    ")
  public actual abstract suspend fun findByClazzUidAsync(clazzUid: Long, excludeStatus: Int):
      List<ClazzLog>

  @Query(`value` =
      "\n        SELECT ClazzLog.* \n          FROM ClazzLog \n         WHERE ClazzLog.clazzLogClazzUid = \n               (SELECT ClazzLogInner.clazzLogClazzUid\n                  FROM ClazzLog ClazzLogInner\n                 WHERE ClazzLogInner.clazzLogUid = :clazzLogUid)\n           AND clazzLog.clazzLogStatusFlag != :excludeStatus\n      ORDER BY ClazzLog.logDate ASC\n    ")
  public actual abstract suspend fun findAllForClazzByClazzLogUid(clazzLogUid: Long,
      excludeStatus: Int): List<ClazzLog>

  @Query(`value` =
      "SELECT ClazzLog.* FROM ClazzLog \n        WHERE \n        ClazzLog.clazzLogClazzUid = :clazzUid \n        AND ClazzLog.logDate BETWEEN :fromTime AND :toTime\n        AND (:excludeStatusFilter = 0 OR ((ClazzLog.clazzLogStatusFlag & :excludeStatusFilter) = 0))\n        ORDER BY ClazzLog.logDate DESC\n        LIMIT :limit\n    ")
  public actual abstract suspend fun findByClazzUidWithinTimeRangeAsync(
    clazzUid: Long,
    fromTime: Long,
    toTime: Long,
    excludeStatusFilter: Int,
    limit: Int,
  ): List<ClazzLog>

  @Query(`value` =
      "SELECT ClazzLog.* FROM ClazzLog \n        WHERE \n        ClazzLog.clazzLogClazzUid = :clazzUid \n        AND ClazzLog.logDate BETWEEN :fromTime AND :toTime\n        AND (:excludeStatusFilter = 0 OR ((ClazzLog.clazzLogStatusFlag & :excludeStatusFilter) = 0))\n        ORDER BY ClazzLog.logDate DESC\n        LIMIT :limit\n    ")
  public actual abstract fun findByClazzUidWithinTimeRange(
    clazzUid: Long,
    fromTime: Long,
    toTime: Long,
    excludeStatusFilter: Int,
    limit: Int,
  ): List<ClazzLog>

  @Query(`value` =
      "SELECT ClazzLog.* FROM ClazzLog \n        WHERE \n        ClazzLog.clazzLogClazzUid = :clazzUid \n        AND ClazzLog.logDate BETWEEN :fromTime AND :toTime\n        AND (:statusFilter = 0 OR ClazzLog.clazzLogStatusFlag = :statusFilter)\n        ORDER BY ClazzLog.logDate\n    ")
  public actual abstract fun findByClazzUidWithinTimeRangeLive(
    clazzUid: Long,
    fromTime: Long,
    toTime: Long,
    statusFilter: Int,
  ): Flow<List<ClazzLog>>

  @Query(`value` =
      "\n        SELECT EXISTS\n               (SELECT ClazzLog.clazzLogUid \n                  FROM ClazzLog \n                 WHERE clazzLogClazzUid = :clazzUid \n                 AND (:excludeStatusFilter = 0 \n                      OR ((ClazzLog.clazzLogStatusFlag & :excludeStatusFilter) = 0))\n               )\n    ")
  public actual abstract fun clazzHasScheduleLive(clazzUid: Long, excludeStatusFilter: Int):
      Flow<Boolean>

  @Query(`value` =
      "UPDATE ClazzLog \n        SET clazzLogStatusFlag = :newStatus,\n        clazzLogLastChangedTime = :timeChanged\n        WHERE clazzLogUid = :clazzLogUid")
  public actual abstract fun updateStatusByClazzLogUid(
    clazzLogUid: Long,
    newStatus: Int,
    timeChanged: Long,
  )

  @Update(
    entity = Any::class,
    onConflict = 3,
  )
  public actual abstract suspend fun updateAsync(clazzLog: ClazzLog)

  @Query(`value` =
      "\n        SELECT COALESCE(\n               (SELECT ClazzLog.clazzLogUid\n                  FROM ClazzLog\n                 WHERE ClazzLog.clazzLogClazzUid = :clazzUid\n                   AND (ClazzLog.clazzLogStatusFlag & 8) != 8\n              ORDER BY ClazzLog.logDate DESC\n                 LIMIT 1), 0)\n\n        \n    ")
  public actual abstract suspend fun findMostRecentClazzLogToEditUid(clazzUid: Long): Long

  @Insert(
    onConflict = 1,
    entity = Any::class,
  )
  public actual abstract suspend fun upsertListAsync(entityList: List<ClazzLog>)
}
