package com.gh.gamecenter.floatingwindow

import android.app.Activity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.view.Gravity
import android.view.View
import androidx.core.view.ViewCompat
import androidx.core.view.ViewPropertyAnimatorListener
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.RecyclerView
import com.therouter.TheRouter
import com.gh.gamecenter.common.base.fragment.BaseLazyFragment
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.exposure.ExposureSource
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.view.HorizontalScrollableView
import com.gh.gamecenter.feature.entity.FloatingWindowEntity
import com.gh.gamecenter.feature.entity.WelcomeDialogEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.provider.IExposureManagerProvider
import com.lzf.easyfloat.EasyFloat
import com.lzf.easyfloat.enums.ShowPattern
import com.lzf.easyfloat.enums.SidePattern

/**
 * 右下角悬浮窗管理类，应用于首页和游戏详情页
 *
 * 相关细节可见 https://jira.shanqu.cc/browse/GHZS-1843
 */
internal object FloatingWindowManager {

    const val TAG = "floating_window"

    private const val TAG_WINDOW_ID = "window_id"
    private const val TAG_PENDING_WINDOW_ACTION = "pending_window_action"
    private const val TAG_ACTIVITY_PREFIX = "activity_floating_window_"

    private const val WINDOW_STATUS_EXPANDED = 0
    private const val WINDOW_STATUS_SHRANK = 1
    private const val WINDOW_STATUS_SHRINKING = 2
    private const val WINDOW_STATUS_EXPANDING = 3

    private const val WINDOW_ACTION_EXPAND = 4

    private const val EXPAND_DELAY = 1000L
    private const val SHRINK_AND_EXPAND_ANIMATION_DURATION = 300L

    private val TAG_VIEW_STATUS = com.gh.gamecenter.common.R.string.app_name

    // activity hash id 和 业务 id 绑定的一个 pair，使用前需要先校验，为了节省内存，每次 dismiss 会置空
    private var mActivityAndBusinessIdPair: Pair<String, String>? = null

    private val mDismissedIdSet by lazy { hashSetOf<String>() } // 已经 dismiss 过的 window id 合集

    val ignoreCountIdSet by lazy { hashSetOf<String>() } // 已经标记为当次启动显示过的 window id 合集

    private var mWelcomeDialogCallback: ((welcomeDialogEntity: WelcomeDialogEntity) -> Unit)? = null

    private val mHandler by lazy {
        object : Handler(Looper.getMainLooper()) {
            override fun handleMessage(msg: Message) {
                super.handleMessage(msg)

                val action = msg.data.getInt(TAG_PENDING_WINDOW_ACTION)
                val windowId = msg.data.getString(TAG_WINDOW_ID)

                EasyFloat.getFloatView(windowId)?.let {
                    when (action) {
                        WINDOW_ACTION_EXPAND -> expandView(it)
                        else -> return
                    }
                }
            }
        }
    }

    /**
     * 根据相关限制，判断是否需要显示浮窗
     */
    private fun showWindowIfNeeded(
        activity: Activity?,
        windowList: ArrayList<FloatingWindowEntity>,
        gameId: String,
        gameName: String,
        location: String
    ) {
        if (activity == null) return
        if (mDismissedIdSet.contains(activity.getUniqueId())) return
        if (windowList.isEmpty()) return

        val originalWindowView = EasyFloat.getFloatView(activity.getUniqueId())?.let {
            it.visibility = View.VISIBLE

            if (it.getStatus() == WINDOW_STATUS_SHRANK) {
                expandView(it)
            }
        }

        // 当前 activity 已经有了相关 window，将其设置为展开可见并返回
        if (originalWindowView != null) return

        showWindow(activity, windowList, gameId, gameName, location)
    }

