package com.github.libretube.ui.fragments

import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.R
import com.github.libretube.api.obj.Playlists
import com.github.libretube.api.obj.StreamItem
import com.github.libretube.constants.PreferenceKeys.HOME_TAB_CONTENT
import com.github.libretube.databinding.FragmentHomeBinding
import com.github.libretube.db.obj.PlaylistBookmark
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.ui.activities.SettingsActivity
import com.github.libretube.ui.adapters.CarouselPlaylist
import com.github.libretube.ui.adapters.CarouselPlaylistAdapter
import com.github.libretube.ui.adapters.VideoCardsAdapter
import com.github.libretube.ui.models.HomeViewModel
import com.github.libretube.ui.models.SubscriptionsViewModel
import com.google.android.material.carousel.CarouselLayoutManager
import com.google.android.material.carousel.CarouselSnapHelper
import com.google.android.material.carousel.UncontainedCarouselStrategy
import com.google.android.material.snackbar.Snackbar
import kotlin.math.max

class HomeFragment : Fragment(R.layout.fragment_home) {
    private var _binding: FragmentHomeBinding? = null
    private val binding get() = _binding!!

    private val homeViewModel: HomeViewModel by activityViewModels()
    private val subscriptionsViewModel: SubscriptionsViewModel by activityViewModels()

    private val recommendationAdapter = VideoCardsAdapter()
    private val feedAdapter = VideoCardsAdapter()
    private val watchingAdapter = VideoCardsAdapter(columnWidthDp = 250f)
    private val bookmarkAdapter = CarouselPlaylistAdapter()
    private val playlistAdapter = CarouselPlaylistAdapter()
    private var loadRequestedForView = false

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        _binding = FragmentHomeBinding.bind(view)
        super.onViewCreated(view, savedInstanceState)

        setupLists()
        setupHomeFilterChips()
        setupActions()
        observeHome()

        val miniPlayerHeight = resources.getDimensionPixelSize(R.dimen.mini_player_height)
        binding.root.setPadding(
            binding.root.paddingLeft,
            binding.root.paddingTop,
            binding.root.paddingRight,
            max(binding.root.paddingBottom, miniPlayerHeight)
        )

        // The YouTube-style chips are the only home discovery selector. The old region/category
        // buttons could make two controls fight over the same list, so they stay hidden here.
        binding.trendingCategory.isGone = true
        binding.trendingRegion.isGone = true
        updateFilterPresentation(homeViewModel.selectedFilter.value ?: HomeViewModel.HomeFilter.ALL)

