package com.gh.gamecenter.floatingwindow

import android.app.Activity
import android.content.Context
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.recyclerview.widget.RecyclerView
import com.alibaba.android.arouter.launcher.ARouter
import com.gh.gamecenter.common.base.fragment.BaseLazyFragment
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.feature.provider.ILinkDirectUtilsProvider
import com.gh.gamecenter.common.utils.ImageUtils
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.enlargeTouchArea
import com.gh.gamecenter.common.view.WrapContentDraweeView
import com.gh.gamecenter.core.HaloApp
import com.gh.gamecenter.core.provider.IAppProvider
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.exposure.ExposureSource
import com.gh.gamecenter.feature.provider.IExposureManagerProvider
import com.lightgame.utils.Utils
import com.lzf.easyfloat.EasyFloat
import com.lzf.easyfloat.enums.ShowPattern
import com.lzf.easyfloat.enums.SidePattern
import java.util.*

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

    private 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 SPLITTER = "<->"

    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

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

    private val mSharedPreferences by lazy {
        HaloApp.getInstance().getSharedPreferences("floating_window", Context.MODE_PRIVATE)
    }

    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?,
        entity: FloatingWindowEntity,
        gameId: String,
        gameName: String
    ) {
        if (activity == null) return
        if (mDismissedIdSet.contains(activity.getUniqueId())) 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

        var shouldShow: Boolean
        val appProvider = ARouter.getInstance().build(RouteConsts.provider.app).navigation() as? IAppProvider
        val isBrandNewInstall = appProvider?.getIsBrandNewInstall() ?: false

        // 如果是仅首次安装时只显示一次，把它转换为只显示一次的事件
        if (entity.pushType == "first") {
            if (isBrandNewInstall) {
                entity.pushType = "1"
            } else {
                entity.pushType = "0"
            }
        }

        // 判断是否还需要显示
        if (entity.pushType == "always") {
            shouldShow = true
            Utils.log(TAG, "${entity.id} 常驻显示")
        } else if (entity.pushType == "first" && isBrandNewInstall) {
            shouldShow = true
            Utils.log(TAG, "${entity.id} 显示的日期与存储的日期不一致，允许显示")
        } else {
            try {
                // 使用 window 实体 id 作为 SP 的 key，dayOfYear + 已经显示的次数作为 value 来判断当前是否需要显示
                val countLimit = entity.pushType.toInt()
                val dateAndCountString = SPUtils.getString(mSharedPreferences, entity.id)

                var currentCount = 0
                var ignoreCountForThisLaunch = mIgnoreCountIdSet.contains(entity.id) // 当前启动忽略显示计数，即当前启动显示一次后后续都允许继续显示
                val currentDayOfYear = Calendar.getInstance().get(Calendar.DAY_OF_YEAR).toString()

                Utils.log(TAG, "${entity.id} 存储的值为 $dateAndCountString")

                if (dateAndCountString.isNotEmpty()) {
                    val dateAndCountArray = dateAndCountString.split(SPLITTER)

                    currentCount = dateAndCountArray[1].toInt()

                    if (currentDayOfYear != dateAndCountArray.first()) {
                        shouldShow = true
                        currentCount = 0

                        Utils.log(TAG, "${entity.id} 显示的日期与存储的日期不一致")
                    } else {
                        Utils.log(TAG, "${entity.id} 历史显示的次数为 $currentCount， 上限为 $countLimit")

                        if (!ignoreCountForThisLaunch) {
                            currentCount = currentCount + 1
                        }
                        shouldShow = currentCount <= countLimit
                    }
                } else {
                    currentCount++
                    shouldShow = true
                }

                if (shouldShow) {
                    val spValue = currentDayOfYear + SPLITTER + currentCount

                    Utils.log(TAG, "${entity.id} 更新显示数据 $spValue")

                    SPUtils.setString(mSharedPreferences, entity.id, spValue)
                }
            } catch (e: NumberFormatException) {
                shouldShow = false
            }
        }

        if (shouldShow) {
            mIgnoreCountIdSet.add(entity.id)
            showWindow(activity, entity, gameId, gameName)
        }
    }

    private fun showWindow(
        activity: Activity,
        entity: FloatingWindowEntity,
        gameId: String,
        gameName: String
    ) {
        EasyFloat.with(activity)
            .setLayout(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 iv = view?.findViewById<WrapContentDraweeView>(R.id.imageIv)
                    val closeView = view?.findViewById<View>(R.id.closeIv)

                    // 记录曝光
                    val exposureEvent = logExposure(gameId, entity.id)

                    if (entity.pushType == "always") {
                        closeView?.visibility = View.GONE
                    } else {
                        closeView?.enlargeTouchArea(5)
                        closeView?.setOnClickListener {
                            dismissWindow(activity)

                            LogUtils.logFloatingWindowClicked(
                                action = "关闭悬浮窗",
                                windowId = entity.id,
                                source = if (gameId.isEmpty()) "首页" else "游戏详情",
                                gameId = gameId,
                                gameName = gameName,
                                linkId = entity.link.link ?: "",
                                linkType = entity.link.type ?: "",
                                linkText = entity.link.text ?: ""
                            )
                        }
                    }

                    // 限制最大宽度为 256dp，后续根据图片大小进行处理
                    iv?.setTag(ImageUtils.TAG_TARGET_WIDTH, 256F.dip2px())
                    // 添加加载回调，避免网络差出现图片没有但是有关闭按钮的情况
                    iv?.registerLoadingCallback(object : WrapContentDraweeView.LoadingCallback {
                        override fun loaded() {
                            if (entity.pushType != "always") {
                                closeView?.visibility = View.VISIBLE
                            }
                        }
                    })
                    iv?.setMaxAndMinSizeLimit(64, 48)
                    iv?.setSubsampleSize(4)
                    ImageUtils.display(iv, entity.image)

                    view?.setOnClickListener {
                        val directUtils = ARouter.getInstance().build(RouteConsts.provider.linkDirectUtils)
                            .navigation() as ILinkDirectUtilsProvider
                        directUtils.directToLinkPage(activity, entity.link, "右下角悬浮窗", "", exposureEvent)

                        LogUtils.logFloatingWindowClicked(
                            action = "点击悬浮窗跳转页面",
                            windowId = entity.id,
                            source = if (gameId.isEmpty()) "首页" else "游戏详情",
                            gameId = gameId,
                            gameName = gameName,
                            linkId = entity.link.link ?: "",
                            linkType = entity.link.type ?: "",
                            linkText = entity.link.text ?: ""
                        )
                    }
                }
            }
            .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
            }
        }
    }

    private 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

        mDismissedIdSet.add(activity.getUniqueId())

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

    /**
     * 收起浮窗
     */
    private fun shrinkView(view: View) {
        val translationX = ((view.findViewById<View>(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(
        entity: FloatingWindowEntity,
        gameId: String,
        gameName: String,
        fragment: BaseLazyFragment,
        recyclerView: RecyclerView
    ) {
        if (fragment.isVisible) {
            showWindowIfNeeded(fragment.activity, entity, gameId, gameName)
        }

        fragment.setResumeAndPauseListener { isResume ->
            if (isResume) {
                showWindowIfNeeded(fragment.activity, entity, gameId, gameName)
            } 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)
    }

    private fun logExposure(gameId: String, windowId: String): ExposureEvent {
        val exposureMangerProvider =
            ARouter.getInstance().build(RouteConsts.provider.exposureManager).navigation() as? IExposureManagerProvider

        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
    }

}