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.repoHttpRequestWithFallback
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.ClazzAndDetailPermissions
import com.ustadmobile.lib.db.composites.ClazzAndDisplayDetails
import com.ustadmobile.lib.db.composites.ClazzNameAndTerminology
import com.ustadmobile.lib.db.entities.Clazz
import com.ustadmobile.lib.db.entities.ClazzWithHolidayCalendarAndAndTerminology
import com.ustadmobile.lib.db.entities.ClazzWithListDisplayDetails
import com.ustadmobile.lib.db.entities.UidAndLabel
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.Boolean
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 ClazzDao_Repo(
  public val _db: RoomDatabase,
  public val _repo: DoorDatabaseRepository,
  public val _dao: ClazzDao,
  public val _httpClient: HttpClient,
  public val _clientId: Long,
  public val _endpoint: String,
) : ClazzDao(), RepositoryDaoWithFlowHelper {
  override val repoDaoFlowHelper: RepoDaoFlowHelper = RepoDaoFlowHelper(_repo)


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

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

  override suspend fun findByClazzCode(code: String): Clazz? = _repo.repoHttpRequestWithFallback(
    repoPath = "ClazzDao/findByClazzCode",
    http =  {
      _repo.config.json.decodeFromString(
        deserializer = Clazz.serializer().nullable,
        string = _repo.config.httpClient.`get` {
          setRepoUrl(_repo.config, "ClazzDao/findByClazzCode")
          doorNodeIdHeader(_repo)
          `header`("cache-control", "no-store")
          parameter("code", _repo.config.json.encodeToString(String.serializer(), code))
        }
        .bodyAsText()
      )
    } ,
    fallback =  {
      _dao.findByClazzCode(code)
    }

  )

  override suspend fun findByClazzCodeFromWeb(code: String): Clazz? {
    val _result = _dao.findByClazzCodeFromWeb(code)
    return _result
  }

  override fun findAllLive(): Flow<List<Clazz>> {
    val _result = _dao.findAllLive()
    return _result
  }

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

  override suspend fun findByUidAsync(clazzUid: Long): Clazz? {
    val _result = _dao.findByUidAsync(clazzUid)
    return _result
  }

  override suspend fun clazzUidExistsAsync(clazzUid: Long): Boolean {
    _repo.replicateHttpRequestCatchAndLog(repoPath = "ClazzDao/clazzUidExistsAsync") {
      val _response = _httpClient.`get` {
        setRepoUrl(_repo.config, "ClazzDao/clazzUidExistsAsync")
        doorNodeIdHeader(_repo)
        `header`("cache-control", "no-store")
        parameter("clazzUid", _repo.config.json.encodeToString(Long.serializer(), clazzUid))
      }

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

  override fun findByUidAsFlow(uid: Long): Flow<Clazz?> = repoDaoFlowHelper.asRepoFlow(
    dbFlow = _dao.findByUidAsFlow(uid),
    onMakeHttpRequest =  {
      _repo.replicateHttpRequestCatchAndLog(repoPath = "ClazzDao/findByUidAsFlow") {
        val _response = _httpClient.`get` {
          setRepoUrl(_repo.config, "ClazzDao/findByUidAsFlow")
          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 findByUidWithHolidayCalendarAsync(uid: Long):
      ClazzWithHolidayCalendarAndAndTerminology? {
    _repo.replicateHttpRequestCatchAndLog(repoPath = "ClazzDao/findByUidWithHolidayCalendarAsync") {
      val _response = _httpClient.`get` {
        setRepoUrl(_repo.config, "ClazzDao/findByUidWithHolidayCalendarAsync")
        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.findByUidWithHolidayCalendarAsync(uid)
    return _result
  }

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

  override fun findClazzesWithPermission(
    searchQuery: String,
    accountPersonUid: Long,
    excludeSelectedClazzList: List<Long>,
    sortOrder: Int,
    filter: Int,
    currentTime: Long,
    permission: Long,
  ): PagingSource<Int, ClazzWithListDisplayDetails> = DoorRepositoryReplicatePullPagingSource(
    repo = _repo,
    repoPath = "ClazzDao/findClazzesWithPermission",
    dbPagingSource = _dao.findClazzesWithPermission(searchQuery, accountPersonUid,
        excludeSelectedClazzList, sortOrder, filter, currentTime, permission),
    onLoadHttp =  {
      _pagingParams -> 
      _repo.replicateHttpRequestOrThrow(repoPath = "ClazzDao/findClazzesWithPermission") {
        val _response = _httpClient.`get` {
          setRepoUrl(_repo.config, "ClazzDao/findClazzesWithPermission")
          doorNodeIdHeader(_repo)
          `header`("cache-control", "no-store")
          parameter("searchQuery", _repo.config.json.encodeToString(String.serializer(),
              searchQuery))
          parameter("accountPersonUid", _repo.config.json.encodeToString(Long.serializer(),
              accountPersonUid))
          parameter("excludeSelectedClazzList",
              _repo.config.json.encodeToString(ListSerializer(Long.serializer()),
              excludeSelectedClazzList))
          parameter("sortOrder", _repo.config.json.encodeToString(Int.serializer(), sortOrder))
          parameter("filter", _repo.config.json.encodeToString(Int.serializer(), filter))
          parameter("currentTime", _repo.config.json.encodeToString(Long.serializer(), currentTime))
          parameter("permission", _repo.config.json.encodeToString(Long.serializer(), permission))
          pagingSourceLoadParameters(
            json = _repo.config.json, 
            keySerializer = Int.serializer().nullable,
            loadParams = _pagingParams
          )
        }

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

  override suspend fun getClassNamesFromListOfIds(ids: List<Long>): List<UidAndLabel> {
    val _result = _dao.getClassNamesFromListOfIds(ids)
    return _result
  }

  override fun findByClazzName(name: String): List<Clazz> {
    val _result = _dao.findByClazzName(name)
    return _result
  }

  override suspend fun updateClazzAttendanceAverageAsync(clazzUid: Long, timeChanged: Long) {
    _repo.withRepoChangeMonitorAsync("Clazz") {
      _dao.updateClazzAttendanceAverageAsync(clazzUid, timeChanged)
    }
  }

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

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

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

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

  override fun findClazzesWithEffectiveHolidayCalendarAndFilter(filterUid: Long):
      List<ClazzWithHolidayCalendarAndAndTerminology> {
    val _result = _dao.findClazzesWithEffectiveHolidayCalendarAndFilter(filterUid)
    return _result
  }

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

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

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

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

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

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

  override suspend fun getClazzTimeZoneByClazzUidAsync(clazzUid: Long): String? =
      _repo.repoHttpRequestWithFallback(
    repoPath = "ClazzDao/getClazzTimeZoneByClazzUidAsync",
    http =  {
      _repo.config.json.decodeFromString(
        deserializer = String.serializer().nullable,
        string = _repo.config.httpClient.`get` {
          setRepoUrl(_repo.config, "ClazzDao/getClazzTimeZoneByClazzUidAsync")
          doorNodeIdHeader(_repo)
          `header`("cache-control", "no-store")
          parameter("clazzUid", _repo.config.json.encodeToString(Long.serializer(), clazzUid))
        }
        .bodyAsText()
      )
    } ,
    fallback =  {
      _dao.getClazzTimeZoneByClazzUidAsync(clazzUid)
    }

  )

  override suspend fun getCoursesByName(names: List<String>): List<Clazz> {
    _repo.replicateHttpRequestCatchAndLog(repoPath = "ClazzDao/getCoursesByName") {
      val _response = _httpClient.`get` {
        setRepoUrl(_repo.config, "ClazzDao/getCoursesByName")
        doorNodeIdHeader(_repo)
        `header`("cache-control", "no-store")
        parameter("names", _repo.config.json.encodeToString(ListSerializer(String.serializer()),
            names))
      }

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

  override suspend fun findOneRosterUserClazzes(accountPersonUid: Long,
      filterByEnrolledMemberPersonUid: Long): List<Clazz> {
    _repo.replicateHttpRequestCatchAndLog(repoPath = "ClazzDao/findOneRosterUserClazzes") {
      val _response = _httpClient.`get` {
        setRepoUrl(_repo.config, "ClazzDao/findOneRosterUserClazzes")
        doorNodeIdHeader(_repo)
        `header`("cache-control", "no-store")
        parameter("accountPersonUid", _repo.config.json.encodeToString(Long.serializer(),
            accountPersonUid))
        parameter("filterByEnrolledMemberPersonUid",
            _repo.config.json.encodeToString(Long.serializer(), filterByEnrolledMemberPersonUid))
      }

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

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

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

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

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