        // Start loading as soon as the view exists instead of waiting for onResume.
        if (homeViewModel.loadedSuccessfully.value != true) {
            loadRequestedForView = true
            fetchHomeFeed()
        }
    }

    private fun setupLists() {
        binding.bookmarksRV.layoutManager = CarouselLayoutManager(UncontainedCarouselStrategy())
        binding.playlistsRV.layoutManager = CarouselLayoutManager(UncontainedCarouselStrategy())
        CarouselSnapHelper().attachToRecyclerView(binding.bookmarksRV)
        CarouselSnapHelper().attachToRecyclerView(binding.playlistsRV)

        binding.trendingRV.adapter = recommendationAdapter
        binding.featuredRV.adapter = feedAdapter
        binding.bookmarksRV.adapter = bookmarkAdapter
        binding.playlistsRV.adapter = playlistAdapter
        binding.historyRV.adapter = watchingAdapter

        binding.playlistsRV.adapter?.registerAdapterDataObserver(object :
            RecyclerView.AdapterDataObserver() {
            override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
                super.onItemRangeRemoved(positionStart, itemCount)
                if (playlistAdapter.itemCount == 0) {
                    binding.playlistsRV.isGone = true
                    binding.playlistsTV.isGone = true
                }
            }
        })

        binding.historyRV.layoutManager =
            LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
        binding.historyRV.setHasFixedSize(true)
        binding.historyRV.isNestedScrollingEnabled = false

        // YouTube mobile uses a full-width vertical home feed; horizontal sections remain carousels.
        val spanCount = 1
        val recommendationLayoutManager = GridLayoutManager(requireContext(), spanCount)
        binding.trendingRV.layoutManager = recommendationLayoutManager
        // This list is wrap_content and starts empty. Its height changes when a tab loads,
        // so it must NOT be marked fixed-size or Android can leave it at height 0 until a relayout.
        binding.trendingRV.setHasFixedSize(false)
        binding.trendingRV.isNestedScrollingEnabled = false
        // The card layout already provides YouTube-like horizontal inset; avoid adding a second
        // grid gutter when the feed is a single column.
        if (spanCount > 1) {
            val spacingPx = resources.getDimensionPixelSize(R.dimen.grid_item_spacing)
            binding.trendingRV.addItemDecoration(
                com.github.libretube.ui.views.GridSpacingItemDecoration(spanCount, spacingPx, true)
            )
        }

        // The RecyclerView lives inside the page ScrollView, so pagination must listen to the
        // page scroll (the RecyclerView itself has nested scrolling disabled).
        binding.scroll.setOnScrollChangeListener { scrollView, _, scrollY, _, oldScrollY ->
            if (scrollY <= oldScrollY) return@setOnScrollChangeListener
            val content = binding.scroll.getChildAt(0) ?: return@setOnScrollChangeListener
            val remaining = content.bottom - (scrollView.height + scrollY)
            if (remaining <= resources.displayMetrics.density * 900f) {
                homeViewModel.loadMoreRecommendations()
            }
        }
    }

    private fun setupHomeFilterChips() {
        val current = homeViewModel.selectedFilter.value ?: HomeViewModel.HomeFilter.ALL
        binding.homeFilterGroup.check(chipIdFor(current))

        binding.homeFilterGroup.setOnCheckedStateChangeListener { _, checkedIds ->
            val checked = checkedIds.firstOrNull() ?: return@setOnCheckedStateChangeListener
            val filter = filterForChipId(checked)
            if (homeViewModel.selectedFilter.value == filter) return@setOnCheckedStateChangeListener

            homeViewModel.setFilter(filter)
            updateFilterPresentation(filter)
            binding.scroll.scrollTo(0, 0)
            fetchHomeFeed()
        }
    }

    private fun setupActions() {
        binding.featuredTV.setOnClickListener {
            findNavController().navigate(R.id.action_homeFragment_to_subscriptionsFragment)
        }
        binding.watchingTV.setOnClickListener {
            findNavController().navigate(R.id.action_homeFragment_to_watchHistoryFragment)
        }
        binding.trendingTV.setOnClickListener {
            if (homeViewModel.selectedFilter.value == HomeViewModel.HomeFilter.ALL) {
                findNavController().navigate(R.id.action_homeFragment_to_trendsFragment)
            }
        }
        binding.playlistsTV.setOnClickListener {
            findNavController().navigate(R.id.action_homeFragment_to_libraryFragment)
        }
        binding.bookmarksTV.setOnClickListener {
            findNavController().navigate(R.id.action_homeFragment_to_libraryFragment)
        }

        binding.refresh.setOnRefreshListener {
            binding.refresh.isRefreshing = true
            fetchHomeFeed(forceRefresh = true)
        }
        binding.refreshButton.setOnClickListener { fetchHomeFeed(forceRefresh = true) }
        binding.changeInstance.setOnClickListener { redirectToIntentSettings() }
    }

    private fun observeHome() = with(homeViewModel) {
        recommendations.observe(viewLifecycleOwner, ::showRecommendations)
        feed.observe(viewLifecycleOwner, ::showFeed)
        bookmarks.observe(viewLifecycleOwner, ::showBookmarks)
        playlists.observe(viewLifecycleOwner, ::showPlaylists)
        continueWatching.observe(viewLifecycleOwner, ::showContinueWatching)
        selectedFilter.observe(viewLifecycleOwner, ::updateFilterPresentation)
        isLoading.observe(viewLifecycleOwner, ::updateLoading)
    }

    override fun onResume() {
        super.onResume()

        val regionChanged = homeViewModel.loadedRegion?.let {
            it != PreferenceHelper.getTrendingRegion(requireContext())
        } == true

        if (!loadRequestedForView && (homeViewModel.loadedSuccessfully.value == false || regionChanged)) {
            fetchHomeFeed()
        }
        loadRequestedForView = false
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }

    private fun fetchHomeFeed(forceRefresh: Boolean = false) {
        binding.nothingHere.isGone = true
        val defaultItems = resources.getStringArray(R.array.homeTabItemsValues)
        val visibleItems = PreferenceHelper.getStringSet(HOME_TAB_CONTENT, defaultItems.toSet())

        homeViewModel.loadHomeFeed(
            context = requireContext(),
            subscriptionsViewModel = subscriptionsViewModel,
            visibleItems = visibleItems,
            forceRefresh = forceRefresh,
            onUnusualLoadTime = ::showChangeInstanceSnackBar
        )
    }

    private fun showRecommendations(streams: List<StreamItem>?) {
        if (streams == null) return
        recommendationAdapter.submitList(streams.toList()) {
            _binding?.let { currentBinding ->
                currentBinding.trendingRV.post {
                    currentBinding.trendingRV.requestLayout()
                    currentBinding.scroll.requestLayout()
                }
            }
        }
        val hasStreams = streams.isNotEmpty()
        binding.trendingRV.isVisible = hasStreams
        // YouTube Home places the feed directly below the topic chips; no extra category header.
        binding.trendingTV.isGone = true
        if (hasStreams) {
            // Make first Home/category result visible immediately. Do not depend on a later
            // navigation/resume pass to reveal the ScrollView.
            binding.nothingHere.isGone = true
            binding.scroll.isVisible = true
        }
    }

    private fun showFeed(streamItems: List<StreamItem>?) {
        if (streamItems == null) return
        feedAdapter.submitList(streamItems.toList()) {
            _binding?.let { currentBinding ->
                currentBinding.featuredRV.post {
                    currentBinding.featuredRV.requestLayout()
                    currentBinding.scroll.requestLayout()
                }
            }
        }
        val show = isAllFilter() && streamItems.isNotEmpty()
        binding.featuredRV.isVisible = show
        binding.featuredTV.isVisible = show
    }

    private fun showBookmarks(bookmarks: List<PlaylistBookmark>?) {
        if (bookmarks == null) return
        bookmarkAdapter.submitList(bookmarks.map { bookmark ->
            CarouselPlaylist(
                id = bookmark.playlistId,
                title = bookmark.playlistName,
                thumbnail = bookmark.thumbnailUrl
            )
        })
        val show = isAllFilter() && bookmarks.isNotEmpty()
        binding.bookmarksTV.isVisible = show
        binding.bookmarksRV.isVisible = show
    }

    private fun showPlaylists(playlists: List<Playlists>?) {
        if (playlists == null) return
        playlistAdapter.submitList(playlists.map { playlist ->
            CarouselPlaylist(
                id = playlist.id!!,
                thumbnail = playlist.thumbnail,
                title = playlist.name
            )
        })
        val show = isAllFilter() && playlists.isNotEmpty()
        binding.playlistsRV.isVisible = show
        binding.playlistsTV.isVisible = show
    }

    private fun showContinueWatching(unwatchedVideos: List<StreamItem>?) {
        if (unwatchedVideos == null) return
        watchingAdapter.submitList(unwatchedVideos)
        val show = isAllFilter() && unwatchedVideos.isNotEmpty()
        binding.historyRV.isVisible = show
        binding.watchingTV.isVisible = show
    }

    private fun updateFilterPresentation(filter: HomeViewModel.HomeFilter) {
        if (_binding == null) return

        val title = when (filter) {
            HomeViewModel.HomeFilter.ALL -> R.string.home_for_you
            HomeViewModel.HomeFilter.VIDEOS -> R.string.videos
            HomeViewModel.HomeFilter.MIXES -> R.string.home_filter_mixes
            HomeViewModel.HomeFilter.LIVE -> R.string.live
            HomeViewModel.HomeFilter.PODCASTS -> R.string.podcasts
        }
        val all = filter == HomeViewModel.HomeFilter.ALL
        (binding.trendingTV.getChildAt(0) as? TextView)?.apply {
            setText(title)
            // Only "Todos" opens the separate Trends screen; category rows are the content itself.
            setCompoundDrawablesRelativeWithIntrinsicBounds(
                0, 0, if (all) R.drawable.ic_arrow_right else 0, 0
            )
        }


        binding.featuredTV.isVisible = all && feedAdapter.itemCount > 0
        binding.featuredRV.isVisible = all && feedAdapter.itemCount > 0
        binding.watchingTV.isVisible = all && watchingAdapter.itemCount > 0
        binding.historyRV.isVisible = all && watchingAdapter.itemCount > 0
        binding.bookmarksTV.isVisible = all && bookmarkAdapter.itemCount > 0
        binding.bookmarksRV.isVisible = all && bookmarkAdapter.itemCount > 0
        binding.playlistsTV.isVisible = all && playlistAdapter.itemCount > 0
        binding.playlistsRV.isVisible = all && playlistAdapter.itemCount > 0
    }

    private fun isAllFilter() =
        homeViewModel.selectedFilter.value == HomeViewModel.HomeFilter.ALL

    private fun filterForChipId(id: Int): HomeViewModel.HomeFilter = when (id) {
        R.id.chip_videos -> HomeViewModel.HomeFilter.VIDEOS
        R.id.chip_mixes -> HomeViewModel.HomeFilter.MIXES
        R.id.chip_live -> HomeViewModel.HomeFilter.LIVE
        R.id.chip_podcasts -> HomeViewModel.HomeFilter.PODCASTS
        else -> HomeViewModel.HomeFilter.ALL
    }

    private fun chipIdFor(filter: HomeViewModel.HomeFilter): Int = when (filter) {
        HomeViewModel.HomeFilter.ALL -> R.id.chip_all
        HomeViewModel.HomeFilter.VIDEOS -> R.id.chip_videos
        HomeViewModel.HomeFilter.MIXES -> R.id.chip_mixes
        HomeViewModel.HomeFilter.LIVE -> R.id.chip_live
        HomeViewModel.HomeFilter.PODCASTS -> R.id.chip_podcasts
    }

    private fun updateLoading(isLoading: Boolean) {
        if (isLoading) showLoading() else hideLoading()
    }

    private fun showLoading() {
        binding.progress.isVisible = !binding.refresh.isRefreshing
        binding.nothingHere.isVisible = false
        binding.scroll.alpha = 0.3f
    }

    private fun hideLoading() {
        binding.progress.isVisible = false
        binding.refresh.isRefreshing = false

        val hasContent = homeViewModel.loadedSuccessfully.value == true
        if (hasContent) showContent() else showNothingHere()
        binding.scroll.alpha = 1.0f
    }

    private fun showNothingHere() {
        binding.nothingHere.isVisible = true
        binding.scroll.isVisible = false
    }

    private fun showContent() {
        val currentBinding = _binding ?: return
        currentBinding.nothingHere.isVisible = false
        currentBinding.scroll.isVisible = true
        currentBinding.scroll.post {
            currentBinding.trendingRV.requestLayout()
            currentBinding.scroll.requestLayout()
        }
    }

    private fun showChangeInstanceSnackBar() {
        val root = _binding?.root ?: return
        Snackbar.make(root, R.string.suggest_change_instance, Snackbar.LENGTH_LONG)
            .apply {
                setAction(R.string.change) { redirectToIntentSettings() }
                show()
            }
    }

    private fun redirectToIntentSettings() {
        val settingsIntent = Intent(context, SettingsActivity::class.java).apply {
            putExtra(SettingsActivity.REDIRECT_KEY, SettingsActivity.REDIRECT_TO_INTENT_SETTINGS)
        }
        startActivity(settingsIntent)
    }
}
