package com.gh.gamecenter.home

import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.alibaba.android.arouter.launcher.ARouter
import com.gh.common.databind.BindingAdapters
import com.gh.common.exposure.ExposureListener
import com.gh.common.util.DialogUtils
import com.gh.common.xapk.XapkInstaller
import com.gh.common.xapk.XapkUnzipStatus
import com.gh.download.DownloadManager
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.LazyFragment
import com.gh.gamecenter.common.baselist.LoadStatus
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.eventbus.EBReuse
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.common.utils.observeNonNull
import com.gh.gamecenter.common.utils.viewModelProvider
import com.gh.gamecenter.common.utils.viewModelProviderFromParent
import com.gh.gamecenter.common.view.OffsetLinearLayoutManager
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.provider.IFloatingWindowProvider
import com.gh.gamecenter.core.provider.IStartUpAdProvider
import com.gh.gamecenter.core.utils.MD5Utils
import com.gh.gamecenter.core.utils.MtaHelper
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.databinding.FragmentMainHomeBinding
import com.gh.gamecenter.eventbus.EBDiscoverChanged
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.eventbus.EBPackage
import com.gh.gamecenter.eventbus.EBUISwitch
import com.gh.gamecenter.fragment.HomeSearchToolWrapperFragment
import com.gh.gamecenter.fragment.HomeSearchToolWrapperViewModel
import com.gh.gamecenter.fragment.MainWrapperFragment
import com.gh.gamecenter.game.gallery.GameGallerySlideViewHolder
import com.gh.gamecenter.home.slide.HomeSlideListAdapter
import com.gh.gamecenter.home.test_v2.HomeGameTestV2ViewModel
import com.gh.gamecenter.home.test_v2.HomeItemGameTestV2ViewHolder
import com.gh.gamecenter.home.test_v2.HomeItemGameTestV2ViewHolderWatcher
import com.gh.gamecenter.home.test_v2.addViewHolderWatcher
import com.gh.gamecenter.home.video.ScrollCalculatorHelper
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadEntity
import io.reactivex.disposables.CompositeDisposable
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode

class HomeFragment : LazyFragment() {

    private lateinit var mViewModel: HomeViewModel
    private lateinit var mHomeGameTestV2ViewModel: HomeGameTestV2ViewModel
    private lateinit var mHomeSearchViewModel: HomeSearchToolWrapperViewModel
    private lateinit var mBinding: FragmentMainHomeBinding
    private lateinit var mLayoutManager: LinearLayoutManager
    private lateinit var mListAdapter: HomeFragmentAdapter

    private lateinit var mExposureListener: ExposureListener
    private lateinit var mScrollCalculatorHelper: ScrollCalculatorHelper
    private lateinit var mAutomaticLayoutManager: OffsetLinearLayoutManager

    private var mCompositeDisposable = CompositeDisposable()

    private val dataWatcher = object : DataWatcher() {
        override fun onDataChanged(downloadEntity: DownloadEntity) {
            mListAdapter.notifyItemByDownload(downloadEntity)

            if (downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] == XapkUnzipStatus.FAILURE.name) {
                showUnzipFailureDialog(downloadEntity)
            }
        }

