package com.adealink.weparty.profile.search

import android.text.Editable
import android.text.TextWatcher
import android.view.Gravity
import android.view.KeyEvent
import android.view.inputmethod.EditorInfo
import android.widget.TextView
import android.widget.TextView.OnEditorActionListener
import androidx.activity.viewModels
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.OrientationHelper
import androidx.recyclerview.widget.RecyclerView
import com.adealink.frame.base.Rlt
import com.adealink.frame.base.fastLazy
import com.adealink.frame.mvvm.view.viewBinding
import com.adealink.frame.router.Router
import com.adealink.frame.router.annotation.RouterUri
import com.adealink.frame.util.onClick
import com.adealink.frame.util.statusBarHeight
import com.adealink.weparty.commonui.BaseActivity
import com.adealink.weparty.commonui.ext.dp
import com.adealink.weparty.commonui.ext.fitSystemWindows
import com.adealink.weparty.commonui.ext.gone
import com.adealink.weparty.commonui.ext.isSuccess
import com.adealink.weparty.commonui.ext.onWindowInsets
import com.adealink.weparty.commonui.ext.show
import com.adealink.weparty.commonui.recycleview.adapter.MultiTypeListAdapter
import com.adealink.weparty.commonui.recycleview.itemdecoration.VerticalSpaceItemDecoration
import com.adealink.weparty.commonui.recycleview.layoutmanager.FlowLayoutManager
import com.adealink.weparty.commonui.toast.util.showFailedToast
import com.adealink.weparty.module.profile.Profile
import com.adealink.weparty.network.data.NoMoreDataError
import com.adealink.weparty.profile.databinding.ActivityUserSearchBinding
import com.adealink.weparty.profile.datasource.local.SearchLocalService
import com.adealink.weparty.profile.relation.viewmodel.FollowViewModel
import com.adealink.weparty.profile.search.adapter.SearchHistoryViewBinder
import com.adealink.weparty.profile.search.adapter.SearchItemViewBinder
import com.adealink.weparty.profile.search.data.SearchHistoryItemData
import com.adealink.weparty.profile.search.data.SearchResultItemData
import com.adealink.weparty.profile.search.viewmodel.SearchViewModel
import com.qmuiteam.qmui.widget.util.QMUIKeyboardHelper
import com.qmuiteam.qmui.widget.util.QMUIStatusBarHelper
import com.adealink.weparty.R as APP_R

