package com.logseq.app

import android.graphics.Color
import android.text.Editable
import android.text.TextWatcher
import android.view.Gravity
import android.view.KeyEvent
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.TextView
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Circle
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.NavigationBarItemDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.ui.graphics.Color as ComposeColor
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.unit.dp // New Import for DP units
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.doOnNextLayout
import com.getcapacitor.JSArray
import com.getcapacitor.JSObject
import com.getcapacitor.Plugin
import com.getcapacitor.PluginCall
import com.getcapacitor.PluginMethod
import com.getcapacitor.annotation.CapacitorPlugin

// NOTE: NativeUiUtils and MaterialIconResolver are assumed to be defined elsewhere in your project
// and are necessary for this code to compile.

@CapacitorPlugin(name = "LiquidTabsPlugin")
class LiquidTabsPlugin : Plugin() {
    private var bottomNav: ComposeView? = null
    private var searchContainer: LinearLayout? = null
    private var searchInput: EditText? = null
    private var resultsContainer: LinearLayout? = null
    private var closeButton: TextView? = null
    private var originalBottomPadding: Int? = null

    private var tabsState by mutableStateOf<List<TabSpec>>(emptyList())
    private var currentTabId by mutableStateOf<String?>(null)

    // Define a standard horizontal padding for consistency
    private val HORIZONTAL_PADDING_DP = 16f
    private val VERTICAL_PADDING_DP = 12f
    private val RESULT_ROW_VERTICAL_PADDING_DP = 10f

    // 💡 NEW: Define padding for the Tab Bar edges (makes it compact and adds left/right space)
    private val TAB_BAR_HORIZONTAL_PADDING = 12.dp
    private val ACCENT_COLOR_HEX = "#6097c7"

    @PluginMethod
    fun configureTabs(call: PluginCall) {
        val activity = activity ?: run {
            call.reject("No activity")
            return
        }
        val tabs = parseTabs(call.getArray("tabs"))

        activity.runOnUiThread {
            tabsState = tabs
            val activeId = currentTabId?.takeIf { id -> tabs.any { it.id == id } }
                ?: tabs.firstOrNull()?.id
            currentTabId = activeId
            ensureNav()
            currentTabId?.let { id ->
                tabsState.find { it.id == id }?.let { tab ->
                    handleSelection(tab, reselected = false)
                }
            } ?: hideSearchUi()
            adjustWebViewPadding()
            call.resolve()
        }
    }

    @PluginMethod
    fun selectTab(call: PluginCall) {
        val id = call.getString("id") ?: run {
            call.reject("Missing id")
            return
        }
        val tab = tabsState.find { it.id == id }
        if (tab == null) {
            call.resolve()
            return
        }
        val nav = bottomNav
        if (nav == null) {
            call.resolve()
            return
        }
        nav.post {
            val reselected = currentTabId == tab.id
            handleSelection(tab, reselected)
            call.resolve()
        }
    }

    @PluginMethod
    fun updateNativeSearchResults(call: PluginCall) {
        val results = parseResults(call.getArray("results"))
        activity?.runOnUiThread {
            ensureSearchUi()
            val container = resultsContainer ?: return@runOnUiThread
            container.removeAllViews()
            results.forEach { result ->
                container.addView(makeResultRow(result))
            }
            call.resolve()
        } ?: call.resolve()
    }

    /**
     * FIX: Allows the web view to explicitly show the search UI again,
     * typically after backing out of an opened search result item.
     */
    @PluginMethod
    fun showSearchUiNative(call: PluginCall) {
        activity?.runOnUiThread {
            showSearchUi()
            // Ensure padding is correct when search UI is manually shown
            adjustWebViewPadding()
            call.resolve()
        } ?: call.resolve()
    }


    private fun ensureNav(): ComposeView {
        val activity = activity ?: throw IllegalStateException("No activity")
        val root = NativeUiUtils.contentRoot(activity)
        val nav = bottomNav ?: ComposeView(activity).also { view ->
            view.id = R.id.liquid_tabs_bottom_nav // Assuming R.id.liquid_tabs_bottom_nav is defined
            view.setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            view.layoutParams = FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT,
                Gravity.BOTTOM
            )
            view.setBackgroundColor(LogseqTheme.current().background)
            bottomNav = view
            root.addView(view)
            setupImeBehaviorForNav(view)
        }

