package com.swmansion.rnscreens

import android.annotation.SuppressLint
import android.text.InputType
import androidx.appcompat.widget.SearchView
import com.facebook.react.bridge.ReactContext
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.uimanager.events.Event
import com.facebook.react.uimanager.events.EventDispatcher
import com.facebook.react.views.view.ReactViewGroup
import com.swmansion.rnscreens.events.SearchBarBlurEvent
import com.swmansion.rnscreens.events.SearchBarChangeTextEvent
import com.swmansion.rnscreens.events.SearchBarCloseEvent
import com.swmansion.rnscreens.events.SearchBarFocusEvent
import com.swmansion.rnscreens.events.SearchBarOpenEvent
import com.swmansion.rnscreens.events.SearchBarSearchButtonPressEvent

@SuppressLint("ViewConstructor")
class SearchBarView(
    reactContext: ReactContext?,
) : ReactViewGroup(reactContext) {
    var inputType: SearchBarInputTypes = SearchBarInputTypes.TEXT
    var autoCapitalize: SearchBarAutoCapitalize = SearchBarAutoCapitalize.NONE
    var textColor: Int? = null
    var tintColor: Int? = null
    var headerIconColor: Int? = null
    var hintTextColor: Int? = null
    var placeholder: String = ""
    var shouldOverrideBackButton: Boolean = true
    var autoFocus: Boolean = false
    var shouldShowHintSearchIcon: Boolean = true

    private var searchViewFormatter: SearchViewFormatter? = null

    private var areListenersSet: Boolean = false

    private val headerConfig: ScreenStackHeaderConfig?
        get() {
            val currentParent = parent
            if (currentParent is ScreenStackHeaderSubview) {
                return currentParent.config
            }

            return null
        }

    private val screenStackFragment: ScreenStackFragment?
        get() = headerConfig?.screenFragment

    fun onUpdate() {
        setSearchViewProps()
    }

    private fun setSearchViewProps() {
        val searchView = screenStackFragment?.searchView
        if (searchView != null) {
            if (!areListenersSet) {
                setSearchViewListeners(searchView)
                areListenersSet = true
            }

            searchView.inputType = inputType.toAndroidInputType(autoCapitalize)
            searchViewFormatter?.setTextColor(textColor)
            searchViewFormatter?.setTintColor(tintColor)
            searchViewFormatter?.setHeaderIconColor(headerIconColor)
            searchViewFormatter?.setHintTextColor(hintTextColor)
            searchViewFormatter?.setPlaceholder(placeholder, shouldShowHintSearchIcon)
            searchView.overrideBackAction = shouldOverrideBackButton
        }
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()

        screenStackFragment?.onSearchViewCreate = { newSearchView ->
            if (searchViewFormatter == null) {
                searchViewFormatter =
                    SearchViewFormatter(newSearchView)
            }
            setSearchViewProps()
            if (autoFocus) {
                screenStackFragment?.searchView?.focus()
            }
        }
    }

    private fun setSearchViewListeners(searchView: SearchView) {
        searchView.setOnQueryTextListener(
            object : SearchView.OnQueryTextListener {
                override fun onQueryTextChange(newText: String?): Boolean {
                    handleTextChange(newText)
                    return true
                }

                override fun onQueryTextSubmit(query: String?): Boolean {
                    handleTextSubmit(query)
                    return true
                }
            },
        )
        searchView.setOnQueryTextFocusChangeListener { _, hasFocus ->
            handleFocusChange(hasFocus)
        }
        searchView.setOnCloseListener {
            handleClose()
            false
        }
        searchView.setOnSearchClickListener {
            handleOpen()
        }
    }

    private fun handleTextChange(newText: String?) {
        sendEvent(SearchBarChangeTextEvent(surfaceId, id, newText))
    }

    private fun handleFocusChange(hasFocus: Boolean) {
        sendEvent(if (hasFocus) SearchBarFocusEvent(surfaceId, id) else SearchBarBlurEvent(surfaceId, id))
    }

    private fun handleClose() {
        sendEvent(SearchBarCloseEvent(surfaceId, id))
        setToolbarElementsVisibility(VISIBLE)
    }

    private fun handleOpen() {
        sendEvent(SearchBarOpenEvent(surfaceId, id))
        setToolbarElementsVisibility(GONE)
    }

    private fun handleTextSubmit(newText: String?) {
        sendEvent(SearchBarSearchButtonPressEvent(surfaceId, id, newText))
    }

    private fun sendEvent(event: Event<*>) {
        val eventDispatcher: EventDispatcher? =
            UIManagerHelper.getEventDispatcherForReactTag(context as ReactContext, id)
        eventDispatcher?.dispatchEvent(event)
    }

    fun handleClearTextJsRequest() {
        screenStackFragment?.searchView?.clearText()
    }

    fun handleFocusJsRequest() {
        screenStackFragment?.searchView?.focus()
    }

    fun handleBlurJsRequest() {
        screenStackFragment?.searchView?.clearFocus()
    }

    fun handleToggleCancelButtonJsRequest(flag: Boolean) = Unit

    fun handleSetTextJsRequest(text: String?) {
        text?.let { screenStackFragment?.searchView?.setText(it) }
    }

    fun handleCancelSearchJsRequest() {
        screenStackFragment?.searchView?.cancelSearch()
    }

    private fun setToolbarElementsVisibility(visibility: Int) {
        for (i in 0..(headerConfig?.configSubviewsCount?.minus(1) ?: 0)) {
            val subview = headerConfig?.getConfigSubview(i)
            if (subview?.type != ScreenStackHeaderSubview.Type.SEARCH_BAR) {
                subview?.visibility = visibility
            }
        }
    }

    private val surfaceId = UIManagerHelper.getSurfaceId(this)

    enum class SearchBarAutoCapitalize {
        NONE,
        WORDS,
        SENTENCES,
        CHARACTERS,
    }

    enum class SearchBarInputTypes {
        TEXT {
            override fun toAndroidInputType(capitalize: SearchBarAutoCapitalize) =
                when (capitalize) {
                    SearchBarAutoCapitalize.NONE -> InputType.TYPE_CLASS_TEXT
                    SearchBarAutoCapitalize.WORDS -> InputType.TYPE_TEXT_FLAG_CAP_WORDS
                    SearchBarAutoCapitalize.SENTENCES -> InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
                    SearchBarAutoCapitalize.CHARACTERS -> InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS
                }
        },
        PHONE {
            override fun toAndroidInputType(capitalize: SearchBarAutoCapitalize) = InputType.TYPE_CLASS_PHONE
        },
        NUMBER {
            override fun toAndroidInputType(capitalize: SearchBarAutoCapitalize) = InputType.TYPE_CLASS_NUMBER
        },
        EMAIL {
            override fun toAndroidInputType(capitalize: SearchBarAutoCapitalize) = InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
        }, ;

        abstract fun toAndroidInputType(capitalize: SearchBarAutoCapitalize): Int
    }
}
