package com.gh.gamecenter.discovery

import android.app.Activity
import android.content.Intent
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.OnScrollListener
import com.ethanhua.skeleton.Skeleton
import com.gh.common.exposure.ExposureListener
import com.gh.common.util.DialogUtils
import com.gh.common.util.NewFlatLogUtils
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.baselist.LazyListFragment
import com.gh.gamecenter.common.baselist.ListAdapter
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.exposure.ExposureSource
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.databinding.LayoutDiscoveryGuideBinding
import com.gh.gamecenter.databinding.LayoutPopupDiscoveryDislikeBinding
import com.gh.gamecenter.eventbus.EBDiscoverChanged
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.eventbus.EBPackage
import com.google.android.flexbox.FlexboxLayout
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadEntity
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.util.*
import kotlin.concurrent.fixedRateTimer

class DiscoveryFragment : LazyListFragment<DiscoveryItemData, DiscoveryViewModel>() {

    private var mAdapter: DiscoveryAdapter? = null
    private var mDiscoveryVisibleTimer: Timer? = null
    private var mDiscoveryQuitTimer: Timer? = null

    //页面顶部露出次数
    private var mPageTopVisibleCount = 0

    //页面底部露出次数
    private var mPageBottomVisibleCount = 0
    private lateinit var mExposureListener: ExposureListener

    //下拉刷新次数
    private var mRefreshCount = 0

    private val dataWatcher = object : DataWatcher() {
        override fun onDataChanged(downloadEntity: DownloadEntity) {
            mAdapter?.notifyItemByDownload(downloadEntity)
            if (downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] == XapkUnzipStatus.FAILURE.name) {
                showUnzipFailureDialog(downloadEntity)
            }
        }

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