    private fun showWindow(
        activity: Activity,
        windowEntityList: ArrayList<FloatingWindowEntity>,
        gameId: String,
        gameName: String,
        location: String
    ) {
        mActivityAndBusinessIdPair = Pair(activity.getUniqueId(), windowEntityList.firstOrNull()?.id ?: "unknown")

        EasyFloat.with(activity)
            .setLayout(com.gh.gamecenter.feature.R.layout.layout_floating_window)
            .setTag(activity.getUniqueId())
            .setAnimator(null)
            .setGravity(Gravity.END.xor(Gravity.BOTTOM), 0, -(118F).dip2px())
            .setSidePattern(SidePattern.RESULT_SIDE)
            .setDragEnable(false)
            .setShowPattern(ShowPattern.CURRENT_ACTIVITY)
            .registerCallback {
                createResult { _, _, view ->
                    val horizontalScrollView = view?.findViewById<HorizontalScrollableView>(com.gh.gamecenter.feature.R.id.horizontalScrollView)
                    horizontalScrollView?.setFixedWidthAndHeightForViewPager2(64F.dip2px(), 64F.dip2px())
                    horizontalScrollView?.disableAutoScroll()
                    horizontalScrollView?.start(
                        windowEntityList,
                        false,
                        FloatingWindowViewAdapter(
                            gameId,
                            gameName,
                            location,
                            windowEntityList,
                            object : FloatingWindowViewAdapter.Callback {
                                override fun onItemExpandCallback(position: Int) {
                                    val windowEntity = windowEntityList[position]
                                    windowEntity.link.icon = windowEntity.expandedImage
                                    windowEntity.link.floatingWindowId = windowEntity.id

                                    hideWindow(activity)
                                    try {
                                        mWelcomeDialogCallback?.invoke(windowEntity.link)
                                        LogUtils.logFloatingWindowClicked(
                                            action = "点击悬浮窗展开弹窗",
                                            windowId = windowEntity.id,
                                            source = "首页",
                                            gameId = "",
                                            gameName = "",
                                            linkId = windowEntity.link.link ?: "",
                                            linkType = windowEntity.link.type ?: "",
                                            linkText = windowEntity.link.text ?: ""
                                        )

                                        SensorsBridge.trackSuspendedWindowClick(
                                            "收起",
                                            "展开",
                                            location,
                                            gameId = gameId,
                                            gameName = gameName,
                                            linkId = windowEntity.link.link ?: "",
                                            linkType = windowEntity.link.type ?: "",
                                            linkText = windowEntity.link.text ?: ""
                                        )
                                    } catch (e: Exception) {
                                        e.printStackTrace()
                                    }
                                }

                                override fun onItemDismissClickedCallback(position: Int) {
                                    horizontalScrollView.resetIndicator()
                                }

                                override fun onDataListEmptyCallback() {
                                    dismissWindow(activity)
                                }
                            }
                        )
                    ) {
                        val link = windowEntityList.getOrNull(it)?.link
                        SensorsBridge.trackSuspendedWindowShow(
                            "收起",
                            location,
                            gameId = gameId,
                            gameName = gameName,
                            linkText = link?.text ?: "",
                            linkType = link?.type ?: "",
                            linkId = link?.link ?: ""
                        )
                    }
                }
            }
            .show()
    }

    private fun dimWindow(activity: Activity?) {
        activity ?: return

        mHandler.removeCallbacksAndMessages(null)
        EasyFloat.getFloatView(activity.getUniqueId())?.let {
            when (it.getStatus()) {
                WINDOW_STATUS_EXPANDED -> shrinkView(it)
                else -> return
            }
        }
    }

    fun showHiddenWindow(activity: Activity?) {
        activity ?: return

        EasyFloat.getFloatView(activity.getUniqueId())?.let {
            it.visibility = View.VISIBLE
        }
    }

    fun hideWindow(activity: Activity?) {
        activity ?: return

        mHandler.removeCallbacksAndMessages(null)

        EasyFloat.getFloatView(activity.getUniqueId())?.let {
            it.visibility = View.GONE
        }
    }

    private fun dismissWindow(activity: Activity?) {
        activity ?: return

        mActivityAndBusinessIdPair = null

        mDismissedIdSet.add(activity.getUniqueId())

        mHandler.removeCallbacksAndMessages(null)
        EasyFloat.dismiss(activity.getUniqueId())
    }

    /**
     * 收起浮窗
     */
    private fun shrinkView(view: View) {
        val translationX = ((view.findViewById<View>(com.gh.gamecenter.feature.R.id.imageIv)?.width ?: view.width) + 8F.dip2px()) / 2F
        ViewCompat.animate(view)
            .setListener(object : ViewPropertyAnimatorListener {
                override fun onAnimationStart(view: View?) {
                    view?.setStatus(WINDOW_STATUS_SHRINKING)
                }

                override fun onAnimationEnd(view: View?) {
                    view?.setStatus(WINDOW_STATUS_SHRANK)
                }

                override fun onAnimationCancel(view: View?) {
                    view?.setStatus(WINDOW_STATUS_EXPANDED)
                }
            })
            .alpha(0.6F)
            .translationX(translationX)
            .setDuration(SHRINK_AND_EXPAND_ANIMATION_DURATION)
            .start()
    }

