package ru.maxi.mobile.feature.mainpage.business

import android.view.View
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.receiveAsFlow
import ru.maxi.mobile.common.features.business.FeatureProvider
import ru.maxi.mobile.common.features.core.FeatureType
import ru.maxi.mobile.feature.analytics.model.AnalyticType
import ru.maxi.mobile.feature.analytics.model.actions.AnalyticMainPageStoriesActions
import ru.maxi.mobile.feature.analytics.model.events.AnalyticsGeneralEvent
import ru.maxi.mobile.feature.analytics.usecase.AnalyticsUseCase
import ru.maxi.mobile.feature.mainpage.entity.stories.MainPageStory
import ru.maxi.mobile.feature.mainpage.entity.stories.MainPageStoryCard
import ru.maxi.mobile.feature.mainpage.entity.stories.MainPageStorySlide
import ru.maxi.mobile.feature.mainpage.presenter.MainPageStoriesEvent
import ru.maxi.mobile.feature.mainpage.presenter.MainPageStoriesRouter
import ru.maxi.mobile.feature.mainpage.repository.stories.local.LocalMainPageStoriesRepository
import ru.maxi.mobile.feature.mainpage.repository.stories.remote.RemoteMainPageStoriesRepository
import ru.maxi.mobile.feature.stories.model.StoriesPreviewFilter
import ru.maxi.mobile.feature.stories.model.Story
import ru.maxi.mobile.feature.stories.model.StoryId
import ru.maxi.mobile.feature.stories.model.StorySlide
import ru.maxi.mobile.feature.stories.model.StorySlideId
import ru.maxi.mobile.feature.stories.usecase.StoriesPreviewUseCase
import java.time.LocalDate
import javax.inject.Inject

/**
 * Интерактор для функционала Stories на главной странице
 */
class MainPageStoriesInteractor @Inject constructor(
    private val remoteRepository: RemoteMainPageStoriesRepository,
    private val localRepository: LocalMainPageStoriesRepository,
    private val featureProvider: FeatureProvider,
    private val analyticsUseCase: AnalyticsUseCase,
) : MainPageStoriesRouter, StoriesPreviewUseCase {

    override val analyticType = AnalyticType.MAIN_PAGE_STORIES

    /** Подписка на список Stories на главной странице */
    val storiesListFlow: Flow<List<MainPageStoryCard>> =
        localRepository.storiesListFlow.map { stories ->
            stories.filteredByDate().sorted()
        }

    /**
     * Обновить список Stories на главной странице
     */
    suspend fun updateStoriesList() {
        if (!featureProvider.isFeatureAvailableSuspend(FeatureType.MAIN_PAGE_STORIES)) {
            return localRepository.saveStoriesList(emptyList())
        }

        val stories = remoteRepository.getStoriesList()
        localRepository.saveStoriesList(stories)
    }

    private val _eventsFlow = Channel<MainPageStoriesEvent>(Channel.BUFFERED)

    override val eventsFlow: Flow<MainPageStoriesEvent>
        get() = _eventsFlow.receiveAsFlow()

    override fun onStoryCardClick(storyId: StoryId, cardView: View, hasIndication: Boolean) {
        val filter = if (hasIndication) StoriesPreviewFilter.NOT_VIEWED else StoriesPreviewFilter.ALL
        _eventsFlow.trySend(MainPageStoriesEvent.StoryCardClicked(storyId, cardView, filter))
        analyticsUseCase log AnalyticsGeneralEvent.SelectContent(
            analyticType,
            analyticAction = AnalyticMainPageStoriesActions.OPEN_STORY,
            itemId = storyId.value.toString(),
        )
    }

    override fun onShowStoryCards(storyIds: List<StoryId>) {
        if (storyIds.isEmpty()) return
        analyticsUseCase log AnalyticsGeneralEvent.ViewItem(
            analyticType,
            analyticAction = AnalyticMainPageStoriesActions.SHOW_STORIES_LIST,
            itemId = storyIds.joinToString { it.value.toString() },
        )
    }

    override suspend fun getStoryIds(filter: StoriesPreviewFilter): List<StoryId> {
        // if filter is StoriesList { return filter.storiesIds }
        return localRepository.getStoriesList()
            .filteredByViewed(filter)
            .filteredByDate()
            .sorted()
            .map(MainPageStoryCard::id)
    }

    override suspend fun getStory(storyId: StoryId): Story {
        check(featureProvider.isFeatureAvailable(FeatureType.MAIN_PAGE_STORIES))

        val story = remoteRepository.getStory(storyId)
        check(story.slides.isNotEmpty())

        val viewedSlideIds = localRepository.getStoryViewedSlideIds(storyId)
        return story.toStory(storyId, viewedSlideIds)
    }

    override suspend fun onStorySlideShown(storyId: StoryId, slideId: StorySlideId, isLastSlide: Boolean) {
        localRepository.markStorySlideAsViewed(storyId, slideId)
        if (isLastSlide) {
            localRepository.markStoryAsViewed(storyId)
        }
        analyticsUseCase log AnalyticsGeneralEvent.ViewItem(
            analyticType,
            analyticAction = AnalyticMainPageStoriesActions.SHOW_STORY_SLIDE,
            itemId = storyId.value.toString(),
            index = AnalyticsGeneralEvent.Index.IntIndex(slideId.value.toInt()),
        )
    }

    override suspend fun onSlideButtonClick(storyId: StoryId, slideId: StorySlideId) {
        analyticsUseCase log AnalyticsGeneralEvent.SelectContent(
            analyticType,
            analyticAction = AnalyticMainPageStoriesActions.CLICK_STORY_SLIDE_BUTTON,
            itemId = storyId.value.toString(),
            index = AnalyticsGeneralEvent.Index.IntIndex(slideId.value.toInt()),
        )
    }
}

private fun List<MainPageStoryCard>.filteredByViewed(filter: StoriesPreviewFilter): List<MainPageStoryCard> =
    when (filter) {
        StoriesPreviewFilter.ALL -> this
        StoriesPreviewFilter.NOT_VIEWED ->
            filter { story ->
                !story.isViewed
            }
        StoriesPreviewFilter.STORIES_LIST -> this
    }

private fun List<MainPageStoryCard>.filteredByDate(): List<MainPageStoryCard> {
    val currentDate = LocalDate.now()
    return filter { story ->
        story.dateEnd >= currentDate && story.dateStart <= currentDate
    }
}

private fun List<MainPageStoryCard>.sorted(): List<MainPageStoryCard> {
    val (fixedStories, notFixedStories) = partition(MainPageStoryCard::fixed)
    return listOf(
        fixedStories.partition { !it.isViewed }.sorted(),
        notFixedStories.partition { !it.isViewed }.sorted(),
    ).flatten()
}

private fun Pair<List<MainPageStoryCard>, List<MainPageStoryCard>>.sorted(): List<MainPageStoryCard> {
    return toList()
        .map { it.sortedByDescending(MainPageStoryCard::weight) }
        .flatten()
}

private fun MainPageStory.toStory(
    storyId: StoryId,
    viewedSlideIds: Set<StorySlideId>,
) = Story(
    id = storyId,
    slides = slides.map {
        it.toStorySlide(isViewed = viewedSlideIds.contains(it.id))
    },
    advertising = advertising,
)

private fun MainPageStorySlide.toStorySlide(isViewed: Boolean) = StorySlide(
    id = id,
    content = content,
    button = button,
    isViewed = isViewed,
)