    override fun initRealView() {
        super.initRealView()
        setNavigationTitle("发现")
        mCachedView.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(requireContext()))
    }

    override fun onRefresh() {
        super.onRefresh()
        NewFlatLogUtils.logDiscoverPageDropDownRefresh(++mRefreshCount)
    }

    override fun initSkeletonScreen() {
        if (mSkeletonScreenView == null) return
        mSkeletonScreen = Skeleton.bind(mSkeletonScreenView)
            .shimmer(true)
            .angle(Constants.SHIMMER_ANGLE)
            .color(com.gh.gamecenter.common.R.color.ui_skeleton_highlight)
            .duration(Constants.SHIMMER_DURATION)
            .maskWidth(Constants.MASK_WIDTH)
            .gradientCenterColorWidth(Constants.GRADIENT_CENTER_COLOR_WIDTH)
            .apply {
                load(R.layout.fragment_discovery_skeleton)
            }
            .show()
    }

    override fun onFragmentFirstVisible() {
        super.onFragmentFirstVisible()
        if (mAdapter != null) {
            mExposureListener = ExposureListener(this, mAdapter!!)
            mListRv?.addOnScrollListener(mExposureListener)
            mListRv?.addOnScrollListener(object : OnScrollListener() {
                override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                    super.onScrollStateChanged(recyclerView, newState)
                    if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                        val layoutManager = mListRv?.layoutManager as? LinearLayoutManager ?: return
                        val firstVisibleItemPosition = layoutManager.findFirstCompletelyVisibleItemPosition()
                        val lastVisibleItemPosition = layoutManager.findLastCompletelyVisibleItemPosition()
                        if (firstVisibleItemPosition == 0) {
                            mPageTopVisibleCount++
                            NewFlatLogUtils.logDiscoverPageTopVisible(mPageTopVisibleCount)
                        }
                        if (lastVisibleItemPosition == (mAdapter?.itemCount ?: 0) - 1) {
                            mPageBottomVisibleCount++
                            NewFlatLogUtils.logDiscoverPageBottomStay(mPageBottomVisibleCount)
                        }
                    }
                }
            })
        }

        mDiscoveryVisibleTimer =
            fixedRateTimer(
                "Discovery-Visible-Rate-Timer",
                initialDelay = mDiscoveryVisibleDuration,
                period = mDiscoveryVisibleDuration
            ) {
                NewFlatLogUtils.logDiscoverPageVisible()
            }

        mDiscoveryQuitTimer =
            fixedRateTimer(
                "Discovery-Quit-Rate-Timer",
                initialDelay = mDiscoveryQuitDuration,
                period = mDiscoveryQuitDuration
            ) {
                val lastVisiblePosition =
                    (mListRv?.layoutManager as? LinearLayoutManager)?.findLastCompletelyVisibleItemPosition() ?: 0
                NewFlatLogUtils.logDiscoverPageQuit(lastVisiblePosition)
                mDiscoveryQuitTimer?.cancel()
                mDiscoveryQuitTimer = null
            }

        NewFlatLogUtils.logDiscoverPageEnter()
    }

    override fun provideListAdapter(): ListAdapter<*> {
        val basicExposureSource = arrayListOf<ExposureSource>().apply {
            arguments?.getParcelable<ExposureSource>(EntranceConsts.KEY_EXPOSURE_SOURCE)?.let { add(it) }
            arguments?.getParcelableArrayList<ExposureSource>(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST)
                ?.let { addAll(it) }
        }
        val outerSequence = requireArguments().getInt(EntranceConsts.KEY_POSITION, -1)

        val dislikeCallback: (anchorView: View, itemData: DiscoveryItemData) -> Unit =
            { view, itemData ->
                showDislikeWindow(view, itemData)
            }
        return mAdapter ?: DiscoveryAdapter(
            requireContext(),
            this,
            mListViewModel,
            basicExposureSource,
            outerSequence,
            "发现页",
            dislikeCallback,
        ).also { mAdapter = it }
    }

    override fun onFragmentResume() {
        super.onFragmentResume()
        DownloadManager.getInstance().addObserver(dataWatcher)
    }

    override fun onFragmentPause() {
        super.onFragmentPause()
        DownloadManager.getInstance().removeObserver(dataWatcher)
        val lastVisiblePosition =
            (mListRv?.layoutManager as? LinearLayoutManager)?.findLastCompletelyVisibleItemPosition() ?: 0
        NewFlatLogUtils.logDiscoverPageInvisible(lastVisiblePosition)
    }

    override fun onStop() {
        super.onStop()
        val lastVisiblePosition =
            (mListRv?.layoutManager as? LinearLayoutManager)?.findLastCompletelyVisibleItemPosition() ?: 0
        NewFlatLogUtils.logDiscoverPageQuit(lastVisiblePosition)
        if (mDiscoveryVisibleTimer != null) {
            mDiscoveryVisibleTimer?.cancel()
            mDiscoveryVisibleTimer = null
        }
        if (mDiscoveryQuitTimer != null) {
            mDiscoveryQuitTimer?.cancel()
            mDiscoveryQuitTimer = null
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == INTERESTED_GAME_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
            onRefresh()
        }
    }

    override fun getItemDecoration(): RecyclerView.ItemDecoration? = null

    private fun showUnzipFailureDialog(downloadEntity: DownloadEntity) {
        val data = mListViewModel.positionAndPackageMap
        for (gameAndPosition in data) {
            if (gameAndPosition.key.contains(downloadEntity.packageName)) {
                val targetView = mLayoutManager.findViewByPosition(gameAndPosition.value)
                if (targetView != null) {
                    DialogUtils.showUnzipFailureDialog(requireContext(), downloadEntity)
                    return
                }
            }
        }
    }

    override fun onChanged(ts: MutableList<DiscoveryItemData>?) {
        super.onChanged(ts)
        val isFirstGuide = SPUtils.getBoolean(Constants.SP_DISCOVERY_GUIDE, true)
        if (!isFirstGuide) return
        AppExecutor.uiExecutor.executeWithDelay({
            showGuideView()
            SPUtils.setBoolean(Constants.SP_DISCOVERY_GUIDE, false)
        }, 800)
    }

    private fun showGuideView() {
        if (!isSupportVisible) return
        val firstView = mListRv?.layoutManager?.findViewByPosition(0)
        if (firstView != null && requireActivity() is AppCompatActivity) {
            val location = IntArray(2)
            firstView.getLocationInWindow(location)
            val decorView = requireActivity().window.decorView as? FrameLayout
            val guideViewBinding =
                LayoutDiscoveryGuideBinding.inflate(LayoutInflater.from(requireContext()), decorView, true)
            val originalBackground = firstView.background
            val newColor =
                if (mIsDarkModeOn) com.gh.gamecenter.common.R.color.black.toColor(requireContext()) else com.gh.gamecenter.common.R.color.white.toColor(requireContext())
            firstView.setBackgroundColor(newColor)
            guideViewBinding.guideImageContainer.setCardBackgroundColor(newColor)
            val snapshotBitmap = firstView.getBitmapFromView(
                firstView.width - 16F.dip2px(),
                firstView.height,
                -8F.dip2px().toFloat(),
                0F
            )
            firstView.background = originalBackground
            guideViewBinding.guideImage.setImageBitmap(snapshotBitmap)
            (guideViewBinding.guideImageContainer.layoutParams as RelativeLayout.LayoutParams).run {
                topMargin = location[1]
                guideViewBinding.guideImageContainer.layoutParams = this
            }
            guideViewBinding.root.setOnClickListener {
                decorView?.removeView(it)
            }
        }
    }

    private fun showDislikeWindow(view: View, itemData: DiscoveryItemData) {
        val decorView = requireActivity().window.decorView as? FrameLayout
        val binding = LayoutPopupDiscoveryDislikeBinding.inflate(LayoutInflater.from(requireContext()), decorView, true)
        binding.reasonFlex.removeAllViews()
        Constants.FEEDBACK_REASON_LIST.toList().forEach {
            val popupItem = getPopupItem(it)
            val params =
                FlexboxLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
            binding.reasonFlex.addView(popupItem, params)
            popupItem.setOnClickListener {
                val gameEntity = itemData.gameEntity ?: return@setOnClickListener
                val index = mAdapter?.entityList?.indexOf(itemData) ?: -1
                if (index < 0) return@setOnClickListener
                mListViewModel.discoveryFeedback(gameEntity.id, popupItem.text.toString(), gameEntity.type ?: "") {
                    NewFlatLogUtils.logDiscoverPageNotInterest(gameEntity.id, gameEntity.name ?: "")
                    mAdapter?.entityList?.removeAt(index)
                    mAdapter?.notifyItemRemoved(index)
                    decorView?.removeView(binding.root)
                    ToastUtils.showToast("已根据你的偏好优化推荐机制~", Gravity.CENTER)
                    EventBus.getDefault().post(EBDiscoverChanged())
                }
            }
        }
        binding.root.setOnClickListener {
            decorView?.removeView(it)
        }
        binding.contentView.visibility = View.INVISIBLE
        binding.contentView.post {
            val (windowPosition, isNeedShowUp) = getWindowPosition(view, binding.contentView, 36F.dip2px())
            (binding.contentView.layoutParams as RelativeLayout.LayoutParams).run {
                topMargin = windowPosition[1]
                binding.contentView.layoutParams = this
            }
            binding.anchorUpIv.goneIf(isNeedShowUp)
            binding.anchorDownIv.goneIf(!isNeedShowUp)
            binding.contentView.visibility = View.VISIBLE
        }
    }

    private fun getPopupItem(reason: String): TextView {
        return TextView(requireContext()).apply {
            height = 32F.dip2px()
            text = reason
            textSize = 12F
            includeFontPadding = false
            setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(requireContext()))
            gravity = Gravity.CENTER
            setPadding(12F.dip2px(), 0F.dip2px(), 12F.dip2px(), 0F.dip2px())
            background = com.gh.gamecenter.common.R.drawable.bg_shape_space_radius_8.toDrawable(requireContext())
        }
    }

    private fun getWindowPosition(
        anchorView: View,
        contentView: View,
        distanceY: Int = 0
    ): Pair<IntArray, Boolean> {
        val windowPos = IntArray(2)
        val anchorLoc = IntArray(2)
        anchorView.getLocationInWindow(anchorLoc)
        val anchorHeight = anchorView.height
        val screenHeight = anchorView.context.resources.displayMetrics.heightPixels
        contentView.measure(0, 0)
        val contentViewWidth = contentView.width
        val contentViewHeight = contentView.height
        val isNeedShowUp = screenHeight - anchorLoc[1] - anchorHeight < contentViewHeight
        windowPos[1] = if (isNeedShowUp) {
            anchorLoc[1] - contentViewHeight + distanceY
        } else {
            anchorLoc[1] + anchorHeight - distanceY
        }
        windowPos[0] = (anchorLoc[0] - contentViewWidth + anchorView.width) / 2
        return Pair(windowPos, isNeedShowUp)
    }

    override fun onDarkModeChanged() {
        super.onDarkModeChanged()
        mListRv?.recycledViewPool?.clear()
        mAdapter?.notifyItemRangeChanged(0, mAdapter?.itemCount ?: 0)
        mCachedView?.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(requireContext()))
    }

    override fun isAutomaticLoad(): Boolean = false

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

    // 安装/卸载 事件
    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onEventMainThread(busFour: EBPackage) {
        if (busFour.isInstalledOrUninstalled()) {
            mAdapter?.notifyDataSetChanged()
        }
    }

    companion object {
        private const val mDiscoveryVisibleDuration = 5 * 1000L
        private const val mDiscoveryQuitDuration = 60 * 60 * 1000L
        const val INTERESTED_GAME_REQUEST_CODE = 100
    }

}