/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.focus.session.ui

import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.PopupWindow
import androidx.recyclerview.widget.LinearLayoutManager
import mozilla.components.browser.state.selector.privateTabs
import mozilla.components.browser.state.state.TabSessionState
import org.mozilla.focus.Components
import org.mozilla.focus.GleanMetrics.TabCount
import org.mozilla.focus.databinding.PopupTabsBinding
import org.mozilla.focus.state.AppAction

/**
 * A [PopupWindow] that displays a list of open tabs, allowing the user to switch between,
 * close, or create new browser sessions.
 *
 * @param parentView The [ViewGroup] used to provide context for layout inflation.
 * @param components The app's [Components] instance used to access state, use cases, and dispatch actions.
 */
class TabsPopup(
    private val parentView: ViewGroup,
    private val components: Components,
) : PopupWindow() {
    private lateinit var binding: PopupTabsBinding

    init {
        initializeLayout()
    }

    private fun initializeLayout() {
        val layoutInflater =
            parentView.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
        binding = PopupTabsBinding.inflate(layoutInflater, parentView, false)
        val sessionsAdapter = TabsAdapter(
            tabList = components.store.state.privateTabs,
            isCurrentSession = { tab -> isCurrentSession(tab) },
            selectSession = { tab -> selectSession(tab) },
            closeSession = { tab -> closeSession(tab) },
            closeOtherSessions = { closeOtherSessions() },
            addNewTab = { addNewTab() },
        )

        binding.sessions.apply {
            adapter = sessionsAdapter
            layoutManager = LinearLayoutManager(parentView.context)
            setHasFixedSize(true)
        }
        contentView = binding.root
        isFocusable = true
        width = FrameLayout.LayoutParams.WRAP_CONTENT
        height = FrameLayout.LayoutParams.WRAP_CONTENT
        animationStyle = android.R.style.Animation_Dialog
        binding.root.setOnClickListener { dismiss() }
    }

    override fun dismiss() {
        super.dismiss()
        val openedTabs = components.store.state.tabs.size
        TabCount.sessionListClosed.record(TabCount.SessionListClosedExtra(openedTabs))

        components.appStore.dispatch(AppAction.HideTabs)
    }

    private fun isCurrentSession(tab: TabSessionState): Boolean {
        return components.store.state.selectedTabId == tab.id
    }

    private fun selectSession(tab: TabSessionState) {
        components.tabsUseCases.selectTab.invoke(tab.id)
        val openedTabs = components.store.state.tabs.size
        TabCount.sessionListItemTapped.record(TabCount.SessionListItemTappedExtra(openedTabs))
        dismiss()
    }

    private fun closeSession(tab: TabSessionState) {
        components.tabsUseCases.removeTab.invoke(tab.id, selectParentIfExists = false)
        val openedTabs = components.store.state.tabs.size
        TabCount.sessionListItemTapped.record(TabCount.SessionListItemTappedExtra(openedTabs))
        dismiss()
    }

    private fun closeOtherSessions() {
        val openedTabsIDs = components.store.state.tabs.map { it.id }
        val tabsToDelete = openedTabsIDs.filter { it != components.store.state.selectedTabId }
        components.tabsUseCases.removeTabs.invoke(tabsToDelete)
        TabCount.sessionCloseOthersTapped.record(TabCount.SessionCloseOthersTappedExtra(tabsToDelete.size))
        dismiss()
    }

    private fun addNewTab() {
        val openedTabs = components.store.state.tabs.size

        val tabId = components.tabsUseCases.addTab(url = "", private = true)

        components.appStore.dispatch(AppAction.EditAction(tabId))

        TabCount.sessionAddNewTabTapped.record(TabCount.SessionAddNewTabTappedExtra(openedTabs))
        dismiss()
    }
}
