package com.ustadmobile.core.db.dao

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import com.ustadmobile.lib.db.entities.UserSession
import com.ustadmobile.lib.db.entities.UserSessionAndPerson
import kotlin.Any
import kotlin.Int
import kotlin.Long
import kotlin.String
import kotlin.collections.List
import kotlinx.coroutines.flow.Flow

@Dao
public actual abstract class UserSessionDao {
  @Insert(
    entity = Any::class,
    onConflict = 3,
  )
  public actual abstract suspend fun insertSession(session: UserSession): Long

  @Query(`value` =
      "\n        SELECT UserSession.*\n          FROM UserSession\n         WHERE usPersonUid = :personUid \n    ")
  public actual abstract suspend fun findSessionsByPerson(personUid: Long): List<UserSession>

  @Query(`value` =
      "\n            SELECT UserSession.*, Person.*, PersonPicture.*\n              FROM UserSession\n                   JOIN Person \n                        ON Person.personUid = UserSession.usPersonUid\n                   LEFT JOIN PersonPicture\n                        ON PersonPicture.personPictureUid = UserSession.usPersonUid\n             WHERE UserSession.usClientNodeId = (\n                   SELECT COALESCE(\n                          (SELECT nodeClientId \n                            FROM SyncNode\n                           LIMIT 1), 0))\n               AND UserSession.usStatus = 1        \n               AND (UserSession.usSessionType & 8) != 8\n            ")
  public actual abstract fun findAllLocalSessionsLive(): Flow<List<UserSessionAndPerson>>

  @Query(`value` =
      "\n            SELECT UserSession.*, Person.*, PersonPicture.*\n              FROM UserSession\n                   JOIN Person \n                        ON Person.personUid = UserSession.usPersonUid\n                   LEFT JOIN PersonPicture\n                        ON PersonPicture.personPictureUid = UserSession.usPersonUid\n             WHERE UserSession.usClientNodeId = (\n                   SELECT COALESCE(\n                          (SELECT nodeClientId \n                            FROM SyncNode\n                           LIMIT 1), 0))\n               AND UserSession.usStatus = 1        \n               AND (UserSession.usSessionType & 8) != 8\n            ")
  public actual abstract suspend fun findAllLocalSessionsAsync(): List<UserSessionAndPerson>

  @Query(`value` =
      "\n            SELECT UserSession.*, Person.*\n              FROM UserSession\n                   JOIN Person ON UserSession.usPersonUid = Person.personUid\n             WHERE Person.username = :username\n               AND UserSession.usClientNodeId = (\n                   SELECT COALESCE(\n                          (SELECT nodeClientId \n                            FROM SyncNode\n                           LIMIT 1), 0))\n               AND UserSession.usStatus = 1        \n            ")
  public actual abstract suspend fun findLocalSessionByUsername(username: String?):
      UserSessionAndPerson?

  @Query(`value` =
      "\n        SELECT COUNT(*)\n          FROM UserSession\n               JOIN Person \n                    ON UserSession.usPersonUid = Person.personUid\n         WHERE UserSession.usClientNodeId = (\n                   SELECT COALESCE(\n                          (SELECT nodeClientId \n                            FROM SyncNode\n                           LIMIT 1), 0))\n           AND UserSession.usStatus = 1                \n           AND (:maxDateOfBirth = 0 OR Person.dateOfBirth < :maxDateOfBirth)\n           AND (UserSession.usSessionType & 8) != 8            \n    ")
  public actual abstract suspend fun countAllLocalSessionsAsync(maxDateOfBirth: Long): Int

  @Query(`value` =
      "\n        UPDATE UserSession\n           SET usAuth = null,\n               usStatus = :newStatus,\n               usReason = :reason,\n               usEndTime = :endTime,\n               usLct = :endTime\n         WHERE UserSession.usUid = :sessionUid\n    ")
  public actual abstract suspend fun endSession(
    sessionUid: Long,
    newStatus: Int,
    reason: Int,
    endTime: Long,
  )

  @Query(`value` =
      "\n        SELECT UserSession.*\n          FROM UserSession\n         WHERE UserSession.usUid = :sessionUid\n         LIMIT 1\n    ")
  public actual abstract fun findByUidLive(sessionUid: Long): Flow<UserSession?>

  @Query(`value` =
      "\n        UPDATE UserSession\n           SET usAuth = null,\n               usStatus = :newStatus,\n               usReason = :reason,\n               usLct = :changeTime\n         WHERE usPersonUid = :personUid\n           AND usClientNodeId != :exemptNodeId\n           AND usStatus != :newStatus                     \n    ")
  public actual abstract suspend fun endOtherSessions(
    personUid: Long,
    exemptNodeId: Long,
    newStatus: Int,
    reason: Int,
    changeTime: Long,
  )

  @Query(`value` =
      "\n        SELECT DISTINCT UserSession.usClientNodeId\n          FROM UserSession\n         WHERE UserSession.usPersonUid IN (:personUids)\n           AND UserSession.usStatus = 1\n    ")
  public actual abstract suspend fun findActiveNodeIdsByPersonUids(personUids: List<Long>):
      List<Long>

  @Query(`value` =
      "\n        SELECT DISTINCT UserSession.usClientNodeId\n          FROM UserSession\n               JOIN PersonGroupMember \n                    ON PersonGroupMember.groupMemberPersonUid = UserSession.usPersonUid\n         WHERE PersonGroupMember.groupMemberGroupUid IN (:groupUids)            \n    ")
  public actual abstract suspend fun findActiveNodesIdsByGroupUids(groupUids: List<Long>):
      List<Long>

  @Query(`value` =
      "\n        SELECT COUNT(*)\n          FROM UserSession\n         WHERE UserSession.usPersonUid = :personUid\n           AND UserSession.usStatus = 1\n           AND UserSession.usClientNodeId = :nodeId\n    ")
  public actual abstract suspend fun countActiveSessionsForUserAndNode(personUid: Long,
      nodeId: Long): Int
}
