/* 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.fenix.settings.wallpaper

import android.annotation.SuppressLint
import android.graphics.Bitmap
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.SemanticsPropertyReceiver
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.onClick
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextDecoration
import androidx.core.graphics.createBitmap
import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview
import mozilla.components.compose.base.modifier.debouncedClickable
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.ClickableSubstringLink
import org.mozilla.fenix.theme.FirefoxTheme
import org.mozilla.fenix.theme.Theme
import org.mozilla.fenix.wallpapers.Wallpaper

/**
 * The screen for controlling settings around Wallpapers. When a new wallpaper is selected,
 * a snackbar will be displayed.
 *
 * @param wallpaperGroups Wallpapers groups to add to grid.
 * @param defaultWallpaper The default wallpaper.
 * @param loadWallpaperResource Callback to handle loading a wallpaper bitmap. Only optional in the default case.
 * @param selectedWallpaper The currently selected wallpaper.
 * @param onSelectWallpaper Callback for when a new wallpaper is selected.
 * @param onLearnMoreClick Callback for when the learn more action is clicked from the group description.
 * Parameters are the URL that is clicked and the name of the collection.
 */
@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
@Composable
fun WallpaperSettings(
    wallpaperGroups: Map<Wallpaper.Collection, List<Wallpaper>>,
    defaultWallpaper: Wallpaper,
    loadWallpaperResource: suspend (Wallpaper) -> Bitmap?,
    selectedWallpaper: Wallpaper,
    onSelectWallpaper: (Wallpaper) -> Unit,
    onLearnMoreClick: (String, String) -> Unit,
) {
    Surface {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .verticalScroll(rememberScrollState())
                .padding(horizontal = FirefoxTheme.layout.space.dynamic400),
        ) {
            Spacer(modifier = Modifier.height(FirefoxTheme.layout.space.dynamic400))

            wallpaperGroups.forEach { (collection, wallpapers) ->
                if (wallpapers.isNotEmpty()) {
                    WallpaperGroupHeading(
                        collection = collection,
                        onLearnMoreClick = onLearnMoreClick,
                    )

                    Spacer(modifier = Modifier.height(FirefoxTheme.layout.space.dynamic400))

                    WallpaperThumbnails(
                        wallpapers = wallpapers,
                        defaultWallpaper = defaultWallpaper,
                        loadWallpaperResource = loadWallpaperResource,
                        selectedWallpaper = selectedWallpaper,
                        onSelectWallpaper = onSelectWallpaper,
                    )

                    Spacer(modifier = Modifier.height(FirefoxTheme.layout.space.dynamic400))
                }
            }
        }
    }
}

@Composable
@Suppress("Deprecation")
private fun WallpaperGroupHeading(
    collection: Wallpaper.Collection,
    onLearnMoreClick: (String, String) -> Unit,
) {
    // Since the last new collection of wallpapers was tied directly to an MR release,
    // it was decided that we should use string resources for these titles
    // and descriptions so they could be localized.
    // In the future, we may want to either use the dynamic wallpaper properties with localized fallbacks
    // or invest in a method of localizing the remote strings themselves.
    if (collection.name == Wallpaper.CLASSIC_FIREFOX_COLLECTION) {
        Text(
            text = stringResource(R.string.wallpaper_classic_title, stringResource(R.string.firefox)),
            color = MaterialTheme.colorScheme.onSurfaceVariant,
            style = FirefoxTheme.typography.subtitle2,
        )
    } else {
        val label = stringResource(id = R.string.a11y_action_label_wallpaper_collection_learn_more)
        val headingSemantics: SemanticsPropertyReceiver.() -> Unit =
            if (collection.learnMoreUrl.isNullOrEmpty()) {
                {}
            } else {
                {
                    role = Role.Button
                    onClick(label = label) {
                        onLearnMoreClick(collection.learnMoreUrl, collection.name)
                        false
                    }
                }
            }
        Column(
            modifier = Modifier.semantics(mergeDescendants = true, properties = headingSemantics),
        ) {
            Text(
                text = stringResource(R.string.wallpaper_artist_series_title),
                color = MaterialTheme.colorScheme.onSurfaceVariant,
                style = FirefoxTheme.typography.subtitle2,
            )

            if (collection.learnMoreUrl.isNullOrEmpty()) {
                val text = stringResource(R.string.wallpaper_artist_series_description)
                Text(
                    text = text,
                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                    style = FirefoxTheme.typography.caption,
                )
            } else {
                val link = stringResource(R.string.wallpaper_learn_more)
                val text = stringResource(R.string.wallpaper_artist_series_description_with_learn_more, link)
                val linkStartIndex = text.indexOf(link)
                val linkEndIndex = linkStartIndex + link.length

                ClickableSubstringLink(
                    text = text,
                    textColor = MaterialTheme.colorScheme.onSurfaceVariant,
                    linkTextColor = MaterialTheme.colorScheme.tertiary,
                    linkTextDecoration = TextDecoration.Underline,
                    clickableStartIndex = linkStartIndex,
                    clickableEndIndex = linkEndIndex,
                ) {
                    onLearnMoreClick(collection.learnMoreUrl, collection.name)
                }
            }
        }
    }
}

