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.composites.CourseBlockAndAssignment
import com.ustadmobile.lib.db.composites.CourseBlockAndDbEntities
import com.ustadmobile.lib.db.composites.CourseBlockAndDisplayDetails
import com.ustadmobile.lib.db.composites.CourseBlockAndGradebookDisplayDetails
import com.ustadmobile.lib.db.composites.CourseBlockAndPicture
import com.ustadmobile.lib.db.composites.CourseBlockUidAndClazzUid
import com.ustadmobile.lib.db.entities.CourseBlock
import kotlin.Any
import kotlin.Boolean
import kotlin.Float
import kotlin.Int
import kotlin.Long
import kotlin.String
import kotlin.collections.List
import kotlinx.coroutines.flow.Flow

@Dao
public actual abstract class CourseBlockDao : BaseDao<CourseBlock>, OneToManyJoinDao<CourseBlock> {
  @Query(`value` = "SELECT * FROM CourseBlock WHERE cbUid = :uid")
  public actual abstract suspend fun findByUidAsync(uid: Long): CourseBlock?

  @Query(`value` =
      "\n        SELECT EXISTS(\n               SELECT CourseBlock.cbUid\n                 FROM CourseBlock\n                WHERE CourseBlock.cbUid = :cbUid)\n    ")
  public actual abstract suspend fun existsByUid(cbUid: Long): Boolean

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

  @Insert(
    onConflict = 1,
    entity = Any::class,
  )
  public actual abstract suspend fun replaceListAsync(list: List<CourseBlock>)

  @Query(`value` = "SELECT * FROM CourseBlock WHERE cbUid = :uid")
  public actual abstract fun findByUidAsyncAsFlow(uid: Long): Flow<CourseBlock?>

  @Query(`value` =
      "\n        SELECT CourseBlock.*, CourseBlockPicture.*\n          FROM CourseBlock\n               LEFT JOIN CourseBlockPicture \n                         ON CourseBlockPicture.cbpUid = :uid\n         WHERE CourseBlock.cbUid = :uid                \n    ")
  public actual abstract fun findByUidWithPictureAsFlow(uid: Long): Flow<CourseBlockAndPicture?>

  @Query(`value` =
      "\n        SELECT CourseBlock.*, Assignment.*, Entry.*, Language.*, CourseBlockPicture.*,\n               (SELECT CourseGroupSet.cgsName\n                  FROM CourseGroupSet\n                 WHERE CourseBlock.cbType = 103\n                   AND assignment.caGroupUid != 0\n                   AND CourseGroupSet.cgsUid = assignment.caGroupUid) AS assignmentCourseGroupSetName\n          FROM CourseBlock \n               LEFT JOIN ClazzAssignment AS Assignment\n                         ON assignment.caUid = CourseBlock.cbEntityUid\n                            AND CourseBlock.cbType = 103\n               LEFT JOIN ContentEntry AS Entry\n                         ON entry.contentEntryUid = CourseBlock.cbEntityUid\n                            AND CourseBlock.cbType = 104\n               LEFT JOIN Language\n                         ON Language.langUid = Entry.primaryLanguageUid\n                            AND CourseBlock.cbType = 104\n               LEFT JOIN CourseBlockPicture\n                         ON CourseBlockPicture.cbpUid = CourseBlock.cbUid    \n         WHERE CourseBlock.cbClazzUid = :clazzUid\n           AND (CAST(:includeInactive AS INTEGER) = 1 OR CourseBlock.cbActive)\n           AND (CourseBlock.cbType != 300)\n      ORDER BY CourseBlock.cbIndex\n          ")
  public actual abstract suspend fun findAllCourseBlockByClazzUidAsync(clazzUid: Long,
      includeInactive: Boolean): List<CourseBlockAndDbEntities>

  @Query(`value` =
      "\n        SELECT CourseBlock.*, ContentEntry.*, CourseBlockPicture.*, ContentEntryPicture2.*\n          FROM CourseBlock\n               LEFT JOIN ContentEntry\n                         ON CourseBlock.cbType = 104\n                            AND ContentEntry.contentEntryUid = CourseBlock.cbEntityUid\n               LEFT JOIN CourseBlockPicture\n                         ON CourseBlockPicture.cbpUid = CourseBlock.cbUid    \n               LEFT JOIN ContentEntryPicture2\n                         ON CourseBlock.cbType = 104\n                            AND ContentEntryPicture2.cepUid = CourseBlock.cbEntityUid\n         WHERE CourseBlock.cbClazzUid = :clazzUid\n           AND (CAST(:includeInactive AS INTEGER) = 1 OR CourseBlock.cbActive)\n           AND (CAST(:includeHidden AS INTEGER) = 1 OR NOT CourseBlock.cbHidden)\n           AND (:hideUntilFilterTime >= CourseBlock.cbHideUntilDate)\n           AND (:hideUntilFilterTime >= COALESCE(\n                (SELECT CourseBlockParent.cbHideUntilDate\n                   FROM CourseBlock CourseBlockParent\n                  WHERE CourseBlockParent.cbUid = CourseBlock.cbModuleParentBlockUid), 0))\n           AND (CourseBlock.cbType != 300)\n           AND :accountPersonUid = :accountPersonUid        \n      ORDER BY CourseBlock.cbIndex       \n    ")
  public actual abstract fun findAllCourseBlockByClazzUidAsFlow(
    clazzUid: Long,
    includeInactive: Boolean,
    includeHidden: Boolean,
    hideUntilFilterTime: Long,
    accountPersonUid: Long,
  ): Flow<List<CourseBlockAndDisplayDetails>>

  @Query(`value` =
      "\n        SELECT CourseBlock.*, ContentEntry.*, CourseBlockPicture.*, ContentEntryPicture2.*,\n               CourseBlock.cbUid NOT IN(:collapseList) AS expanded\n          FROM CourseBlock\n               LEFT JOIN ContentEntry\n                         ON CourseBlock.cbType = 104\n                            AND ContentEntry.contentEntryUid = CourseBlock.cbEntityUid\n               LEFT JOIN CourseBlockPicture\n                         ON CourseBlockPicture.cbpUid = CourseBlock.cbUid    \n               LEFT JOIN ContentEntryPicture2\n                         ON CourseBlock.cbType = 104\n                            AND ContentEntryPicture2.cepUid = CourseBlock.cbEntityUid\n         WHERE CourseBlock.cbClazzUid = :clazzUid\n           AND CourseBlock.cbModuleParentBlockUid NOT IN(:collapseList)\n           AND (CAST(:includeInactive AS INTEGER) = 1 OR CourseBlock.cbActive)\n           AND (CAST(:includeHidden AS INTEGER) = 1 OR NOT CourseBlock.cbHidden)\n           AND (:hideUntilFilterTime >= CourseBlock.cbHideUntilDate)\n           AND (:hideUntilFilterTime >= COALESCE(\n                (SELECT CourseBlockParent.cbHideUntilDate\n                   FROM CourseBlock CourseBlockParent\n                  WHERE CourseBlockParent.cbUid = CourseBlock.cbModuleParentBlockUid), 0))\n           AND (CourseBlock.cbType != 300) \n           AND :accountPersonUid = :accountPersonUid       \n      ORDER BY CourseBlock.cbIndex       \n    ")
  public actual abstract fun findAllCourseBlockByClazzUidAsPagingSource(
    clazzUid: Long,
    collapseList: List<Long>,
    includeInactive: Boolean,
    includeHidden: Boolean,
    hideUntilFilterTime: Long,
    accountPersonUid: Long,
  ): PagingSource<Int, CourseBlockAndDisplayDetails>

  @Query(`value` =
      "\n        UPDATE CourseBlock \n           SET cbActive = :active, \n               cbLct = :changeTime\n         WHERE cbUid = :cbUid")
  public actual abstract suspend fun updateActiveByUid(
    cbUid: Long,
    active: Boolean,
    changeTime: Long,
  )

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

  @Query(`value` =
      "\n        SELECT CourseBlock.cbTitle\n          FROM CourseBlock \n         WHERE CourseBlock.cbEntityUid = :assignmentUid\n           AND CourseBlock.cbType = 103\n    ")
  public actual abstract fun getTitleByAssignmentUid(assignmentUid: Long): Flow<String?>

  @Query(`value` =
      "\n        SELECT CourseBlock.*\n          FROM CourseBlock\n         WHERE CourseBlock.cbUid = :courseBlockUid \n    ")
  public actual abstract fun findByUidAsFlow(courseBlockUid: Long): Flow<CourseBlock?>

  @Query(`value` =
      "\n        SELECT COALESCE(CourseBlock.cbUid, 0) AS courseBlockUid,\n               COALESCE(CourseBlock.cbClazzUid, 0) AS clazzUid\n          FROM CourseBlock\n         WHERE CourseBlock.cbUid = \n               (SELECT DiscussionPost.discussionPostCourseBlockUid \n                  FROM DiscussionPost\n                 WHERE DiscussionPost.discussionPostUid = :postUid)\n         LIMIT 1\n    ")
  public actual abstract suspend fun findCourseBlockAndClazzUidByDiscussionPostUid(postUid: Long):
      CourseBlockUidAndClazzUid?

  @Query(`value` =
      "\n        SELECT CourseBlock.*\n          FROM CourseBlock\n         WHERE CourseBlock.cbUid = \n               (SELECT DiscussionPost.discussionPostCourseBlockUid \n                  FROM DiscussionPost\n                 WHERE DiscussionPost.discussionPostUid = :postUid) \n    ")
  public actual abstract suspend fun findCourseBlockByDiscussionPostUid(postUid: Long): CourseBlock?

  @Query(`value` =
      "\n        SELECT COALESCE(CourseBlock.cbClazzUid, 0) AS clazzUid\n          FROM CourseBlock\n         WHERE CourseBlock.cbUid = :courseBlockUid\n    ")
  public actual abstract suspend fun findClazzUidByCourseBlockUid(courseBlockUid: Long): Long

  @Query(`value` =
      "\n        SELECT ClazzAssignment.*, CourseBlock.* \n          FROM ClazzAssignment\n               JOIN CourseBlock \n                    ON CourseBlock.cbEntityUid = :assignmentUid\n         WHERE ClazzAssignment.caUid = :assignmentUid\n         LIMIT 1 \n    ")
  public actual abstract fun findCourseBlockByAssignmentUid(assignmentUid: Long):
      Flow<CourseBlockAndAssignment?>

  @Query(`value` =
      "\n            SELECT CourseBlock.*\n              FROM CourseBlock\n             WHERE CAST(cbUid AS TEXT) = :sourcedId\n                OR cbSourcedId = :sourcedId\n               AND :accountPersonUid != 0 \n        ")
  public actual abstract suspend fun findBySourcedId(sourcedId: String, accountPersonUid: Long):
      CourseBlock?

  @Query(`value` =
      "\n        SELECT CourseBlock.*\n          FROM CourseBlock\n         WHERE CourseBlock.cbClazzUid = :clazzUid \n    ")
  public actual abstract suspend fun findByClazzUid(clazzUid: Long): List<CourseBlock>

  @Query(`value` =
      "\n        SELECT CourseBlock.*, ContentEntry.*, CourseBlockPicture.*, ContentEntryPicture2.*\n          FROM CourseBlock\n               LEFT JOIN ContentEntry\n                         ON CourseBlock.cbType = 104\n                            AND ContentEntry.contentEntryUid = CourseBlock.cbEntityUid\n               LEFT JOIN CourseBlockPicture\n                         ON CourseBlockPicture.cbpUid = CourseBlock.cbUid    \n               LEFT JOIN ContentEntryPicture2\n                         ON CourseBlock.cbType = 104\n                            AND ContentEntryPicture2.cepUid = CourseBlock.cbEntityUid\n         WHERE CourseBlock.cbClazzUid = :clazzUid\n           AND CAST(CourseBlock.cbActive AS INTEGER) = 1\n      ORDER BY CourseBlock.cbIndex\n    ")
  public actual abstract fun findByClazzUidAsFlow(clazzUid: Long):
      Flow<List<CourseBlockAndGradebookDisplayDetails>>

  @Query(`value` =
      "\n        UPDATE CourseBlock\n           SET cbActive = :active,\n               cbLct = :dateLastModified,\n               cbTitle = :title,\n               cbDescription = :description,\n               cbHideUntilDate = :assignDate,\n               cbDeadlineDate = :dueDate,\n               cbMinPoints = :resultValueMin,\n               cbMaxPoints = :resultValueMax\n         WHERE cbUid = :cbUid      \n    ")
  public actual abstract suspend fun updateFromLineItem(
    cbUid: Long,
    active: Boolean,
    dateLastModified: Long,
    title: String,
    description: String,
    assignDate: Long,
    dueDate: Long,
    resultValueMin: Float,
    resultValueMax: Float,
  )

  @Query(`value` =
      "\n        SELECT CourseBlock.cbUid AS courseBlockUid, \n               CourseBlock.cbClazzUid AS clazzUid\n          FROM CourseBlock\n         WHERE cbUid = :cbUid\n           AND :accountPersonUid != 0     \n    ")
  public actual abstract suspend fun findCourseBlockAndClazzUidByCbUid(cbUid: Long,
      accountPersonUid: Long): CourseBlockUidAndClazzUid?
}
