package com.ustadmobile.core.db.dao

import androidx.paging.PagingSource
import com.ustadmobile.door.DoorDatabaseRepository
import com.ustadmobile.door.ext.doorNodeIdHeader
import com.ustadmobile.door.ext.pagingSourceLoadParameters
import com.ustadmobile.door.ext.setRepoUrl
import com.ustadmobile.door.http.RepoDaoFlowHelper
import com.ustadmobile.door.http.RepositoryDaoWithFlowHelper
import com.ustadmobile.door.http.replicateHttpRequestCatchAndLog
import com.ustadmobile.door.http.replicateHttpRequestOrThrow
import com.ustadmobile.door.http.repoHttpRequest
import com.ustadmobile.door.paging.DoorRepositoryReplicatePullPagingSource
import com.ustadmobile.door.paging.endOfPaginationReached
import com.ustadmobile.door.replication.onClientRepoDoorMessageHttpResponse
import com.ustadmobile.door.replication.withRepoChangeMonitor
import com.ustadmobile.door.replication.withRepoChangeMonitorAsync
import com.ustadmobile.door.room.RoomDatabase
import com.ustadmobile.lib.db.composites.PersonAndListDisplayDetails
import com.ustadmobile.lib.db.composites.PersonAndPicture
import com.ustadmobile.lib.db.composites.PersonNames
import com.ustadmobile.lib.db.entities.Person
import com.ustadmobile.lib.db.entities.PersonAndDisplayDetail
import com.ustadmobile.lib.db.entities.PersonAuth
import com.ustadmobile.lib.db.entities.PersonGroup
import com.ustadmobile.lib.db.entities.PersonGroupMember
import com.ustadmobile.lib.db.entities.PersonUidAndPasswordHash
import io.ktor.client.HttpClient
import io.ktor.client.request.`get`
import io.ktor.client.request.`header`
import io.ktor.client.request.parameter
import io.ktor.client.statement.bodyAsText
import kotlin.Int
import kotlin.Long
import kotlin.String
import kotlin.Suppress
import kotlin.collections.List
import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.nullable
import kotlinx.serialization.builtins.serializer

