/* 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/. */

@file:Suppress("TooManyFunctions")

package org.mozilla.fenix.tabstray.ui.syncedtabs

import android.content.res.Configuration
import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import mozilla.components.browser.storage.sync.TabEntry
import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview
import mozilla.components.compose.base.button.FilledButton
import mozilla.components.compose.base.modifier.dashedBorder
import mozilla.components.feature.syncedtabs.view.SyncedTabsView
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.list.ExpandableListHeader
import org.mozilla.fenix.tabstray.TabsTrayTestTag
import org.mozilla.fenix.tabstray.syncedtabs.SyncedTabsListItem
import org.mozilla.fenix.tabstray.ui.tabitems.BasicTabListItem
import org.mozilla.fenix.theme.FirefoxTheme
import mozilla.components.browser.storage.sync.Tab as SyncTab
import mozilla.components.ui.icons.R as iconsR

private const val EXPANDED_BY_DEFAULT = true
private val CardRoundedCornerShape = RoundedCornerShape(12.dp)

/**
 * A lambda invoked when the user clicks on a synced tab in the [SyncedTabsList].
 */
typealias OnTabClick = (tab: SyncTab) -> Unit

/**
 * A lambda invoked when the user clicks a synced tab's close button in the [SyncedTabsList].
 */
typealias OnTabCloseClick = (deviceId: String, tab: SyncTab) -> Unit

/**
 * Top-level list UI for displaying Synced Tabs in the Tabs Tray.
 *
 * @param syncedTabs The tab UI items to be displayed.
 * @param onTabClick The lambda for handling clicks on synced tabs.
 * @param onTabCloseClick The lambda for handling clicks on a synced tab's close button.
 */
@SuppressWarnings("LongMethod", "CognitiveComplexMethod")
@Composable
fun SyncedTabsList(
    syncedTabs: List<SyncedTabsListItem>,
    onTabClick: OnTabClick,
    onTabCloseClick: OnTabCloseClick,
) {
    val listState = rememberLazyListState()
    val expandedState =
        remember(syncedTabs) { syncedTabs.map { EXPANDED_BY_DEFAULT }.toMutableStateList() }

    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.TopCenter,
    ) {
        LazyColumn(
            modifier = Modifier
                .fillMaxHeight()
                .width(FirefoxTheme.layout.size.containerMaxWidth)
                .testTag(TabsTrayTestTag.SYNCED_TABS_LIST),
            state = listState,
            verticalArrangement = Arrangement.spacedBy(8.dp),
        ) {
            syncedTabs.forEachIndexed { index, syncedTabItem ->
                when (syncedTabItem) {
                    is SyncedTabsListItem.DeviceSection -> {
                        val sectionExpanded = expandedState[index]

                        item(key = "header-${syncedTabItem.displayName}-$index") {
                            SyncedTabsSectionHeader(
                                headerText = syncedTabItem.displayName,
                                expanded = sectionExpanded,
                            ) {
                                expandedState[index] = !sectionExpanded
                            }
                        }

                        if (sectionExpanded) {
                            if (syncedTabItem.tabs.isNotEmpty()) {
                                item(key = "card-${syncedTabItem.displayName}-$index") {
                                    Card(
                                        modifier = Modifier
                                            .fillMaxWidth()
                                            .padding(horizontal = 16.dp),
                                        shape = CardRoundedCornerShape,
                                        colors = CardDefaults.cardColors(
                                            containerColor = MaterialTheme.colorScheme.surfaceContainerLowest,
                                        ),
                                    ) {
                                        syncedTabItem.tabs.forEachIndexed { index, syncedTab ->
                                            val isLast = index == syncedTabItem.tabs.lastIndex
                                            when (syncedTab.action) {
                                                is SyncedTabsListItem.Tab.Action.Close -> BasicTabListItem(
                                                    title = syncedTab.displayTitle,
                                                    url = syncedTab.displayURL,
                                                    faviconShape = CircleShape,
                                                    showCloseButton = true,
                                                    onClick = { onTabClick(syncedTab.tab) },
                                                    onCloseButtonClick = {
                                                        onTabCloseClick(
                                                            syncedTab.action.deviceId,
                                                            syncedTab.tab,
                                                        )
                                                    },
                                                )
                                                is SyncedTabsListItem.Tab.Action.None -> BasicTabListItem(
                                                    title = syncedTab.displayTitle,
                                                    url = syncedTab.displayURL,
                                                    modifier = Modifier.padding(vertical = 2.dp),
                                                    faviconShape = CircleShape,
                                                    onClick = { onTabClick(syncedTab.tab) },
                                                )
                                            }
                                            if (!isLast) {
                                                HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant)
                                            }
                                        }
                                    }
                                }
                            } else {
                                item(key = "no-tabs-${syncedTabItem.displayName}-$index") { SyncedTabsNoTabsItem() }
                            }
                        }
                    }

                    is SyncedTabsListItem.Error -> {
                        item(key = "error-${syncedTabItem.errorText}-$index") {
                            SyncedTabsErrorItem(
                                errorText = syncedTabItem.errorText,
                                errorButton = syncedTabItem.errorButton,
                            )
                        }
                    }

                    else -> {
                        // no-op
                    }
                }
            }

            item(key = "footer-spacer") {
                // The Spacer here is to act as a footer to add padding to the bottom of the list so
                // the FAB or any potential SnackBar doesn't overlap with the items at the end.
                Spacer(modifier = Modifier.height(240.dp))
            }
        }
    }
}

