# KnowledgeViewModel.kt

    /*
     * The MIT License
     *
     * Original work sponsored and donated by The Danish Health Data Authority (http://www.sundhedsdatastyrelsen.dk)
     *
     * Copyright (C) 2021 The Danish Health Data Authority (http://www.sundhedsdatastyrelsen.dk)
     *
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy of
     * this software and associated documentation files (the "Software"), to deal in
     * the Software without restriction, including without limitation the rights to
     * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
     * of the Software, and to permit persons to whom the Software is furnished to do
     * so, subject to the following conditions:
     *
     *
     * The above copyright notice and this permission notice shall be included in all
     * copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     * SOFTWARE.
     */

    package dk.nsp.mingraviditet.ui.main.knowledge

    import androidx.hilt.Assisted
    import androidx.lifecycle.*
    import com.commonsense.android.kotlin.base.extensions.coroutines.launchIO
    import dagger.hilt.android.lifecycle.HiltViewModel
    import dk.nsp.domain.auth.AuthRepo
    import dk.nsp.domain.knowledge.Article
    import dk.nsp.domain.knowledge.ArticleCategory
    import dk.nsp.domain.knowledge.Checklist
    import dk.nsp.domain.repository.ArticlesRepository
    import dk.nsp.domain.repository.ChecklistRepository
    import dk.nsp.domain.repository.DataCardRepository
    import dk.nsp.mingraviditet.R
    import dk.nsp.mingraviditet.ui.adapterviews.HorizontalRecyclerSectionData
    import dk.nsp.mingraviditet.ui.adapterviews.HorizontalRecyclerSectionView
    import dk.nsp.mingraviditet.ui.adapterviews.IdentityIllustrationRow
    import dk.nsp.mingraviditet.ui.adapterviews.TextViewRow
    import dk.nsp.mingraviditet.ui.adapterviews.models.SectionType
    import dk.nsp.mingraviditet.ui.common.SectionHeaderAdapterView
    import dk.nsp.mingraviditet.ui.main.knowledge.article.KnowledgeFooterRow
    import dk.nsp.mingraviditet.ui.main.knowledge.search.SearchNoResultsView
    import dk.nsp.mingraviditet.ui.main.knowledge.viewbinding.ChecklistOverviewCardViewBindingModel
    import dk.nsp.mingraviditet.util.android.motionlayout.MotionLayoutStateEnum
    import dk.nsp.mingraviditet.util.android.recycler.RecyclerAdapterView
    import dk.nsp.mingraviditet.util.extensions.toSingleEvent
    import dk.nsp.mingraviditet.util.livedata.CombinedLiveData
    import dk.nsp.mingraviditet.util.livedata.SingleEvent
    import dk.nsp.models.ArticleSearchResult
    import dk.nsp.models.DataCard
    import dk.nsp.shared.Failed
    import dk.nsp.shared.Success
    import dk.nsp.util.TextModel
    import dk.nsp.util.hasSearchResults
    import dk.nsp.util.searchResults
    import javax.inject.Inject

    @HiltViewModel
    class KnowledgeViewModel @Inject constructor(
        private val authRepo: AuthRepo,
        private val articlesRepo: ArticlesRepository,
        private val checklistRepo: ChecklistRepository,
        private val dataCardRepository: DataCardRepository,
        @Assisted private val savedStateHandle: SavedStateHandle
    ) : ViewModel() {

        var toolbarState: MotionLayoutStateEnum = MotionLayoutStateEnum.Start

        private val _onChecklistClicked: MutableLiveData<SingleEvent<Checklist>> = MutableLiveData()
        val onChecklistClicked: LiveData<SingleEvent<Checklist>> = _onChecklistClicked

        private val _onArticleCategoryClicked: MutableLiveData<SingleEvent<ArticleCategory>> =
            MutableLiveData()
        val onArticleCategoryClicked: LiveData<SingleEvent<ArticleCategory>> = _onArticleCategoryClicked

        private val _searchResults: MutableLiveData<List<RecyclerAdapterView<*>>> = MutableLiveData()
        val searchResults: LiveData<List<RecyclerAdapterView<*>>> = _searchResults

        private val _categoriesData: MutableLiveData<List<RecyclerAdapterView<*>>> = MutableLiveData()

        private val _onError: MutableLiveData<SingleEvent<String>> = MutableLiveData()
        val onError: LiveData<SingleEvent<String>> = _onError

        private val _onSearchedArticleClicked: MutableLiveData<SingleEvent<Pair<Article, Int>>> =
            MutableLiveData()
        val onSearchArticleClicked: LiveData<SingleEvent<Pair<Article, Int>>> =
            _onSearchedArticleClicked

        val onDataCombined =
            CombinedLiveData(checklistRepo.checklists, _categoriesData) { checklists, categories ->
                val mList = mutableListOf<RecyclerAdapterView<*>>()

                if (checklists != null) {
                    mList.add(mapChecklistIntoSection(checklists))
                } else {
                    mList.add(createChecklistNudgeSection())
                }

                categories?.let {
                    mList.add(SectionHeaderAdapterView(TextModel.from(R.string.subjects_title)))
                    mList.addAll(it)
                    mList.add(KnowledgeFooterRow())
                }

                mList + IdentityIllustrationRow(SectionType.Knowledge)
            }

        private val dataCardObserver =
            Observer<DataCard?> { currentDataCard: DataCard? ->
                currentDataCard?.let {
                    getChecklists(it.episodeOfCareIdentifier)
                }
            }

        init {
            getArticleSections()

            dataCardRepository.currentDataCard.observeForever(dataCardObserver)
        }

        override fun onCleared() {
            super.onCleared()
            dataCardRepository.currentDataCard.removeObserver(dataCardObserver)
        }

        private fun getChecklists(episodeOfCareIdentifier: String) = viewModelScope.launchIO {
            if (!authRepo.haveLogin()) {
                // Shouldn't be shown checklist view
                checklistRepo.clearChecklists(this).await()
                return@launchIO
            }

            when (val checklistsRes =
                checklistRepo.getChecklists(this, episodeOfCareIdentifier).await()) {
                is Success -> {
                }
                is Failed -> {
                    _onError.postValue(
                        (checklistsRes.error.message ?: "Kunne ikke hente tjeklister").toSingleEvent()
                    )
                }
            }
        }

        private fun getArticleSections() = viewModelScope.launchIO {
            val categoriesDef = articlesRepo.getArticleSections(this)
            when (val categoriesRes = categoriesDef.await()) {
                is Success -> {
                    val cards = mapSectionsIntoCards(categoriesRes.value)

                    _categoriesData.postValue(cards)
                }
                is Failed -> {
                    _onError.postValue(
                        (categoriesRes.error.message ?: "Kunne ikke hente kategorier").toSingleEvent()
                    )
                }
            }
        }

        private fun mapSectionsIntoCards(categories: List<ArticleCategory>): List<CategoryCard> {
            return categories.map {
                CategoryCard(
                    CategoryCardData.WithIconUrl(
                        it.name,
                        it.description,
                        TextModel.Companion.from(
                            R.plurals.articles_count,
                            it.articles.size,
                            TextModel.from("${it.articles.size}")
                        ),
                        it.icon
                    ) {
                        _onArticleCategoryClicked.postValue(it.toSingleEvent())
                    })
            }
        }

        private fun mapChecklistIntoSection(checklists: List<Checklist>): HorizontalRecyclerSectionView {
            return if (!authRepo.isLoggedIn()) {
                createChecklistNudgeSection()
            } else {
                val checklistCards = checklists.map { checklist ->
                    ChecklistOverviewCard(ChecklistOverviewCardViewBindingModel(checklist) {
                        _onChecklistClicked.postValue(checklist.toSingleEvent())
                    })
                }

                HorizontalRecyclerSectionView(
                    HorizontalRecyclerSectionData(
                        TextModel.from(R.string.checklists_title),
                        checklistCards
                    )
                )
            }
        }

        private fun createChecklistNudgeSection() = HorizontalRecyclerSectionView(
            HorizontalRecyclerSectionData(
                TextModel.from(R.string.checklist_title),
                listOf(
                    TextViewRow(
                        TextModel.from(
                            if (!authRepo.isLoggedIn()) {
                                R.string.login_required_for_feature_text
                            } else {
                                R.string.checklists_requires_active_maternity
                            }, TextModel.from(R.string.checklists_title_lowercase)
                        )
                    )
                ),
                1F
            )
        )

        fun searchFor(searchTerm: String) {
            if (searchTerm.isBlank()) {
                // Post its own values, so we recreate the list
                val categoryData = _categoriesData.value
                _categoriesData.postValue(categoryData)
            } else if (searchTerm.length >= 3) {
                viewModelScope.launchIO {

                    when (val searchRes = articlesRepo.searchArticles(this, searchTerm).await()) {
                        is Success -> {
                            if (!searchRes.value.hasSearchResults()) {
                                _searchResults.postValue(listOf(SearchNoResultsView()))
                                return@launchIO
                            }

                            val rowsToShow = mutableListOf<RecyclerAdapterView<*>>()
                            searchRes.value.forEach { searchResultForCategory ->
                                rowsToShow.add(
                                    SectionHeaderAdapterView(
                                        TextModel.from(
                                            searchResultForCategory.name
                                        )
                                    )
                                )

                                val newRows = searchResultForCategory.searchResults().map {
                                    createCardForSearchResult(it)
                                }
                                rowsToShow.addAll(newRows)
                            }

                            _searchResults.postValue(rowsToShow)
                        }
                        is Failed -> {
                            _onError.postValue(
                                SingleEvent(
                                    searchRes.error.message ?: "Kunne ikke hente søgeresultater"
                                )
                            )
                        }
                    }
                }
            }
        }

        private fun createCardForSearchResult(articleSearchResult: ArticleSearchResult): CategoryCard {
            return CategoryCard(
                CategoryCardData.WithIconUrl(
                    articleSearchResult.title,
                    articleSearchResult.description,
                    TextModel.from(articleSearchResult.subtitle),
                    articleSearchResult.iconUrl
                ) {
                    _onSearchedArticleClicked.postValue(
                        Pair(
                            articleSearchResult.article,
                            articleSearchResult.articleSectionId ?: -1
                        ).toSingleEvent()
                    )
                })

        }
    }