/**
 * A grid of selectable wallpaper thumbnails.
 *
 * @param wallpapers Wallpapers to add to grid.
 * @param defaultWallpaper The default wallpaper.
 * @param selectedWallpaper The currently selected wallpaper.
 * @param loadWallpaperResource Callback to handle loading a wallpaper bitmap. Only optional in the default case.
 * @param onSelectWallpaper Action to take when a new wallpaper is selected.
 */
@Composable
fun WallpaperThumbnails(
    wallpapers: List<Wallpaper>,
    defaultWallpaper: Wallpaper,
    selectedWallpaper: Wallpaper,
    loadWallpaperResource: suspend (Wallpaper) -> Bitmap?,
    onSelectWallpaper: (Wallpaper) -> Unit,
) {
    FlowRow(
        horizontalArrangement = Arrangement.spacedBy(FirefoxTheme.layout.space.dynamic150),
        verticalArrangement = Arrangement.spacedBy(FirefoxTheme.layout.space.dynamic150),
    ) {
        wallpapers.forEach { wallpaper ->
            WallpaperThumbnailItem(
                wallpaper = wallpaper,
                defaultWallpaper = defaultWallpaper,
                loadWallpaperResource = loadWallpaperResource,
                isSelected = selectedWallpaper.name == wallpaper.name,
                isLoading = wallpaper.assetsFileState == Wallpaper.ImageFileState.Downloading,
                onSelect = onSelectWallpaper,
            )
        }
    }
}

/**
 * A single wallpaper thumbnail.
 *
 * @param wallpaper The wallpaper to display.
 * @param defaultWallpaper The default wallpaper.
 * @param loadWallpaperResource Callback to handle loading a wallpaper bitmap.
 * @param isSelected Whether the wallpaper is currently selected.
 * @param isLoading Whether the wallpaper is currently downloading.
 * @param aspectRatio The ratio of height to width of the thumbnail.
 * @param loadingOpacity Opacity of the currently downloading wallpaper.
 * @param onSelect Action to take when a new wallpaper is selected.
 */
@Composable
@Suppress("CognitiveComplexMethod")
private fun WallpaperThumbnailItem(
    wallpaper: Wallpaper,
    defaultWallpaper: Wallpaper,
    loadWallpaperResource: suspend (Wallpaper) -> Bitmap?,
    isSelected: Boolean,
    isLoading: Boolean,
    aspectRatio: Float = 1.1f,
    loadingOpacity: Float = 0.5f,
    onSelect: (Wallpaper) -> Unit,
) {
    var bitmap: Bitmap? by remember { mutableStateOf(null) }
    LaunchedEffect(LocalConfiguration.current.orientation) {
        bitmap = loadWallpaperResource(wallpaper)
    }
    val border = if (isSelected) {
        BorderStroke(
            width = FirefoxTheme.layout.border.thick,
            color = MaterialTheme.colorScheme.primary,
        )
    } else if (wallpaper.name == Wallpaper.DEFAULT) {
        BorderStroke(
            width = FirefoxTheme.layout.border.thick,
            color = MaterialTheme.colorScheme.outlineVariant,
        )
    } else {
        null
    }

    // Completely avoid drawing the item if a bitmap cannot be loaded and is required
    if (bitmap != null || wallpaper == defaultWallpaper) {
        val description = stringResource(
            R.string.wallpapers_item_name_content_description,
            wallpaper.name,
        )

        // For the default wallpaper to be accessible, we should set the content description for
        // the Surface instead of the thumbnail image
        val contentDescriptionModifier = if (bitmap == null) {
            Modifier.semantics {
                contentDescription = description
            }
        } else {
            Modifier
        }

        Surface(
            modifier = Modifier
                .width(width = FirefoxTheme.layout.size.static1200)
                .aspectRatio(aspectRatio)
                .debouncedClickable { onSelect(wallpaper) }
                .then(contentDescriptionModifier),
            shape = RoundedCornerShape(size = FirefoxTheme.layout.corner.large),
            border = border,
            shadowElevation = FirefoxTheme.layout.elevation.medium,
        ) {
            if (bitmap == null) {
                Spacer(
                    modifier = Modifier.fillMaxSize(),
                )
            } else {
                bitmap?.let {
                    Image(
                        bitmap = it.asImageBitmap(),
                        contentScale = ContentScale.FillBounds,
                        contentDescription = description,
                        modifier = Modifier.fillMaxSize(),
                        alpha = if (isLoading) loadingOpacity else 1.0f,
                    )
                }
            }

            if (isLoading) {
                Box(
                    modifier = Modifier.fillMaxSize(),
                    contentAlignment = Alignment.Center,
                ) {
                    CircularProgressIndicator(
                        modifier = Modifier.size(FirefoxTheme.layout.size.circularIndicatorDiameter),
                        color = MaterialTheme.colorScheme.primary,
                    )
                }
            }
        }
    }
}