/**
 * Collapsible header for sections of synced tabs
 *
 * @param headerText The section title for a group of synced tabs.
 * @param expanded Indicates whether the section of content is expanded. If null, the Icon will be hidden.
 * @param onClick Optional lambda for handling section header clicks.
 */
@Composable
fun SyncedTabsSectionHeader(
    headerText: String,
    expanded: Boolean? = null,
    onClick: () -> Unit = {},
) {
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .padding(horizontal = 16.dp),
    ) {
        ExpandableListHeader(
            headerText = headerText,
            headerTextStyle = MaterialTheme.typography.bodyMedium,
            headerTextColor = MaterialTheme.colorScheme.secondary,
            expanded = expanded,
            expandActionContentDescription = stringResource(R.string.synced_tabs_expand_group),
            collapseActionContentDescription = stringResource(R.string.synced_tabs_collapse_group),
            onClick = onClick,
        )
    }
}

/**
 * Error UI to show if there is one of the errors outlined in [SyncedTabsView.ErrorType].
 *
 * @param errorText The text to be displayed to the user.
 * @param errorButton Optional class to set up and handle any clicks in the Error UI.
 */
@Composable
private fun SyncedTabsErrorItem(
    errorText: String,
    errorButton: SyncedTabsListItem.ErrorButton? = null,
) {
    Box(
        Modifier
            .padding(all = 8.dp)
            .height(IntrinsicSize.Min)
            .dashedBorder(
                color = MaterialTheme.colorScheme.outlineVariant,
                cornerRadius = 8.dp,
                dashHeight = 2.dp,
                dashWidth = 4.dp,
            ),
    ) {
        Column(
            Modifier
                .padding(all = 16.dp)
                .fillMaxWidth(),
        ) {
            Text(
                text = errorText,
                color = MaterialTheme.colorScheme.onErrorContainer,
                modifier = Modifier.fillMaxWidth(),
                fontSize = 14.sp,
            )

            errorButton?.let {
                Spacer(modifier = Modifier.height(12.dp))

                FilledButton(
                    text = it.buttonText,
                    modifier = Modifier.fillMaxWidth(),
                    icon = painterResource(iconsR.drawable.mozac_ic_avatar_circle_fill_24),
                    onClick = it.onClick,
                )
            }
        }
    }
}

/**
 * UI to be displayed when a user's device has no synced tabs.
 */