    /**
     * 展开浮窗
     */
    private fun expandView(view: View) {
        ViewCompat.animate(view)
            .setListener(object : ViewPropertyAnimatorListener {
                override fun onAnimationStart(view: View?) {
                    view?.setStatus(WINDOW_STATUS_EXPANDING)
                }

                override fun onAnimationEnd(view: View?) {
                    view?.setStatus(WINDOW_STATUS_EXPANDED)
                }

                override fun onAnimationCancel(view: View?) {
                    view?.setStatus(WINDOW_STATUS_SHRANK)
                }
            })
            .alpha(1F)
            .translationX(0F)
            .setDuration(SHRINK_AND_EXPAND_ANIMATION_DURATION)
            .start()
    }

    private fun Activity.getUniqueId(): String {
        return TAG_ACTIVITY_PREFIX + System.identityHashCode(this).toString()
    }

    private fun View.setStatus(status: Int) {
        setTag(TAG_VIEW_STATUS, status)
    }

    private fun View.getStatus(): Int {
        return when (val statusTag = getTag(TAG_VIEW_STATUS)) {
            is Int -> return statusTag
            else -> WINDOW_STATUS_EXPANDED
        }
    }

    internal fun showFloatingWindowIfNeeded(
        windowEntityList: ArrayList<FloatingWindowEntity>,
        gameId: String,
        gameName: String,
        location: String,
        fragment: BaseLazyFragment,
        recyclerView: RecyclerView,
        welcomeDialogCallback: ((welcomeDialogEntity: WelcomeDialogEntity) -> Unit)?
    ) {
        mWelcomeDialogCallback = welcomeDialogCallback ?: mWelcomeDialogCallback

        fragment.parentFragmentManager.registerFragmentLifecycleCallbacks(
            object : FragmentManager.FragmentLifecycleCallbacks() {
                override fun onFragmentViewDestroyed(fm: FragmentManager, f: Fragment) {
                    if (fragment == f) {
                        fragment.parentFragmentManager.unregisterFragmentLifecycleCallbacks(this)
                        mWelcomeDialogCallback = null
                    }
                }
            }, false
        )

        if (fragment.isCurrentlyVisible()) {
            showWindowIfNeeded(fragment.activity, windowEntityList, gameId, gameName, location)
        }

        fragment.setResumeAndPauseListener { isResume ->
            if (isResume) {
                showWindowIfNeeded(fragment.activity, windowEntityList, gameId, gameName, location)
            } else {
                hideWindow(fragment.activity)
            }
        }

        recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                super.onScrollStateChanged(recyclerView, newState)

                val activity = fragment.activity ?: return

                if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                    expandAfterDelay(activity)
                } else {
                    dimWindow(fragment.activity)
                }
            }
        })
    }

    private fun expandAfterDelay(activity: Activity) {
        val message = Message().apply {
            data = Bundle().also {
                it.putString(TAG_WINDOW_ID, activity.getUniqueId())
                it.putInt(TAG_PENDING_WINDOW_ACTION, WINDOW_ACTION_EXPAND)
            }
        }
        mHandler.sendMessageDelayed(message, EXPAND_DELAY)
    }

    fun logExposure(gameId: String, windowId: String): ExposureEvent {
        val exposureMangerProvider = TheRouter.get(IExposureManagerProvider::class.java)

        val exposureSourceList = arrayListOf<ExposureSource>()

        if (gameId.isEmpty()) {
            exposureSourceList.add(ExposureSource("新首页"))
        } else {
            exposureSourceList.add(ExposureSource("游戏详情", gameId))
        }

        exposureSourceList.add(ExposureSource("右下悬浮窗", windowId))

        val exposureEvent = ExposureEvent.createEvent(null, exposureSourceList)

        exposureMangerProvider?.logExposure(exposureEvent)

        return exposureEvent
    }

}