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.Schedule
import kotlin.Boolean
import kotlin.Int
import kotlin.Long
import kotlin.Unit
import kotlin.collections.List

@Dao
public actual abstract class ScheduleDao : BaseDao<Schedule>, OneToManyJoinDao<Schedule> {
  @Query(`value` =
      "\n     REPLACE INTO ScheduleReplicate(schedulePk, scheduleDestination)\n      SELECT DISTINCT Schedule.scheduleUid AS schedulePk,\n             :newNodeId AS scheduleDestination\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 Schedule\n                   ON Schedule.scheduleClazzUid = Clazz.clazzUid\n       WHERE UserSession.usClientNodeId = :newNodeId\n         AND UserSession.usStatus = 1\n         AND Schedule.scheduleLastChangedTime != COALESCE(\n             (SELECT scheduleVersionId\n                FROM ScheduleReplicate\n               WHERE schedulePk = Schedule.scheduleUid\n                 AND scheduleDestination = :newNodeId), 0) \n      /*psql ON CONFLICT(schedulePk, scheduleDestination) DO UPDATE\n             SET schedulePending = true\n      */       \n ")
  public actual abstract suspend fun replicateOnNewNode(newNodeId: Long): Unit

  @Query(`value` =
      "\n REPLACE INTO ScheduleReplicate(schedulePk, scheduleDestination)\n  SELECT DISTINCT Schedule.scheduleUid AS scheduleUid,\n         UserSession.usClientNodeId AS scheduleDestination\n    FROM ChangeLog\n         JOIN Schedule\n              ON ChangeLog.chTableId = 21\n                 AND Schedule.scheduleUid = ChangeLog.chEntityPk\n         JOIN Clazz\n              ON Clazz.clazzUid = Schedule.scheduleClazzUid\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 Schedule.scheduleLastChangedTime != COALESCE(\n         (SELECT scheduleVersionId\n            FROM ScheduleReplicate\n           WHERE schedulePk = Schedule.scheduleUid\n             AND scheduleDestination = UserSession.usClientNodeId), 0)\n /*psql ON CONFLICT(schedulePk, scheduleDestination) DO UPDATE\n     SET schedulePending = true\n  */               \n    ")
  public actual abstract suspend fun replicateOnChange(): Unit

  @Insert(onConflict = 3)
  public actual abstract override fun insert(entity: Schedule): Long

  @Update(onConflict = 3)
  public actual abstract suspend fun updateAsync(entity: Schedule): Int

  @Query(`value` =
      "\n        UPDATE Schedule \n           SET scheduleActive = :active,\n               scheduleLastChangedTime = :changeTime\n         WHERE scheduleUid = :scheduleUid\n            ")
  public actual abstract suspend fun updateScheduleActivated(
    scheduleUid: Long,
    active: Boolean,
    changeTime: Long,
  ): Unit

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

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

  @Query(`value` =
      "SELECT * FROM Schedule WHERE scheduleClazzUid = :clazzUid AND CAST(scheduleActive AS INTEGER) = 1 ")
  public actual abstract fun findAllSchedulesByClazzUid(clazzUid: Long):
      DataSource.Factory<Int, Schedule>

  @Query(`value` =
      "SELECT * FROM Schedule WHERE scheduleClazzUid = :clazzUid AND CAST(scheduleActive AS INTEGER) = 1")
  public actual abstract fun findAllSchedulesByClazzUidAsList(clazzUid: Long): List<Schedule>

  @Query(`value` =
      "SELECT * FROM Schedule WHERE scheduleClazzUid = :clazzUid AND CAST(scheduleActive AS INTEGER) = 1")
  public actual abstract fun findAllSchedulesByClazzUidAsLiveList(clazzUid: Long):
      LiveData<List<Schedule>>

  @Query(`value` =
      "SELECT * FROM Schedule WHERE scheduleClazzUid = :clazzUid AND CAST(scheduleActive AS INTEGER) = 1 ")
  public actual abstract suspend fun findAllSchedulesByClazzUidAsync(clazzUid: Long): List<Schedule>
}