@RouterUri(
    path = [Profile.Search.PATH],
    desc = "搜索"
)
class SearchActivity : BaseActivity(),
    SearchItemViewBinder.OnResultClickListener,
    SearchHistoryViewBinder.OnHistoryClickListener {

    private val binding by viewBinding(ActivityUserSearchBinding::inflate)

    private val historyAdapter by fastLazy { MultiTypeListAdapter<SearchHistoryItemData>() }
    private var historyList = mutableListOf<String>()
    private val resultAdapter by fastLazy { MultiTypeListAdapter<SearchResultItemData>() }

    private val searchViewModel by viewModels<SearchViewModel>()
    private val followViewModel by viewModels<FollowViewModel>()
    private var isSearching = MutableLiveData<Boolean>()
    private var isInputEmpty = MutableLiveData<Boolean>()

    override fun onBeforeCreate() {
        super.onBeforeCreate()
        Router.bind(this)
    }

    override fun initViews() {
        super.initViews()
        QMUIStatusBarHelper.setStatusBarLightMode(this)
        setContentView(binding.root)
        binding.clTop.updateLayoutParams<ConstraintLayout.LayoutParams> {
            topMargin = this@SearchActivity.statusBarHeight()
        }

        historyAdapter.register(SearchHistoryViewBinder(this))
        binding.rvHistory.layoutManager =
            FlowLayoutManager(OrientationHelper.VERTICAL, Gravity.START, 8.dp(), 10.dp())
        binding.rvHistory.adapter = historyAdapter

        resultAdapter.register(SearchItemViewBinder(this))
        binding.rvResult.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
        binding.rvResult.adapter = resultAdapter
        binding.rvResult.addItemDecoration(
            VerticalSpaceItemDecoration(20.dp())
        )

        binding.etSearchInput.isClickable = true
        binding.etSearchInput.setOnEditorActionListener(object : OnEditorActionListener {
            override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean {
                if (actionId == EditorInfo.IME_ACTION_SEARCH
                    || (actionId == EditorInfo.IME_ACTION_UNSPECIFIED && event != null && event.keyCode == KeyEvent.KEYCODE_ENTER && event.action == KeyEvent.ACTION_DOWN)
                ) {
                    search(binding.etSearchInput.text?.toString(), false)
                }
                return true
            }
        })
        binding.etSearchInput.addTextChangedListener(object : TextWatcher {
            override fun beforeTextChanged(
                s: CharSequence?,
                start: Int,
                count: Int,
                after: Int
            ) {
            }

            override fun onTextChanged(
                s: CharSequence?,
                start: Int,
                before: Int,
                count: Int
            ) {
            }

            override fun afterTextChanged(s: Editable?) {
                val input = s?.trim()?.toString()
                isInputEmpty.postValue(input.isNullOrEmpty())
            }
        })
        binding.tvSearch.onClick {
            search(binding.etSearchInput.text?.toString(), false)
        }

        fitWindow()
        QMUIKeyboardHelper.showKeyboard(binding.etSearchInput, 200)
        binding.etSearchInput.requestFocus()
    }

    private fun fitWindow() {
        // 关键：只在需要的时候设置
        window?.fitSystemWindows(false)
        // 内容布局处理键盘
        binding.root.onWindowInsets { view, insets ->
            val ime = insets.getInsets(WindowInsetsCompat.Type.ime())
            val nav = insets.getInsets(WindowInsetsCompat.Type.navigationBars())

            // 键盘显示时使用键盘高度，否则使用导航栏高度
            val bottomInset = if (ime.bottom > 0) ime.bottom else nav.bottom
            view.updatePadding(bottom = bottomInset)
            insets
        }
    }

    override fun loadData() {
        super.loadData()
        updateHistoryList(SearchLocalService.getSearchHistory()) { list ->
            if (list.isEmpty()) {
                binding.clHistory.gone()
                return@updateHistoryList
            }
            binding.clHistory.show()
            historyAdapter.submitList(list.map { SearchHistoryItemData(it) })
        }
    }

    private fun search(input: String?, fromHistory: Boolean) {
        val keyword = input?.trim()
        if (keyword.isNullOrEmpty()) {
            return
        }
        binding.etSearchInput.clearFocus()
        QMUIKeyboardHelper.hideKeyboard(binding.etSearchInput)
        hideHistoryList()

        //校验非法字符
        isSearching.postValue(true)
        searchViewModel.search(keyword)

        if (!fromHistory) {
            addHistoryList(input) { list ->
                historyAdapter.submitList(list.map { SearchHistoryItemData(it) })
            }
        }
    }

    private fun addHistoryList(
        keyword: String?,
        commit: ((historyList: List<String>) -> Unit)?
    ) {
        if (keyword.isNullOrEmpty()) {
            return
        }
        historyList.add(0, keyword)
        updateHistoryList(historyList, commit)
    }

    private fun updateHistoryList(
        historyList: List<String>,
        commit: ((historyList: List<String>) -> Unit)?
    ) {
        this.historyList = historyList.take(SearchLocalService.MAX_HISTORY_COUNT).toMutableList()
        SearchLocalService.saveSearchHistory(this.historyList)
        commit?.invoke(this.historyList)
    }

    override fun observeViewModel() {
        super.observeViewModel()
        isSearching.observe(this) {
            if (it) {
                binding.clHistory.gone()
            }
        }
        isInputEmpty.observe(this) {
            binding.tvSearch.isEnabled = !it
            if (it) {
                binding.clSearchResult.gone()
                if (historyList.isEmpty()) {
                    binding.clHistory.gone()
                } else {
                    binding.clHistory.show()
                }
            }
        }
        searchViewModel.searchRltLD.observeWithoutCache(this) { rlt ->
            isSearching.postValue(false)
            when (rlt) {
                is Rlt.Failed -> {
                    if (rlt.error is NoMoreDataError) {
                        return@observeWithoutCache
                    }
                    binding.clSearchResult.show()
                    binding.rvResult.gone()
                    binding.vErrorView.show(
                        errorEmptyResId = APP_R.drawable.common_list_empty_ic,
                        title = APP_R.string.commonui_list_empty
                    )
                }

                is Rlt.Success -> {
                    binding.clSearchResult.show()
                    binding.rvResult.show()
                    binding.vErrorView.gone()
                }
            }
        }
        searchViewModel.searchResultLD.observeWithoutCache(this) { list ->
            resultAdapter.submitList(list, true)
        }
    }

    private fun hideHistoryList() {
        binding.clHistory.gone()
    }


    override fun goProfile(uid: String) {
        Router.build(this, Profile.UserProfile.PATH)
            .putExtra(Profile.Common.EXTRA_UID, uid)
            .start()
    }

    override fun follow(uid: String) {
        showLoading()
        followViewModel.follow(uid).observe(this) {
            dismissLoading()
            if (it.isSuccess) {
                searchViewModel.notifyFollowChanged(uid, true)
            } else {
                showFailedToast(it)
            }
        }
    }

    override fun unFollow(uid: String) {
        showLoading()
        followViewModel.follow(uid).observe(this) {
            dismissLoading()
            if (it.isSuccess) {
                searchViewModel.notifyFollowChanged(uid, false)
            } else {
                showFailedToast(it)
            }
        }
    }


    override fun onClick(
        item: SearchHistoryItemData,
        pos: Int
    ) {
        binding.etSearchInput.setText(item.keyword)
        search(item.keyword, true)
    }

    override fun onDestroy() {
        super.onDestroy()
        binding.etSearchInput.clearFocus()
        QMUIKeyboardHelper.hideKeyboard(binding.etSearchInput)
    }

}