/**
 * Teapod
 *
 * Copyright 2020-2022  <seil0@mosad.xyz>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 *
 */

package org.mosad.teapod.ui.activity.main.viewmodel

import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.mosad.teapod.parser.crunchyroll.*
import kotlin.random.Random

class HomeViewModel : ViewModel()  {

    private val WATCHLIST_LENGTH = 50

    private val uiState = MutableStateFlow<UiState>(UiState.Loading)

    sealed class UiState {
        object Loading : UiState()
        data class Normal(
            val upNextItems: List<UpNextAccountItem>,
            val watchlistItems: List<Item>,
            val recommendationsItems: List<Item>,
            val recentlyAddedItems: List<Item>,
            val topTenItems: List<Item>,
            val highlightItem: Item,
            val highlightItemUpNext: UpNextSeriesItem,
            val highlightIsWatchlist:Boolean
        ) : UiState()
        data class Error(val message: String?) : UiState()
    }

    init {
        load()
    }

    fun onUiState(scope: LifecycleCoroutineScope, collector: (UiState) -> Unit) {
        scope.launch { uiState.collect { collector(it) } }
    }

    fun load() {
        viewModelScope.launch {
            uiState.emit(UiState.Loading)
            try {
                // run the loading in parallel to speed up the process
                val upNextJob = viewModelScope.async { Crunchyroll.upNextAccount(n = 20).data }
                val watchlistJob = viewModelScope.async { Crunchyroll.watchlist(WATCHLIST_LENGTH).data }
                val recommendationsJob = viewModelScope.async {
                    Crunchyroll.recommendations(n = 20).data
                }
                val recentlyAddedJob = viewModelScope.async {
                    Crunchyroll.browse(sortBy = SortBy.NEWLY_ADDED, n = 50).data
                }
                val topTenJob = viewModelScope.async {
                    Crunchyroll.browse(sortBy = SortBy.POPULARITY, n = 10).data
                }

                val recentlyAddedItems = recentlyAddedJob.await()
                // FIXME crashes on newTitles.items.size == 0
                val highlightItem = recentlyAddedItems[Random.nextInt(recentlyAddedItems.size)]
                val highlightItemUpNextJob = viewModelScope.async {
                    Crunchyroll.upNextSeries(highlightItem.id).data.first()
                }
                val highlightItemIsWatchlistJob = viewModelScope.async {
                    Crunchyroll.isWatchlist(highlightItem.id)
                }

                uiState.emit(UiState.Normal(
                    upNextJob.await(), watchlistJob.await(), recommendationsJob.await(),
                    recentlyAddedJob.await(), topTenJob.await(), highlightItem,
                    highlightItemUpNextJob.await(), highlightItemIsWatchlistJob.await()
                ))
            } catch (e: Exception) {
                uiState.emit(UiState.Error(e.message))
            }
        }

    }

    /**
     * Toggle the watchlist state of the highlight media.
     */
    fun toggleHighlightWatchlist() {
        viewModelScope.launch {
            uiState.update { currentUiState ->
                if (currentUiState is UiState.Normal) {
                    if (currentUiState.highlightIsWatchlist) {
                        Crunchyroll.deleteWatchlist(currentUiState.highlightItem.id)
                    } else {
                        Crunchyroll.postWatchlist(currentUiState.highlightItem.id)
                    }

                    // update the watchlist after a item has been added/removed
                    val watchlistItems = Crunchyroll.watchlist(WATCHLIST_LENGTH).data

                    currentUiState.copy(
                        watchlistItems = watchlistItems,
                        highlightIsWatchlist = !currentUiState.highlightIsWatchlist)
                } else {
                    currentUiState
                }
            }
        }
    }

    /**
     * Update the up next list. To be used on player result callbacks.
     */
    fun updateUpNextItems() {
        viewModelScope.launch {
            uiState.update { currentUiState ->
                if (currentUiState is UiState.Normal) {
                    val upNextItems = Crunchyroll.upNextAccount(n = 20).data
                    currentUiState.copy(upNextItems = upNextItems)
                } else {
                    currentUiState
                }
            }
        }
    }

}