@Composable
private fun SyncedTabsNoTabsItem() {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .padding(horizontal = 16.dp),
        shape = CardRoundedCornerShape,
        colors = CardDefaults.cardColors(
            containerColor = MaterialTheme.colorScheme.surfaceContainerLowest,
        ),
    ) {
        Text(
            text = stringResource(R.string.synced_tabs_no_open_tabs),
            color = MaterialTheme.colorScheme.secondary,
            style = FirefoxTheme.typography.body1,
            modifier = Modifier
                .padding(all = 16.dp)
                .fillMaxWidth(),
        )
    }
}

@Composable
@FlexibleWindowLightDarkPreview
private fun SyncedTabsListItemsPreview() {
    FirefoxTheme {
        Surface {
            SyncedTabsSectionHeader(headerText = "Google Pixel Pro Max +Ultra 5000")

            Spacer(modifier = Modifier.height(16.dp))

            SyncedTabsSectionHeader(
                headerText = "Collapsible Google Pixel Pro Max +Ultra 5000",
                expanded = true,
            ) { println("Clicked section header") }

            Spacer(modifier = Modifier.height(16.dp))

            BasicTabListItem(
                title = "Mozilla",
                url = "www.mozilla.org",
                onClick = {},
            )

            Spacer(modifier = Modifier.height(16.dp))

            SyncedTabsErrorItem(errorText = stringResource(R.string.synced_tabs_reauth))

            Spacer(modifier = Modifier.height(16.dp))

            SyncedTabsNoTabsItem()

            Spacer(modifier = Modifier.height(16.dp))
        }
    }
}

@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun SyncedTabsErrorPreview() {
    FirefoxTheme {
        Surface {
            SyncedTabsErrorItem(
                errorText = stringResource(R.string.synced_tabs_no_tabs),
                errorButton = SyncedTabsListItem.ErrorButton(
                    buttonText = stringResource(R.string.synced_tabs_sign_in_button),
                ) {
                    println("SyncedTabsErrorButton click")
                },
            )
        }
    }
}

@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun SyncedTabsListPreview() {
    FirefoxTheme {
        Surface {
            SyncedTabsList(
                syncedTabs = getFakeSyncedTabList(),
                onTabClick = { println("Tab clicked") },
                onTabCloseClick = { _, _ -> println("Tab closed") },
            )
        }
    }
}

/**
 * Helper function to create a List of [SyncedTabsListItem] for previewing.
 */
@VisibleForTesting
internal fun getFakeSyncedTabList(): List<SyncedTabsListItem> = listOf(
    SyncedTabsListItem.DeviceSection(
        displayName = "Device 1",
        tabs = listOf(
            generateFakeTab("Mozilla", "www.mozilla.org"),
            generateFakeTab("Google", "www.google.com"),
            generateFakeTab("", "www.google.com"),
        ),
    ),
    SyncedTabsListItem.DeviceSection(
        displayName = "Device 2",
        tabs = listOf(
            generateFakeTab("Firefox", "www.getfirefox.org", SyncedTabsListItem.Tab.Action.Close("device2222")),
            generateFakeTab("Thunderbird", "www.getthunderbird.org", SyncedTabsListItem.Tab.Action.Close("device2222")),
        ),
    ),
    SyncedTabsListItem.DeviceSection("Device 3", emptyList()),
    SyncedTabsListItem.Error("Please re-authenticate"),
)

/**
 * Helper function to create a [SyncedTabsListItem.Tab] for previewing.
 */
private fun generateFakeTab(
    tabName: String,
    tabUrl: String,
    action: SyncedTabsListItem.Tab.Action = SyncedTabsListItem.Tab.Action.None,
): SyncedTabsListItem.Tab =
    SyncedTabsListItem.Tab(
        tabName.ifEmpty { tabUrl },
        tabUrl,
        action,
        SyncTab(
            history = listOf(TabEntry(tabName, tabUrl, null)),
            active = 0,
            lastUsed = 0L,
            inactive = false,
        ),
    )