        override fun onDataInit(downloadEntity: DownloadEntity) {
            mListAdapter.notifyItemByDownload(downloadEntity)
        }
    }

    override fun getRealLayoutId() = R.layout.fragment_main_home

    override fun onFragmentFirstVisible() {
        mViewModel = viewModelProvider()
        mHomeGameTestV2ViewModel = viewModelProvider()
        mViewModel.setHomeGameTestV2DownloadStateUpdateHelper(mHomeGameTestV2ViewModel)
        mHomeSearchViewModel = viewModelProviderFromParent()
        mViewModel.homeOnlyWithoutOtherTab = arguments?.getInt(EntranceConsts.KEY_TAB_COUNT) == 1
        super.onFragmentFirstVisible()

        mViewModel.itemDataList.observeNonNull(this, callback = {
            mListAdapter.submitList(it)
        })
        mViewModel.loadStatus.observeNonNull(this, callback = {
            val loadStatus = it
            mBinding.run {
                BindingAdapters.isRefreshing(gameRefresh, LoadStatus.LIST_LOADED)
                gameRefresh.goneIf(loadStatus == LoadStatus.INIT_FAILED)
                gameList.goneIf(loadStatus == LoadStatus.INIT_LOADING)
                reuseNoConnection.root.goneIf(loadStatus != LoadStatus.INIT_FAILED)
                reuseLoading.root.goneIf(loadStatus != LoadStatus.INIT_LOADING)
            }
            mListAdapter.setLoadStatus(it)
            val startUpAdProvider =
                ARouter.getInstance().build(RouteConsts.provider.adSdk).navigation() as? IStartUpAdProvider
            val startAdContainerView =
                requireActivity().findViewById<View>(if (startUpAdProvider != null) R.id.sdkStartAdContainer else R.id.startAdContainer)
            if (it == LoadStatus.INIT_LOADED && startAdContainerView == null) {
                AppExecutor.uiExecutor.executeWithDelay({
                    scroll()
                    mScrollCalculatorHelper.onScrollStateChanged(RecyclerView.SCROLL_STATE_IDLE)
                }, 100)
                if (parentFragment is HomeSearchToolWrapperFragment) {
                    (parentFragment as HomeSearchToolWrapperFragment).finishRefresh()
                }
            }
        })
        mHomeSearchViewModel.homeDataLiveData.observe(this) {
            mViewModel.initData(it)
            mBinding.gameRefresh.isEnabled = it.homePush == null
        }
        listenStartAdViewRemoved()
    }

    override fun onRealLayoutInflated(inflatedView: View) {
        super.onRealLayoutInflated(inflatedView)

        mBinding = FragmentMainHomeBinding.bind(inflatedView)
        mScrollCalculatorHelper = ScrollCalculatorHelper(mBinding.gameList, R.id.autoVideoView, 0)
        mBinding.gameRefresh.setColorSchemeColors(
            ContextCompat.getColor(
                requireContext(),
                R.color.theme
            )
        )
        mBinding.run {
            BindingAdapters.isRefreshing(gameRefresh, LoadStatus.LIST_LOADED)
        }
        mAutomaticLayoutManager = OffsetLinearLayoutManager(requireContext())
        mLayoutManager = mAutomaticLayoutManager
        mListAdapter = HomeFragmentAdapter(
            requireContext(),
            this,
            mViewModel,
            mHomeGameTestV2ViewModel,
            mLayoutManager,
            mScrollCalculatorHelper
        ) {
            if (parentFragment is HomeSearchToolWrapperFragment) {
                (parentFragment as HomeSearchToolWrapperFragment).changeAppBarColor(it)
            }
        }
        mExposureListener = ExposureListener(this, mListAdapter)
        (mBinding.gameList.itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false
        mBinding.gameList.layoutManager = mLayoutManager
        mBinding.gameList.adapter = mListAdapter
        //监听新游开测条目可见性变化
        mBinding.gameList.addViewHolderWatcher(
            HomeItemGameTestV2ViewHolder::class.java,
            HomeItemGameTestV2ViewHolderWatcher()
        )
        mBinding.gameList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                super.onScrollStateChanged(recyclerView, newState)
                mScrollCalculatorHelper.onScrollStateChanged(newState)
                if (mLayoutManager.findLastVisibleItemPosition() == mListAdapter.itemCount - 1
                    && RecyclerView.SCROLL_STATE_IDLE == newState
                ) mViewModel.getHomeContent(false)
            }

            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
                if (parentFragment is HomeSearchToolWrapperFragment) {
                    val bannerViewHeight = mAutomaticLayoutManager.heightMap[0] ?: 0
                    val type = (parentFragment as HomeSearchToolWrapperFragment).getCurrentTabEntity()?.type
                    if (type == "home") {
                        (parentFragment as HomeSearchToolWrapperFragment).onScrollChanged(
                            bannerViewHeight,
                            mBinding.gameList.computeVerticalScrollOffset()
                        )
                    }
                }
                scroll()
            }
        })
        mBinding.gameList.addOnScrollListener(mExposureListener)

        mBinding.gameRefresh.setOnRefreshListener {
            onRefresh()
        }

        mBinding.reuseNoConnection.root.setOnClickListener {
            mViewModel.loadStatus.postValue(LoadStatus.INIT_LOADING)
            mHomeSearchViewModel.getHomeContentUnion(true)
        }
    }

    fun setScrollEnabled(isScrollEnabled: Boolean) {
        if (::mAutomaticLayoutManager.isInitialized) {
            mAutomaticLayoutManager.isScrollEnabled = isScrollEnabled
        }
    }

    fun onRefresh() {
        MtaHelper.onEvent("首页_新", "刷新")
        mViewModel.updateHomeSlideCardsPosition()
        mViewModel.loadStatus.postValue(LoadStatus.LIST_LOADING)
        mHomeSearchViewModel.getHomeContentUnion(true)
        mViewModel.refreshDiscoverCardData()
    }

    fun getHomeSlideBackgroundView(): View? {
        val homeSlideView = view?.findViewById<RecyclerView>(R.id.game_list)?.layoutManager?.findViewByPosition(0)
        return homeSlideView?.findViewById(R.id.placeholderView)
    }

    /**
     * 监听广告view被remove时再执行部分操作，避免
     * 1. 自动播放视频，出现首页还未显示视频就开始播放的情况
     * 2. 过早显示悬浮窗，出现首页还未显示悬浮窗就在广告页显示的情况
     */
    private fun listenStartAdViewRemoved() {
        val startUpAdProvider =
            ARouter.getInstance().build(RouteConsts.provider.adSdk).navigation() as? IStartUpAdProvider
        val startAdContainerView =
            requireActivity().findViewById<View>(if (startUpAdProvider != null) R.id.sdkStartAdContainer else R.id.startAdContainer)
        val parentView = startAdContainerView?.parent as? ViewGroup

        if (parentView == null) {
            showFloatingWindowIfNeeded()
        } else {
            parentView.setOnHierarchyChangeListener(object : ViewGroup.OnHierarchyChangeListener {
                override fun onChildViewRemoved(parent: View?, child: View?) {
                    if (mViewModel.loadStatus.value == LoadStatus.INIT_LOADED && child == startAdContainerView) {
                        AppExecutor.uiExecutor.executeWithDelay({
                            scroll()
                            mScrollCalculatorHelper.onScrollStateChanged(RecyclerView.SCROLL_STATE_IDLE)
                            showFloatingWindowIfNeeded()
                        }, 100)
                        parentView.setOnHierarchyChangeListener(null)
                    }
                }

                override fun onChildViewAdded(parent: View?, child: View?) {
                    // Do nothing
                }
            })
        }
    }

    private fun scroll() {
        val firstVisibleItem = mAutomaticLayoutManager.findFirstVisibleItemPosition()
        val lastVisibleItem = mAutomaticLayoutManager.findLastVisibleItemPosition()
        mScrollCalculatorHelper.onScroll(firstVisibleItem, lastVisibleItem)
    }

    override fun onFragmentPause() {
        pauseVideo()

        DownloadManager.getInstance().removeObserver(dataWatcher)

        handleAutoScroll(false)
    }

    override fun onFragmentResume() {
        resumeVideo()

        DownloadManager.getInstance().addObserver(dataWatcher)

        handleAutoScroll(true)
    }

    private fun pauseVideo() {
        val currentPlayer = mScrollCalculatorHelper.currentPlayer
        if (currentPlayer != null) {
            currentPlayer.resetDetailMask()
            currentPlayer.onVideoPause()
            val currentPosition = currentPlayer.getCurrentPosition()
            val videoUrl = currentPlayer.getUrl()
            if (videoUrl.isNotEmpty()) {
                ScrollCalculatorHelper.savePlaySchedule(MD5Utils.getContentMD5(videoUrl), currentPosition)
            }
        }
    }

    private fun resumeVideo() {
        val currentPlayer = mScrollCalculatorHelper.currentPlayer
        if (currentPlayer != null) {
            val videoUrl = currentPlayer.getUrl()
            if (videoUrl.isNotEmpty()) {
                val position = ScrollCalculatorHelper.getPlaySchedule(MD5Utils.getContentMD5(videoUrl))
                //这里必须要延迟操作，否则会白屏
                mBaseHandler.postDelayed({
                    if (position != 0L) {
                        mScrollCalculatorHelper.currentPlayer?.seekTo(position)
                        mScrollCalculatorHelper.currentPlayer?.onVideoResume(false)
                        val topVideoVoiceStatus = SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true)
                        if (topVideoVoiceStatus) {
                            mScrollCalculatorHelper.currentPlayer?.mute()
                        } else {
                            mScrollCalculatorHelper.currentPlayer?.unMute()
                        }
                    } else {
                        mScrollCalculatorHelper.currentPlayer?.release()
                    }

                }, 50)
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        mCompositeDisposable.dispose()
        if (::mScrollCalculatorHelper.isInitialized) {
            mScrollCalculatorHelper.release()
        }
    }

    private fun handleAutoScroll(resumeScroll: Boolean) {
        val view = mBinding.gameList.layoutManager?.findViewByPosition(0)
        val recyclerView = view?.findViewById<RecyclerView>(R.id.recycler_view)
        val adapter = recyclerView?.adapter
        if (adapter is HomeSlideListAdapter) {
            if (resumeScroll) adapter.startScroll() else adapter.stopScroll()
        }

        val firstPosition =
            (mBinding.gameList.layoutManager as? LinearLayoutManager)?.findFirstVisibleItemPosition()
                ?: 0
        val lastPosition =
            (mBinding.gameList.layoutManager as? LinearLayoutManager)?.findLastVisibleItemPosition()
                ?: 0

        for (i in firstPosition..lastPosition) {
            val viewHolder = mBinding.gameList.findViewHolderForAdapterPosition(i)
            if (viewHolder is GameGallerySlideViewHolder) {
                if (resumeScroll) viewHolder.resumeScrolling() else viewHolder.pauseScrolling()
            }
        }
    }

    //下载被删除事件
    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onEventMainThread(status: EBDownloadStatus) {
        if ("delete" == status.status) {
            mListAdapter.notifyItemAndRemoveDownload(status)
        }
    }

    //安装、卸载事件
    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onEventMainThread(busFour: EBPackage) {
        val data = mListAdapter.getGameEntityByPackage(busFour.packageName)
        for (gameAndPosition in data) {
            mListAdapter.notifyChildItem(gameAndPosition.position, busFour.packageName)
        }
        mViewModel.refreshRecentVGameIfNeeded()
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onEventMainThread(reuse: EBReuse) {
        if (::mListAdapter.isInitialized && "Refresh" == reuse.type) {
            mListAdapter.notifyDataSetChanged()
        }
        mViewModel.refreshRecentVGameIfNeeded()
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onEventMainThread(busNine: EBUISwitch) {
        if (::mLayoutManager.isInitialized
            && MainWrapperFragment.EB_MAIN_SCROLL_TOP == busNine.from
            && MainWrapperFragment.INDEX_HOME == busNine.position
        ) {
            mBinding.gameList.stopScroll()
            mLayoutManager.scrollToPosition(0)
            mScrollCalculatorHelper.currentPlayer?.release()
        }
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onEventMainThread(changed: EBDiscoverChanged) {
        mViewModel.refreshDiscoverCardData()
    }

    fun showUnzipFailureDialog(downloadEntity: DownloadEntity) {
        val data = mListAdapter.getGameEntityByPackage(downloadEntity.packageName)
        for (gameAndPosition in data) {
            val targetView = mLayoutManager.findViewByPosition(gameAndPosition.position)
            if (targetView != null) {
                if (targetView is RecyclerView) {
                    // todo 如果时竖向专题该怎么判断?
                } else {
                    DialogUtils.showUnzipFailureDialog(requireContext(), downloadEntity)
                    return
                }
            }
        }
    }

    fun onScrollChanged() {
        if (parentFragment is HomeSearchToolWrapperFragment && ::mAutomaticLayoutManager.isInitialized) {
            val bannerViewHeight = mAutomaticLayoutManager.heightMap[0] ?: 0
            (parentFragment as HomeSearchToolWrapperFragment).onScrollChanged(
                bannerViewHeight,
                mBinding.gameList.computeVerticalScrollOffset()
            )
        }
    }

    override fun onDarkModeChanged() {
        super.onDarkModeChanged()
        if (::mBinding.isInitialized) {
            mBinding.gameList.recycledViewPool.clear()
            if (::mListAdapter.isInitialized) mListAdapter.notifyItemRangeChanged(0, mListAdapter.itemCount)
            if (isSupportVisible) {
                onScrollChanged()
            }
            val view = mBinding.gameList.layoutManager?.findViewByPosition(0)
            val recyclerView = view?.findViewById<RecyclerView>(R.id.recycler_view)
            val adapter = recyclerView?.adapter
            if (adapter is HomeSlideListAdapter) {
                adapter.notifyItemRangeChanged(0, adapter.itemCount)
            }
        }
    }

    private fun showFloatingWindowIfNeeded() {
        val floatingWindowProvider =
            ARouter.getInstance().build(RouteConsts.provider.floatingwindow).navigation() as? IFloatingWindowProvider

        floatingWindowProvider?.getFloatingWindow(
            "",
            "",
            this,
            mBinding.gameList
        )?.let {
            mCompositeDisposable.add(it)
        }
    }
}