@Suppress("REDUNDANT_PROJECTION", "LocalVariableName", "ClassName")
public class PersonDao_Repo(
  public val _db: RoomDatabase,
  public val _repo: DoorDatabaseRepository,
  public val _dao: PersonDao,
  public val _httpClient: HttpClient,
  public val _clientId: Long,
  public val _endpoint: String,
) : PersonDao(), RepositoryDaoWithFlowHelper {
  override val repoDaoFlowHelper: RepoDaoFlowHelper = RepoDaoFlowHelper(_repo)


  override suspend fun insertListAsync(entityList: List<Person>) {
    _repo.withRepoChangeMonitorAsync("Person") {
      _dao.insertListAsync(entityList)
    }
  }

  override suspend fun insertOrReplace(person: Person) {
    _repo.withRepoChangeMonitorAsync("Person") {
      _dao.insertOrReplace(person)
    }
  }

  override suspend fun countUsername(username: String): Int = _repo.config.json.decodeFromString(
    deserializer = Int.serializer(),
    string =_repo.repoHttpRequest(repoPath = "PersonDao/countUsername") {
      _repo.config.httpClient.`get` {
        setRepoUrl(_repo.config, "PersonDao/countUsername")
        doorNodeIdHeader(_repo)
        `header`("cache-control", "no-store")
        parameter("username", _repo.config.json.encodeToString(String.serializer(), username))
      }
      .bodyAsText()
    }
  )

  override suspend fun findUidAndPasswordHashAsync(username: String): PersonUidAndPasswordHash? {
    val _result = _dao.findUidAndPasswordHashAsync(username)
    return _result
  }

  override suspend fun findByUsernameAndPasswordHash2(username: String, passwordHash: String):
      Person? {
    val _result = _dao.findByUsernameAndPasswordHash2(username, passwordHash)
    return _result
  }

  override fun insertPersonAuth(personAuth: PersonAuth) {
    _repo.withRepoChangeMonitor("PersonAuth") {
      _dao.insertPersonAuth(personAuth)
    }
  }

  override fun findByUsername(username: String?): Person? {
    val _result = _dao.findByUsername(username)
    return _result
  }

  override suspend fun findByUsernameAsync(username: String): Person? {
    val _result = _dao.findByUsernameAsync(username)
    return _result
  }

  override suspend fun findSystemAccount(nodeId: Long): Person? {
    val _result = _dao.findSystemAccount(nodeId)
    return _result
  }

  override fun findByUid(uid: Long): Person? {
    val _result = _dao.findByUid(uid)
    return _result
  }

  override suspend fun findByUidWithPicture(accountPersonUid: Long): PersonAndPicture? {
    _repo.replicateHttpRequestCatchAndLog(repoPath = "PersonDao/findByUidWithPicture") {
      val _response = _httpClient.`get` {
        setRepoUrl(_repo.config, "PersonDao/findByUidWithPicture")
        doorNodeIdHeader(_repo)
        `header`("cache-control", "no-store")
        parameter("accountPersonUid", _repo.config.json.encodeToString(Long.serializer(),
            accountPersonUid))
      }

      _db.onClientRepoDoorMessageHttpResponse(_response, _repo.config.json)
    }
    val _result = _dao.findByUidWithPicture(accountPersonUid)
    return _result
  }

  override fun findByUidWithPictureAsFlow(uid: Long): Flow<PersonAndPicture?> {
    val _result = _dao.findByUidWithPictureAsFlow(uid)
    return _result
  }

  override fun findByUidLive(uid: Long): Flow<Person?> {
    val _result = _dao.findByUidLive(uid)
    return _result
  }

  override suspend fun findByUidAsync(uid: Long): Person? {
    _repo.replicateHttpRequestCatchAndLog(repoPath = "PersonDao/findByUidAsync") {
      val _response = _httpClient.`get` {
        setRepoUrl(_repo.config, "PersonDao/findByUidAsync")
        doorNodeIdHeader(_repo)
        `header`("cache-control", "no-store")
        parameter("uid", _repo.config.json.encodeToString(Long.serializer(), uid))
      }

      _db.onClientRepoDoorMessageHttpResponse(_response, _repo.config.json)
    }
    val _result = _dao.findByUidAsync(uid)
    return _result
  }

  override fun findByUidAsFlow(uid: Long): Flow<Person?> {
    val _result = _dao.findByUidAsFlow(uid)
    return _result
  }

  override suspend fun updateAsync(entity: Person): Int {
    val _result = _repo.withRepoChangeMonitorAsync("Person") {
      _dao.updateAsync(entity)
    }
    return _result
  }

  override suspend fun insertPersonGroup(personGroup: PersonGroup): Long {
    val _result = _repo.withRepoChangeMonitorAsync("PersonGroup") {
      _dao.insertPersonGroup(personGroup)
    }
    return _result
  }

  override suspend fun insertPersonGroupMember(personGroupMember: PersonGroupMember): Long {
    val _result = _repo.withRepoChangeMonitorAsync("PersonGroupMember") {
      _dao.insertPersonGroupMember(personGroupMember)
    }
    return _result
  }

  override fun findPersonsWithPermissionAsList(
    timestamp: Long,
    excludeClazz: Long,
    excludeSelected: List<Long>,
    accountPersonUid: Long,
    sortOrder: Int,
    searchText: String?,
  ): List<PersonAndListDisplayDetails> {
    val _result = _dao.findPersonsWithPermissionAsList(timestamp, excludeClazz, excludeSelected,
        accountPersonUid, sortOrder, searchText)
    return _result
  }

  override fun findPersonsWithPermissionAsPagingSource(
    timestamp: Long,
    excludeClazz: Long,
    excludeSelected: List<Long>,
    accountPersonUid: Long,
    sortOrder: Int,
    searchText: String?,
  ): PagingSource<Int, PersonAndListDisplayDetails> = DoorRepositoryReplicatePullPagingSource(
    repo = _repo,
    repoPath = "PersonDao/findPersonsWithPermissionAsPagingSource",
    dbPagingSource = _dao.findPersonsWithPermissionAsPagingSource(timestamp, excludeClazz,
        excludeSelected, accountPersonUid, sortOrder, searchText),
    onLoadHttp =  {
      _pagingParams -> 
      _repo.replicateHttpRequestOrThrow(repoPath =
          "PersonDao/findPersonsWithPermissionAsPagingSource") {
        val _response = _httpClient.`get` {
          setRepoUrl(_repo.config, "PersonDao/findPersonsWithPermissionAsPagingSource")
          doorNodeIdHeader(_repo)
          `header`("cache-control", "no-store")
          parameter("timestamp", _repo.config.json.encodeToString(Long.serializer(), timestamp))
          parameter("excludeClazz", _repo.config.json.encodeToString(Long.serializer(),
              excludeClazz))
          parameter("excludeSelected",
              _repo.config.json.encodeToString(ListSerializer(Long.serializer()), excludeSelected))
          parameter("accountPersonUid", _repo.config.json.encodeToString(Long.serializer(),
              accountPersonUid))
          parameter("sortOrder", _repo.config.json.encodeToString(Int.serializer(), sortOrder))
          parameter("searchText", _repo.config.json.encodeToString(String.serializer().nullable,
              searchText))
          pagingSourceLoadParameters(
            json = _repo.config.json, 
            keySerializer = Int.serializer().nullable,
            loadParams = _pagingParams
          )
        }

        _db.onClientRepoDoorMessageHttpResponse(_response, _repo.config.json)
        _response.endOfPaginationReached()
      }
    },
  )

  override fun findByUidWithDisplayDetailsLive(personUid: Long, activeUserPersonUid: Long):
      Flow<PersonAndDisplayDetail?> {
    val _result = _dao.findByUidWithDisplayDetailsLive(personUid, activeUserPersonUid)
    return _result
  }

  override fun findByUidWithDisplayDetailsFlow(personUid: Long, accountPersonUid: Long):
      Flow<PersonAndDisplayDetail?> = repoDaoFlowHelper.asRepoFlow(
    dbFlow = _dao.findByUidWithDisplayDetailsFlow(personUid, accountPersonUid),
    onMakeHttpRequest =  {
      _repo.replicateHttpRequestCatchAndLog(repoPath =
          "PersonDao/findByUidWithDisplayDetailsFlow") {
        val _response = _httpClient.`get` {
          setRepoUrl(_repo.config, "PersonDao/findByUidWithDisplayDetailsFlow")
          doorNodeIdHeader(_repo)
          `header`("cache-control", "no-store")
          parameter("personUid", _repo.config.json.encodeToString(Long.serializer(), personUid))
          parameter("accountPersonUid", _repo.config.json.encodeToString(Long.serializer(),
              accountPersonUid))
        }

        _db.onClientRepoDoorMessageHttpResponse(_response, _repo.config.json)
      }
    },
  )

  override fun getAllPerson(): List<Person> {
    val _result = _dao.getAllPerson()
    return _result
  }

  override fun getNamesByUid(uid: Long): Flow<PersonNames?> = repoDaoFlowHelper.asRepoFlow(
    dbFlow = _dao.getNamesByUid(uid),
    onMakeHttpRequest =  {
      _repo.replicateHttpRequestCatchAndLog(repoPath = "PersonDao/getNamesByUid") {
        val _response = _httpClient.`get` {
          setRepoUrl(_repo.config, "PersonDao/getNamesByUid")
          doorNodeIdHeader(_repo)
          `header`("cache-control", "no-store")
          parameter("uid", _repo.config.json.encodeToString(Long.serializer(), uid))
        }

        _db.onClientRepoDoorMessageHttpResponse(_response, _repo.config.json)
      }
    },
  )

  override suspend fun getNamesByUidAsync(uid: Long): PersonNames? {
    _repo.replicateHttpRequestCatchAndLog(repoPath = "PersonDao/getNamesByUidAsync") {
      val _response = _httpClient.`get` {
        setRepoUrl(_repo.config, "PersonDao/getNamesByUidAsync")
        doorNodeIdHeader(_repo)
        `header`("cache-control", "no-store")
        parameter("uid", _repo.config.json.encodeToString(Long.serializer(), uid))
      }

      _db.onClientRepoDoorMessageHttpResponse(_response, _repo.config.json)
    }
    val _result = _dao.getNamesByUidAsync(uid)
    return _result
  }

  override suspend fun getMinorByParentPersonUidAsync(uid: Long): List<Person>? {
    val _result = _dao.getMinorByParentPersonUidAsync(uid)
    return _result
  }

  override suspend fun updateUsername(
    personUid: Long,
    username: String,
    currentTime: Long,
  ): Int {
    val _result = _repo.withRepoChangeMonitorAsync("Person") {
      _dao.updateUsername(personUid, username, currentTime)
    }
    return _result
  }

  override suspend fun selectExistingUsernames(usernames: List<String>): List<String?> {
    val _result = _dao.selectExistingUsernames(usernames)
    return _result
  }

  public override fun insert(entity: Person): Long {
    val _result = _repo.withRepoChangeMonitor("Person") {
      _dao.insert(entity)
    }
    return _result
  }

  public override suspend fun insertAsync(entity: Person): Long {
    val _result = _repo.withRepoChangeMonitorAsync("Person") {
      _dao.insertAsync(entity)
    }
    return _result
  }

  public override fun insertList(entityList: List<Person>) {
    _repo.withRepoChangeMonitor("Person") {
      _dao.insertList(entityList)
    }
  }

  public override fun update(entity: Person) {
    _repo.withRepoChangeMonitor("Person") {
      _dao.update(entity)
    }
  }
}
