package com.example.myapplication.api.bike

import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import androidx.room.withTransaction
import com.example.myapplication.api.MyServerService
import com.example.myapplication.api.item.toItem
import com.example.myapplication.database.AppDatabase
import com.example.myapplication.database.entities.model.Bike
import com.example.myapplication.database.entities.repository.OfflineBikeRepository
import com.example.myapplication.database.entities.repository.OfflineItemRepository
import com.example.myapplication.database.remotekeys.model.RemoteKeyType
import com.example.myapplication.database.remotekeys.model.RemoteKeys
import com.example.myapplication.database.remotekeys.repository.OfflineRemoteKeyRepository
import retrofit2.HttpException
import java.io.IOException

@OptIn(ExperimentalPagingApi::class)
class BikeRemoteMediator(
    private val service: MyServerService,
    private val dbBikeRepository: OfflineBikeRepository,
    private val dbItemRepository: OfflineItemRepository,
    private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
    private val database: AppDatabase
) : RemoteMediator<Int, Bike>() {

    override suspend fun initialize(): InitializeAction {
        return InitializeAction.LAUNCH_INITIAL_REFRESH
    }

    override suspend fun load(
        loadType: LoadType,
        state: PagingState<Int, Bike>
    ): MediatorResult {
        val page = when (loadType) {
            LoadType.REFRESH -> {
                val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
                remoteKeys?.nextKey?.minus(1) ?: 1
            }

            LoadType.PREPEND -> {
                val remoteKeys = getRemoteKeyForFirstItem(state)
                remoteKeys?.prevKey
                    ?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
            }

            LoadType.APPEND -> {
                val remoteKeys = getRemoteKeyForLastItem(state)
                remoteKeys?.nextKey
                    ?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
            }
        }

        try {
            val bikes = service.getBikes(page, state.config.pageSize).map { it.toBike() }
            val bikesWithItems = bikes.map { bike ->
                service.getItemsForBike(bike.uid).map {
                    service.getItem(it.id).toItem()
                }
            }
            val endOfPaginationReached = bikes.isEmpty()
            database.withTransaction {
                if (loadType == LoadType.REFRESH) {
                    dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.CINEMA)
                    dbItemRepository.clearItems()
                    dbBikeRepository.clearBikes()
                }
                val prevKey = if (page == 1) null else page - 1
                val nextKey = if (endOfPaginationReached) null else page + 1
                val keys = bikes.map {
                    RemoteKeys(
                        entityId = it.uid,
                        type = RemoteKeyType.CINEMA,
                        prevKey = prevKey,
                        nextKey = nextKey
                    )
                }
                dbRemoteKeyRepository.createRemoteKeys(keys)
                dbBikeRepository.insertBikes(bikes)
                bikesWithItems.forEach {
                    try {
                        dbItemRepository.insertItems(it)
                    } catch (_: Exception) {
                    }
                }
            }
            return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
        } catch (exception: IOException) {
            return MediatorResult.Error(exception)
        } catch (exception: HttpException) {
            return MediatorResult.Error(exception)
        }
    }

    private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, Bike>): RemoteKeys? {
        return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
            ?.let { bike ->
                dbRemoteKeyRepository.getAllRemoteKeys(bike.uid, RemoteKeyType.CINEMA)
            }
    }

    private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, Bike>): RemoteKeys? {
        return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
            ?.let { bike ->
                dbRemoteKeyRepository.getAllRemoteKeys(bike.uid, RemoteKeyType.CINEMA)
            }
    }

    private suspend fun getRemoteKeyClosestToCurrentPosition(
        state: PagingState<Int, Bike>
    ): RemoteKeys? {
        return state.anchorPosition?.let { position ->
            state.closestItemToPosition(position)?.uid?.let { bikeUid ->
                dbRemoteKeyRepository.getAllRemoteKeys(bikeUid, RemoteKeyType.CINEMA)
            }
        }
    }

}