        nav.setContent {
            BottomNavBar(
                tabs = tabsState,
                currentId = currentTabId,
                onSelect = { tab ->
                    val reselected = tab.id == currentTabId
                    handleSelection(tab, reselected)
                }
            )
        }

        nav.doOnNextLayout { adjustWebViewPadding() }
        return nav
    }

    private fun handleSelection(tab: TabSpec, reselected: Boolean) {
        currentTabId = tab.id
        if (tab.role == "search") {
            showSearchUi()
        } else {
            hideSearchUi()
        }

        notifyListeners("tabSelected", JSObject().put("id", tab.id).put("reselected", reselected))
    }

    private fun adjustWebViewPadding() {
        val webView = bridge.webView ?: return
        val nav = bottomNav ?: return
        if (originalBottomPadding == null) {
            originalBottomPadding = webView.paddingBottom
        }
        nav.post {
            val padding = originalBottomPadding ?: 0
            val h = nav.height
            val newPadding = if (searchContainer?.visibility == View.VISIBLE) {
                padding
            } else {
                padding + h
            }
            webView.setPadding(webView.paddingLeft, webView.paddingTop, webView.paddingRight, newPadding)
        }
    }

    private fun setupImeBehaviorForNav(nav: View) {
        ViewCompat.setOnApplyWindowInsetsListener(nav) { v, insets ->
            val imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime())
            val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())

            val extra = if (imeVisible) {
                imeInsets.bottom
            } else {
                0
            }
            v.translationY = extra.toFloat()
            insets
        }

        ViewCompat.requestApplyInsets(nav)
    }

    private fun ensureSearchUi() {
        if (searchContainer != null) return
        showSearchUi()
    }

    private fun showSearchUi() {
        val activity = activity ?: return
        val root = NativeUiUtils.contentRoot(activity)
        val theme = LogseqTheme.current()
        val labelColor = if (theme.isDark) theme.tint else Color.BLACK
        val secondaryLabelColor =
            if (theme.isDark) Color.argb(200, 245, 247, 250) else Color.DKGRAY

        // Calculate status bar height for safe area padding
        val insets = ViewCompat.getRootWindowInsets(root)
        val statusBarHeight = insets?.getInsets(WindowInsetsCompat.Type.statusBars())?.top ?: 0

        val container = searchContainer ?: LinearLayout(activity).also { layout ->
            layout.orientation = LinearLayout.VERTICAL
            layout.setBackgroundColor(theme.background)

            val lp = FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
            )

            // Set bottom margin to clear the bottom navigation bar
            lp.setMargins(0, 0, 0, bottomNav?.height ?: NativeUiUtils.dp(activity, 56f))

            // Remove elevation/shadow
            layout.elevation = 0f

            // Apply status bar height as top padding for safe area
            layout.setPadding(0, statusBarHeight, 0, 0)

            root.addView(layout, lp)
            searchContainer = layout
        }

        // Re-apply top padding in case insets were not available on first run
        container.setPadding(0, statusBarHeight, 0, 0)

        // Search Input Setup
        if (searchInput == null) {
            // Container for input and close button
            val searchRow = LinearLayout(activity).apply {
                orientation = LinearLayout.HORIZONTAL
                gravity = Gravity.CENTER_VERTICAL // Center items vertically
            }

            val input = EditText(activity).apply {
                hint = "Search"
                setSingleLine(true)
                imeOptions = EditorInfo.IME_ACTION_SEARCH
                setTextColor(labelColor)
                setHintTextColor(secondaryLabelColor)
                // Remove EditText default background/border for a flat look
                setBackgroundColor(Color.TRANSPARENT)

                // Fine-tune padding inside the EditText for text alignment
                setPadding(
                    NativeUiUtils.dp(activity, 0f),
                    NativeUiUtils.dp(activity, 10f),
                    NativeUiUtils.dp(activity, 0f),
                    NativeUiUtils.dp(activity, 10f)
                )

                // Layout params to make EditText take most of the horizontal space
                layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1.0f)

                setOnEditorActionListener { _, actionId, event ->
                    val isEnter =
                        actionId == EditorInfo.IME_ACTION_SEARCH ||
                            actionId == EditorInfo.IME_ACTION_DONE ||
                            actionId == EditorInfo.IME_ACTION_GO ||
                            (event?.keyCode == KeyEvent.KEYCODE_ENTER)
                    if (isEnter) {
                        notifyListeners("keyboardHackKey", JSObject().put("key", "enter"))
                        true
                    } else {
                        false
                    }
                }

                setOnKeyListener { _, keyCode, event ->
                    when (keyCode) {
                        KeyEvent.KEYCODE_ENTER -> {
                            if (event.action == KeyEvent.ACTION_DOWN) {
                                notifyListeners("keyboardHackKey", JSObject().put("key", "enter"))
                            }
                            true
                        }
                        KeyEvent.KEYCODE_DEL -> {
                            if (text.isNullOrEmpty() && event.action == KeyEvent.ACTION_DOWN) {
                                notifyListeners("keyboardHackKey", JSObject().put("key", "backspace"))
                                true
                            } else {
                                false
                            }
                        }
                        else -> false
                    }
                }
                addTextChangedListener(object : TextWatcher {
                    override fun afterTextChanged(s: Editable?) {
                        // Toggle close button visibility based on text
                        val hasText = !s.isNullOrEmpty()
                        closeButton?.visibility = if (hasText) View.VISIBLE else View.GONE
                    }
                    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
                    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                        notifyListeners("searchChanged", JSObject().put("query", s?.toString() ?: ""))
                    }
                })
            }

            // Close Button
            val button = TextView(activity).apply {
                text = "X" // Close icon (using simple 'X')
                setTextColor(secondaryLabelColor)
                textSize = 18f
                gravity = Gravity.CENTER
                setPadding(
                    NativeUiUtils.dp(activity, 8f),
                    NativeUiUtils.dp(activity, 8f),
                    NativeUiUtils.dp(activity, 8f),
                    NativeUiUtils.dp(activity, 8f)
                )
                visibility = View.GONE // Initially hidden

                setOnClickListener {
                    input.setText("") // Clear the EditText
                    // TextWatcher will handle notifying the web view and hiding the button
                }
                // Set layout params for the button
                layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
            }

            // 1. Add EditText
            searchRow.addView(input)
            // 2. Add Close Button
            searchRow.addView(button)

            // inputContainer (was the old search container wrapper)
            val inputContainer = LinearLayout(activity).apply {
                // Add horizontal padding for the search box container
                setPadding(
                    NativeUiUtils.dp(activity, HORIZONTAL_PADDING_DP),
                    NativeUiUtils.dp(activity, VERTICAL_PADDING_DP),
                    NativeUiUtils.dp(activity, HORIZONTAL_PADDING_DP),
                    NativeUiUtils.dp(activity, VERTICAL_PADDING_DP)
                )
                orientation = LinearLayout.VERTICAL
                // Add the new searchRow (input + button)
                addView(searchRow, 0, LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT))

                // Add a divider below the search box (optional visual polish)
                val divider = View(activity).apply {
                    layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, NativeUiUtils.dp(activity, 1f))
                    setBackgroundColor(
                        if (theme.isDark) Color.argb(40, 245, 247, 250) else Color.parseColor("#E0E0E0")
                    )
                }
                addView(divider, LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, NativeUiUtils.dp(activity, 1f)))
            }

            // Insert the inputContainer into the main searchContainer
            container.addView(inputContainer, 0, LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT))
            searchInput = input
            closeButton = button
        }

        // Search Results Setup
        if (resultsContainer == null) {
            val scroll = ScrollView(activity)
            val inner = LinearLayout(activity).apply {
                orientation = LinearLayout.VERTICAL
                // Apply horizontal padding for the list of results
                setPadding(
                    NativeUiUtils.dp(activity, HORIZONTAL_PADDING_DP), // Left
                    NativeUiUtils.dp(activity, 0f),  // Top
                    NativeUiUtils.dp(activity, HORIZONTAL_PADDING_DP), // Right
                    NativeUiUtils.dp(activity, 12f)  // Bottom
                )
            }
            scroll.addView(inner, FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT))
            // The ScrollView should take up the rest of the vertical space
            container.addView(scroll, LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))
            resultsContainer = inner
        }

        container.visibility = View.VISIBLE
    }

    private fun hideSearchUi() {
        searchContainer?.visibility = View.GONE
    }

    private fun makeResultRow(result: SearchResult): View {
        val activity = activity ?: throw IllegalStateException("No activity")
        val theme = LogseqTheme.current()
        val labelColor = if (theme.isDark) theme.tint else Color.BLACK
        val secondaryLabelColor =
            if (theme.isDark) Color.argb(200, 245, 247, 250) else Color.DKGRAY
        return LinearLayout(activity).apply {
            orientation = LinearLayout.VERTICAL

            // Apply vertical padding for the row item, using RESULT_ROW_VERTICAL_PADDING_DP (10f)
            // for both top and bottom to ensure they are equal.
            setPadding(0,
                NativeUiUtils.dp(activity, RESULT_ROW_VERTICAL_PADDING_DP), // TOP: 10f
                0,
                NativeUiUtils.dp(activity, RESULT_ROW_VERTICAL_PADDING_DP)  // BOTTOM: 10f
            )

            val subtitleText = result.subtitle
            if (subtitleText != null &&
                !subtitleText.isNullOrBlank() &&
                subtitleText.lowercase() != "null") {

                val sub = TextView(activity).apply {
                    text = subtitleText
                    setTextColor(secondaryLabelColor)
                    textSize = 13f
                }
                addView(sub)
            }

            val titleView = TextView(activity).apply {
                text = result.title
                setTextColor(labelColor)
                textSize = 15f
            }
            addView(titleView)

            setOnClickListener {
                hideSearchUi()
                notifyListeners("openSearchResultBlock", JSObject().put("id", result.id))
            }
        }
    }

    @Composable
    private fun BottomNavBar(
        tabs: List<TabSpec>,
        currentId: String?,
        onSelect: (TabSpec) -> Unit
    ) {
        val theme by LogseqTheme.colors.collectAsState()
        val container = ComposeColor(theme.background)
        val unselected = if (theme.isDark) ComposeColor(theme.tint).copy(alpha = 0.78f) else ComposeColor.Black.copy(alpha = 0.65f)
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .background(container)
                .padding(horizontal = TAB_BAR_HORIZONTAL_PADDING)
        ) {
            NavigationBar(
                modifier = Modifier.fillMaxWidth(),
                containerColor = container
            ) {
                tabs.forEach { tab ->
                    val selected = tab.id == currentId
                    val icon = remember(tab.systemImage, tab.id) {
                        MaterialIconResolver.resolve(tab.systemImage) ?: MaterialIconResolver.resolve(tab.id)
                    }
                    val accent = ComposeColor(NativeUiUtils.parseColor(ACCENT_COLOR_HEX, Color.parseColor(ACCENT_COLOR_HEX)))

                    NavigationBarItem(
                        selected = selected,
                        onClick = { onSelect(tab) },
                        colors = NavigationBarItemDefaults.colors(
                            selectedIconColor = accent,
                            selectedTextColor = accent,
                            unselectedIconColor = unselected,
                            unselectedTextColor = unselected,
                            indicatorColor = accent.copy(alpha = 0.12f)
                        ),
                        icon = {
                            Icon(
                                imageVector = icon ?: Icons.Filled.Circle,
                                contentDescription = tab.title
                            )
                        },
                        // Slightly reduce the default Material3 gap between icon and label.
                        label = { Text(tab.title, modifier = Modifier.offset(y = (-4).dp)) }
                    )
                }
            }
        }
    }

    private fun parseTabs(array: JSArray?): List<TabSpec> {
        if (array == null) return emptyList()
        val result = mutableListOf<TabSpec>()
        for (i in 0 until array.length()) {
            val obj = array.optJSONObject(i) ?: continue
            val id = obj.optString("id", "")
            if (id.isBlank()) continue
            val title = obj.optString("title", id)
            val systemImage = obj.optString("systemImage", "")
            val role = obj.optString("role", "normal")
            result.add(TabSpec(id, title, systemImage, role))
        }
        return result
    }

    private fun parseResults(array: JSArray?): List<SearchResult> {
        if (array == null) return emptyList()
        val result = mutableListOf<SearchResult>()
        for (i in 0 until array.length()) {
            val obj = array.optJSONObject(i) ?: continue
            val id = obj.optString("id", "")
            if (id.isBlank()) continue
            val title = obj.optString("title", "")
            val subtitle = obj.optString("subtitle", null)
            result.add(SearchResult(id, title, subtitle))
        }
        return result
    }
}

data class TabSpec(
    val id: String,
    val title: String,
    val systemImage: String,
    val role: String
)

data class SearchResult(
    val id: String,
    val title: String,
    val subtitle: String?
)
