package com.tangem.features.feed.model.news.list

import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.ui.DesignFeatureToggles
import com.tangem.core.ui.components.chip.entity.ChipUM
import com.tangem.core.ui.event.consumedEvent
import com.tangem.core.ui.event.triggeredEvent
import com.tangem.domain.news.model.NewsListConfig
import com.tangem.domain.news.usecase.GetNewsCategoriesUseCase
import com.tangem.domain.news.usecase.GetNewsListBatchFlowUseCase
import com.tangem.features.feed.components.news.list.DefaultNewsListComponent
import com.tangem.features.feed.model.news.list.analytics.NewsListAnalyticsEvent
import com.tangem.features.feed.model.news.list.loader.NewsCategoriesLoader
import com.tangem.features.feed.model.news.list.statemanager.NewsListBatchFlowManager
import com.tangem.features.feed.model.news.list.statemanager.NewsListStateManager
import com.tangem.features.feed.ui.news.list.state.NewsListState
import com.tangem.features.feed.ui.news.list.state.NewsListUM
import com.tangem.utils.Provider
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject

@ModelScoped
internal class NewsListModel @Inject constructor(
    override val dispatchers: CoroutineDispatcherProvider,
    private val getNewsCategoriesUseCase: GetNewsCategoriesUseCase,
    private val getNewsListBatchFlowUseCase: GetNewsListBatchFlowUseCase,
    private val analyticsEventHandler: AnalyticsEventHandler,
    private val designFeatureToggles: DesignFeatureToggles,
    paramsContainer: ParamsContainer,
) : Model() {

    private val params = paramsContainer.require<DefaultNewsListComponent.Params>()
    private val selectedCategoryId = MutableStateFlow(params.preselectedCategoryId)

    private val categoriesLoader by lazy {
        NewsCategoriesLoader(
            getNewsCategoriesUseCase = getNewsCategoriesUseCase,
            defaultAllNewsCategoryId = DEFAULT_ALL_NEWS_CATEGORIES_ID,
            onCategoryClick = ::onCategoryClick,
        )
    }

    private val batchFlowManager by lazy {
        NewsListBatchFlowManager(
            getNewsListBatchFlowUseCase = getNewsListBatchFlowUseCase,
            currentCategoryIds = Provider {
                selectedCategoryId.value?.takeIf { it > 0 }?.let { listOf(it) }.orEmpty()
            },
            modelScope = modelScope,
            dispatchers = dispatchers,
            isRedesignEnabled = designFeatureToggles.isRedesignEnabled,
        )
    }

    private val newsListStateManager by lazy(LazyThreadSafetyMode.NONE) {
        NewsListStateManager(analyticsEventHandler)
    }

    private val _state = MutableStateFlow(
        NewsListUM(
            selectedCategoryId = params.preselectedCategoryId ?: DEFAULT_ALL_NEWS_CATEGORIES_ID,
            filters = persistentListOf(),
            newsListState = NewsListState.Loading,
            listOfArticles = persistentListOf(),
            onArticleClick = { articleId ->
                params.onArticleClicked(
                    /* currentArticle */ articleId,
                    /* prefetchedArticles */ getCurrentFetchedArticlesIds(),
                    /* paginationConfig */ createNewsListConfig(),
                )
            },
            onBackClick = params.onBackClick,
        ),
    )
    val state = _state.asStateFlow()

    init {
        observeNewsList()
        batchFlowManager.reload()
        loadCategories()
    }

    private fun loadCategories() {
        modelScope.launch(dispatchers.default) {
            val filterChips = categoriesLoader.load()
            val validIds = filterChips.mapTo(mutableSetOf()) { it.id }
            selectedCategoryId.value = selectedCategoryId.value?.takeIf { it in validIds }
            val effectiveId = selectedCategoryId.value ?: DEFAULT_ALL_NEWS_CATEGORIES_ID
            val scrollIndex = filterChips.indexOfFirst { it.id == effectiveId }.takeIf { it > 0 }
            _state.update { currentState ->
                currentState.copy(
                    selectedCategoryId = effectiveId,
                    filters = filterChips.map { chip ->
                        chip.copy(isSelected = chip.id == effectiveId)
                    }.toImmutableList(),
                    scrollToCategoryEvent = if (scrollIndex != null) {
                        triggeredEvent(data = scrollIndex, onConsume = ::onScrollToCategoryConsumed)
                    } else {
                        currentState.scrollToCategoryEvent
                    },
                )
            }
            batchFlowManager.reload()
        }
    }

    private fun onScrollToCategoryConsumed() {
        _state.update { it.copy(scrollToCategoryEvent = consumedEvent()) }
    }

    private fun observeNewsList() {
        modelScope.launch(dispatchers.default) {
            combine(
                batchFlowManager.uiItems,
                batchFlowManager.initialLoadingError,
                batchFlowManager.paginationStatus,
            ) { articles, loadingError, paginationStatus ->
                newsListStateManager.calculateState(
                    articles = articles,
                    error = loadingError,
                    paginationStatus = paginationStatus,
                    onRetryClick = {
                        loadCategories()
                    },
                    onLoadMore = { batchFlowManager.loadMore() },
                )
            }.collect { (listState, articles) ->
                _state.update { currentState ->
                    currentState.copy(
                        listOfArticles = articles,
                        newsListState = listState,
                    )
                }
            }
        }
    }

    private fun onCategoryClick(categoryId: Int) {
        if (state.value.selectedCategoryId == categoryId) {
            return
        }
        if (categoryId != DEFAULT_ALL_NEWS_CATEGORIES_ID) {
            analyticsEventHandler.send(NewsListAnalyticsEvent.NewsCategoriesClick(categoryId))
        }
        selectedCategoryId.value = categoryId
        _state.update { currentState ->
            currentState.copy(
                selectedCategoryId = categoryId,
                filters = updateFilterChips(categoryId),
            )
        }
        batchFlowManager.reload()
    }

    private fun updateFilterChips(categoryId: Int?): ImmutableList<ChipUM> {
        return state.value.filters.map { chip ->
            chip.copy(isSelected = chip.id == categoryId)
        }.toImmutableList()
    }

    private fun getCurrentFetchedArticlesIds(): List<Int> {
        return state.value.listOfArticles.map { it.id }
    }

    private fun createNewsListConfig(): NewsListConfig {
        return NewsListConfig(
            snapshot = null,
            tokenIds = emptyList(),
            categoryIds = selectedCategoryId.value?.takeIf { it > 0 }?.let { listOf(it) }.orEmpty(),
        )
    }

    companion object {
        private const val DEFAULT_ALL_NEWS_CATEGORIES_ID = -1
    }
}