package com.ustadmobile.core.db.dao

import androidx.lifecycle.LiveData
import androidx.paging.DataSource
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.Boolean
import kotlin.Int
import kotlin.Long
import kotlin.Unit
import kotlin.collections.List

@Dao
public actual abstract class ClazzLogDao : BaseDao<ClazzLog> {
  @Query(`value` =
      "\n     REPLACE INTO ClazzLogReplicate(clPk, clDestination)\n      SELECT DISTINCT ClazzLog.clazzLogUid AS clUid,\n             :newNodeId AS clDestination\n        FROM UserSession\n             JOIN PersonGroupMember \n                  ON UserSession.usPersonUid = PersonGroupMember.groupMemberPersonUid\n             \n               JOIN ScopedGrant\n                    ON ScopedGrant.sgGroupUid = PersonGroupMember.groupMemberGroupUid\n                        AND (ScopedGrant.sgPermissions & \n        \n                  2 \n                  \n                       ) > 0\n               JOIN Clazz \n                    ON \n            ((ScopedGrant.sgTableId = -2\n                                AND ScopedGrant.sgEntityUid = -2)\n                            OR (ScopedGrant.sgTableId = 6\n                                AND ScopedGrant.sgEntityUid = Clazz.clazzUid)\n                            OR (ScopedGrant.sgTableId = 164\n                                AND ScopedGrant.sgEntityUid = Clazz.clazzSchoolUid))\n        \n        \n             JOIN ClazzLog\n                  ON ClazzLog.clazzLogClazzUid = Clazz.clazzUid\n       WHERE ClazzLog.clazzLogLastChangedTime != COALESCE(\n             (SELECT clVersionId\n                FROM ClazzLogReplicate\n               WHERE clPk = ClazzLog.clazzLogUid\n                 AND clDestination = :newNodeId), 0) \n      /*psql ON CONFLICT(clPk, clDestination) DO UPDATE\n             SET clPending = true\n      */       \n    ")
  public actual abstract suspend fun replicateOnNewNode(newNodeId: Long): Unit

  @Insert(onConflict = 1)
  public actual abstract fun replace(entity: ClazzLog): Long

  @Query(`value` =
      "\n REPLACE INTO ClazzLogReplicate(clPk, clDestination)\n  SELECT DISTINCT ClazzLog.clazzLogUid AS clUid,\n         UserSession.usClientNodeId AS clDestination\n    FROM ChangeLog\n         JOIN ClazzLog \n              ON ChangeLog.chTableId = 14 \n             AND ClazzLog.clazzLogUid = ChangeLog.chEntityPk\n         JOIN Clazz \n              ON Clazz.clazzUid = ClazzLog.clazzLogClazzUid \n         \n            JOIN ScopedGrant\n                 ON \n            ((ScopedGrant.sgTableId = -2\n                                AND ScopedGrant.sgEntityUid = -2)\n                            OR (ScopedGrant.sgTableId = 6\n                                AND ScopedGrant.sgEntityUid = Clazz.clazzUid)\n                            OR (ScopedGrant.sgTableId = 164\n                                AND ScopedGrant.sgEntityUid = Clazz.clazzSchoolUid))\n        \n                    AND (ScopedGrant.sgPermissions & \n        \n              2\n              \n              \n                                                       ) > 0\n             JOIN PersonGroupMember AS PrsGrpMbr\n                   ON ScopedGrant.sgGroupUid = PrsGrpMbr.groupMemberGroupUid\n                                               \n              JOIN UserSession\n                   ON UserSession.usPersonUid = PrsGrpMbr.groupMemberPersonUid\n                      AND UserSession.usStatus = 1\n        \n   WHERE UserSession.usClientNodeId != (\n         SELECT nodeClientId \n           FROM SyncNode\n          LIMIT 1)\n     AND ClazzLog.clazzLogLastChangedTime != COALESCE(\n         (SELECT clVersionId\n            FROM ClazzLogReplicate\n           WHERE clPk = ClazzLog.clazzLogUid\n             AND clDestination = UserSession.usClientNodeId), 0)\n /*psql ON CONFLICT(clPk, clDestination) DO UPDATE\n     SET clPending = true\n  */               \n    ")
  public actual abstract suspend fun replicateOnChange(): Unit

  @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): LiveData<ClazzLog?>

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

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

  @Query(`value` =
      "\n        SELECT EXISTS(SELECT ClazzLog.clazzLogUid FROM ClazzLog WHERE clazzLogClazzUid = :clazzUid \n        AND (:excludeStatusFilter = 0 OR ((ClazzLog.clazzLogStatusFlag & :excludeStatusFilter) = 0)))\n    ")
  public actual abstract fun clazzHasScheduleLive(clazzUid: Long, excludeStatusFilter: Int):
      LiveData<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,
  ): Unit

  @Update(onConflict = 3)
  public actual abstract suspend fun updateAsync(clazzLog: ClazzLog): Unit
}