@FlexibleWindowLightDarkPreview
@Composable
@Suppress("MagicNumber")
private fun WallpaperThumbnailsPreview() {
    val downloadedCollection: List<Wallpaper> = List(size = 5) { index ->
        Wallpaper(
            name = "downloaded wallpaper $index",
            textColor = 0L,
            cardColorLight = 0L,
            cardColorDark = 0L,
            thumbnailFileState = Wallpaper.ImageFileState.Downloaded,
            assetsFileState = Wallpaper.ImageFileState.Downloaded,
            collection = Wallpaper.ClassicFirefoxCollection,
        )
    } + Wallpaper.Default
    val downloadingCollection: List<Wallpaper> = List(size = 5) { index ->
        Wallpaper(
            name = "downloading wallpaper $index",
            textColor = 0L,
            cardColorLight = 0L,
            cardColorDark = 0L,
            thumbnailFileState = Wallpaper.ImageFileState.Downloading,
            assetsFileState = Wallpaper.ImageFileState.Downloading,
            collection = Wallpaper.ClassicFirefoxCollection,
        )
    }
    var selectedWallpaper by remember { mutableStateOf(downloadedCollection[0]) }

    FirefoxTheme {
        WallpaperSettings(
            defaultWallpaper = Wallpaper.Default,
            loadWallpaperResource = { wallpaper ->
                if (wallpaper == Wallpaper.Default) {
                    null
                } else {
                    createBitmap(100, 100, Bitmap.Config.ARGB_8888)
                }
            },
            wallpaperGroups = mapOf(
                Wallpaper.DefaultCollection to downloadedCollection,
                Wallpaper.ClassicFirefoxCollection to downloadingCollection,
            ),
            selectedWallpaper = selectedWallpaper,
            onSelectWallpaper = { wallpaper ->
                selectedWallpaper = wallpaper
            },
            onLearnMoreClick = { _, _ -> },
        )
    }
}

@FlexibleWindowLightDarkPreview
@Composable
@Suppress("MagicNumber")
private fun WallpaperThumbnailsPrivatePreview() {
    val downloadedCollection: List<Wallpaper> = List(size = 5) { index ->
        Wallpaper(
            name = "downloaded wallpaper $index",
            textColor = 0L,
            cardColorLight = 0L,
            cardColorDark = 0L,
            thumbnailFileState = Wallpaper.ImageFileState.Downloaded,
            assetsFileState = Wallpaper.ImageFileState.Downloaded,
            collection = Wallpaper.ClassicFirefoxCollection,
        )
    } + Wallpaper.Default
    val downloadingCollection: List<Wallpaper> = List(size = 5) { index ->
        Wallpaper(
            name = "downloading wallpaper $index",
            textColor = 0L,
            cardColorLight = 0L,
            cardColorDark = 0L,
            thumbnailFileState = Wallpaper.ImageFileState.Downloading,
            assetsFileState = Wallpaper.ImageFileState.Downloading,
            collection = Wallpaper.ClassicFirefoxCollection,
        )
    }
    var selectedWallpaper by remember { mutableStateOf(downloadedCollection[0]) }

    FirefoxTheme(theme = Theme.Private) {
        WallpaperSettings(
            defaultWallpaper = Wallpaper.Default,
            loadWallpaperResource = { wallpaper ->
                if (wallpaper == Wallpaper.Default) {
                    null
                } else {
                    createBitmap(100, 100, Bitmap.Config.ARGB_8888)
                }
            },
            wallpaperGroups = mapOf(
                Wallpaper.DefaultCollection to downloadedCollection,
                Wallpaper.ClassicFirefoxCollection to downloadingCollection,
            ),
            selectedWallpaper = selectedWallpaper,
            onSelectWallpaper = { wallpaper ->
                selectedWallpaper = wallpaper
            },
            onLearnMoreClick = { _, _ -> },
        )
    }
}
