package hu.bbara.purefin.ui.screen.home.components.search

import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionScope
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowBack
import androidx.compose.material.icons.outlined.Check
import androidx.compose.material.icons.outlined.Close
import androidx.compose.material.icons.outlined.Search
import androidx.compose.material.icons.outlined.Tune
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilterChip
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
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.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import hu.bbara.purefin.core.feature.search.SearchResult
import hu.bbara.purefin.core.feature.search.SearchViewModel
import hu.bbara.purefin.model.Genre
import hu.bbara.purefin.model.MediaKind
import hu.bbara.purefin.navigation.HOME_SEARCH_SHARED_BOUNDS_KEY
import hu.bbara.purefin.navigation.LocalNavSharedAnimatedVisibilityScope
import hu.bbara.purefin.navigation.LocalSharedTransitionScope
import hu.bbara.purefin.ui.screen.home.components.DefaultTopBar
import hu.bbara.purefin.ui.screen.home.components.DefaultTopBarIconButton
import hu.bbara.purefin.ui.screen.home.components.rememberDefaultTopBarScrollBehavior
import hu.bbara.purefin.ui.theme.AppTheme
import java.util.UUID

@OptIn(ExperimentalLayoutApi::class, ExperimentalSharedTransitionApi::class)
@Composable
fun SearchScreen(
    viewModel: SearchViewModel = hiltViewModel(),
    modifier: Modifier = Modifier
) {
    val sharedTransitionScope = LocalSharedTransitionScope.current
    val animatedVisibilityScope = LocalNavSharedAnimatedVisibilityScope.current
    val searchResults by viewModel.searchResult.collectAsStateWithLifecycle()
    val genres by viewModel.genres.collectAsStateWithLifecycle()
    var query by rememberSaveable { mutableStateOf("") }
    var selectedGenreNames by rememberSaveable { mutableStateOf(emptySet<String>()) }
    val sharedBoundsModifier = if (
        sharedTransitionScope != null &&
        animatedVisibilityScope != null
    ) {
        with(sharedTransitionScope) {
            Modifier.sharedBounds(
                sharedContentState = rememberSharedContentState(
                    key = HOME_SEARCH_SHARED_BOUNDS_KEY
                ),
                animatedVisibilityScope = animatedVisibilityScope,
                enter = fadeIn(),
                exit = fadeOut(),
                resizeMode = SharedTransitionScope.ResizeMode.scaleToBounds()
            )
        }
    } else {
        Modifier
    }

    SearchScreenContent(
        query = query,
        searchResults = searchResults,
        genres = genres.sortedBy { it.name },
        selectedGenreNames = selectedGenreNames,
        onQueryChange = {
            query = it
            viewModel.search(it)
        },
        onSearch = { viewModel.search(query) },
        onResultClick = viewModel::onSearchResultSelected,
        onGenreSelected = { genre ->
            selectedGenreNames = if (genre.name in selectedGenreNames) {
                selectedGenreNames - genre.name
            } else {
                selectedGenreNames + genre.name
            }
            viewModel.setSelectedGenres(selectedGenreNames)
        },
        onClearGenres = {
            selectedGenreNames = emptySet()
            viewModel.setSelectedGenres(selectedGenreNames)
        },
        onBack = viewModel::onBack,
        modifier = modifier.then(sharedBoundsModifier)
    )
}

@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
@Composable
private fun SearchScreenContent(
    query: String,
    searchResults: List<SearchResult>,
    genres: List<Genre>,
    selectedGenreNames: Set<String>,
    onQueryChange: (String) -> Unit,
    onSearch: () -> Unit,
    onResultClick: (SearchResult) -> Unit,
    onGenreSelected: (Genre) -> Unit,
    onClearGenres: () -> Unit,
    onBack: () -> Unit,
    modifier: Modifier = Modifier
) {
    val scheme = MaterialTheme.colorScheme
    val topBarScrollBehavior = rememberDefaultTopBarScrollBehavior()

    Scaffold(
        topBar = { SearchHeader(
            onBack = onBack,
            scrollBehavior = topBarScrollBehavior
        ) },
        modifier = modifier
            .fillMaxSize()
            .testTag(SearchScreenTag)
            .nestedScroll(topBarScrollBehavior.nestedScrollConnection)
            .background(scheme.background)
    ) { innerPadding ->
        Column(
            modifier = Modifier
                .padding(innerPadding)
                .verticalScroll(rememberScrollState())
                .padding(horizontal = 20.dp)
                .padding(bottom = 28.dp)
        ) {
            SearchField(
                query = query,
                onQueryChange = onQueryChange,
                onSearch = onSearch
            )
            Spacer(modifier = Modifier.height(30.dp))
            if (query.isBlank()) {
                SectionTitle(text = "Browse Genres")
                Spacer(modifier = Modifier.height(18.dp))
                GenreSelector(
                    genres = genres,
                    selectedGenreNames = selectedGenreNames,
                    onGenreSelected = onGenreSelected,
                    onClearGenres = onClearGenres
                )
                if (selectedGenreNames.isNotEmpty()) {
                    Spacer(modifier = Modifier.height(30.dp))
                    SectionTitle(text = "Search Results")
                    Spacer(modifier = Modifier.height(16.dp))
                    SearchResults(
                        searchResults = searchResults,
                        onResultClick = onResultClick
                    )
                }
            } else {
                SectionTitle(text = "Search Results")
                Spacer(modifier = Modifier.height(16.dp))
                SearchResults(
                    searchResults = searchResults,
                    onResultClick = onResultClick
                )
            }
        }
    }
}

@Composable
private fun SearchResults(
    searchResults: List<SearchResult>,
    onResultClick: (SearchResult) -> Unit,
    modifier: Modifier = Modifier
) {
    if (searchResults.isEmpty()) {
        SearchMessage(
            title = "No matches",
            body = "Try a different title or browse your libraries.",
            modifier = modifier
        )
    } else {
        SearchResultsGrid(
            results = searchResults,
            onResultClick = onResultClick,
            modifier = modifier
        )
    }
}

@Composable
@OptIn(ExperimentalMaterial3Api::class)
private fun SearchHeader(
    onBack: () -> Unit,
    scrollBehavior: TopAppBarScrollBehavior? = null,
) {
    DefaultTopBar(
        scrollBehavior = scrollBehavior,
        leftActions = {
            DefaultTopBarIconButton(
                imageVector = Icons.Outlined.ArrowBack,
                contentDescription = "Back",
                onClick = onBack
            )
        }
    )
}

@Composable
private fun SearchField(
    query: String,
    onQueryChange: (String) -> Unit,
    onSearch: () -> Unit,
    modifier: Modifier = Modifier
) {
    val scheme = MaterialTheme.colorScheme
    val keyboardController = LocalSoftwareKeyboardController.current
    val focusRequester = remember { FocusRequester() }

    LaunchedEffect(Unit) {
        focusRequester.requestFocus()
    }

    TextField(
        value = query,
        onValueChange = onQueryChange,
        singleLine = true,
        textStyle = MaterialTheme.typography.titleMedium,
        placeholder = {
            Text(
                text = "Search movies, shows, genres...",
                style = MaterialTheme.typography.titleMedium,
                maxLines = 1,
                overflow = TextOverflow.Ellipsis
            )
        },
        leadingIcon = {
            Icon(
                imageVector = Icons.Outlined.Search,
                contentDescription = null,
                tint = scheme.onSurfaceVariant,
                modifier = Modifier.size(30.dp)
            )
        },
        trailingIcon = {
            if (query.isNotBlank()) {
                IconButton(
                    onClick = { onQueryChange("") },
                    modifier = Modifier.testTag(SearchClearButtonTag)
                ) {
                    Icon(
                        imageVector = Icons.Outlined.Close,
                        contentDescription = "Clear search",
                        tint = scheme.onSurface
                    )
                }
            }
        },
        shape = RoundedCornerShape(24.dp),
        colors = TextFieldDefaults.colors(
            focusedContainerColor = scheme.surfaceContainer,
            unfocusedContainerColor = scheme.surfaceContainer,
            focusedIndicatorColor = Color.Transparent,
            unfocusedIndicatorColor = Color.Transparent
        ),
        keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
        keyboardActions = KeyboardActions(
            onSearch = {
                onSearch()
                keyboardController?.hide()
            }
        ),
        modifier = modifier
            .fillMaxWidth()
            .focusRequester(focusRequester)
            .testTag(SearchQueryFieldTag)
            .height(68.dp)
    )
}

@Composable
private fun SearchResultsGrid(
    results: List<SearchResult>,
    onResultClick: (SearchResult) -> Unit,
    modifier: Modifier = Modifier
) {
    Column(
        verticalArrangement = Arrangement.spacedBy(16.dp),
        modifier = modifier
            .fillMaxWidth()
            .testTag(SearchResultsGridTag)
    ) {
        results.chunked(2).forEachIndexed { rowIndex, rowItems ->
            Row(
                horizontalArrangement = Arrangement.spacedBy(16.dp),
                modifier = Modifier.fillMaxWidth()
            ) {
                rowItems.forEachIndexed { columnIndex, item ->
                    val itemIndex = rowIndex * 2 + columnIndex
                    SearchResultCard(
                        item = item,
                        onClick = { onResultClick(item) },
                        modifier = Modifier
                            .weight(1f)
                            .testTag("$SearchResultItemTagPrefix$itemIndex")
                    )
                }
                if (rowItems.size == 1) {
                    Spacer(modifier = Modifier.weight(1f))
                }
            }
        }
    }
}

@Composable
private fun SectionTitle(
    text: String,
    modifier: Modifier = Modifier,
    icon: @Composable (() -> Unit)? = null
) {
    val scheme = MaterialTheme.colorScheme

    Row(
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(12.dp),
        modifier = modifier.fillMaxWidth()
    ) {
        icon?.invoke()
        Text(
            text = text,
            style = MaterialTheme.typography.headlineSmall,
            color = scheme.onBackground,
            fontWeight = FontWeight.Bold
        )
    }
}

@OptIn(ExperimentalLayoutApi::class)
@Composable
private fun GenreSelector(
    genres: List<Genre>,
    selectedGenreNames: Set<String>,
    onGenreSelected: (Genre) -> Unit,
    onClearGenres: () -> Unit,
    modifier: Modifier = Modifier
) {
    var showGenres by rememberSaveable { mutableStateOf(false) }
    val visibleGenres = (genres.filter { it.name in selectedGenreNames } + genres)
        .distinctBy { it.name }
        .take(19)

    FlowRow(
        horizontalArrangement = Arrangement.spacedBy(10.dp),
        verticalArrangement = Arrangement.spacedBy(10.dp),
        modifier = modifier
            .fillMaxWidth()
            .height(260.dp)
    ) {
        FilterChip(
            selected = false,
            onClick = { showGenres = true },
            modifier = Modifier.testTag(SearchGenreSelectorTag),
            leadingIcon = {
                Icon(
                    imageVector = Icons.Outlined.Tune,
                    contentDescription = null,
                    modifier = Modifier.size(18.dp)
                )
            },
            label = {
                Text(
                    text = if (selectedGenreNames.isEmpty()) {
                        "All genres"
                    } else {
                        "${selectedGenreNames.size} selected"
                    },
                    style = MaterialTheme.typography.labelLarge,
                    fontWeight = FontWeight.SemiBold,
                    maxLines = 1,
                    overflow = TextOverflow.Ellipsis
                )
            }
        )
        visibleGenres.forEachIndexed { index, genre ->
            GenreChip(
                genre = genre,
                selected = genre.name in selectedGenreNames,
                onClick = { onGenreSelected(genre) },
                modifier = Modifier.testTag("$SearchGenreChipTagPrefix$index")
            )
        }
    }

    if (showGenres) {
        AlertDialog(
            onDismissRequest = { showGenres = false },
            modifier = Modifier.testTag(SearchGenreDialogTag),
            title = {
                Text(text = "Select genres")
            },
            text = {
                FlowRow(
                    horizontalArrangement = Arrangement.spacedBy(10.dp),
                    verticalArrangement = Arrangement.spacedBy(10.dp),
                    modifier = Modifier
                        .fillMaxWidth()
                        .heightIn(max = 360.dp)
                        .verticalScroll(rememberScrollState())
                ) {
                    genres.forEachIndexed { index, genre ->
                        GenreChip(
                            genre = genre,
                            selected = genre.name in selectedGenreNames,
                            onClick = { onGenreSelected(genre) },
                            modifier = Modifier.testTag("$SearchGenreDialogChipTagPrefix$index")
                        )
                    }
                }
            },
            confirmButton = {
                TextButton(onClick = { showGenres = false }) {
                    Text(text = "Done")
                }
            },
            dismissButton = {
                if (selectedGenreNames.isNotEmpty()) {
                    TextButton(onClick = onClearGenres) {
                        Text(text = "Clear")
                    }
                }
            }
        )
    }
}

@Composable
private fun GenreChip(
    genre: Genre,
    selected: Boolean,
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) {
    FilterChip(
        selected = selected,
        onClick = onClick,
        leadingIcon = if (selected) {
            {
                Icon(
                    imageVector = Icons.Outlined.Check,
                    contentDescription = null,
                    modifier = Modifier.size(18.dp)
                )
            }
        } else {
            null
        },
        label = {
            Text(
                text = genre.name,
                style = MaterialTheme.typography.labelLarge,
                fontWeight = FontWeight.SemiBold
            )
        },
        modifier = modifier
    )
}

@Preview(showBackground = true)
@Composable
private fun SearchScreenPreview() {
    var query by rememberSaveable { mutableStateOf("du") }

    AppTheme {
        SearchScreenContent(
            query = query,
            searchResults = previewSearchResults,
            genres = previewGenres,
            selectedGenreNames = setOf("Action", "Comedy"),
            onQueryChange = { query = it },
            onSearch = {},
            onResultClick = {},
            onGenreSelected = {},
            onClearGenres = {},
            onBack = {},
            modifier = Modifier.fillMaxSize()
        )
    }
}

private val previewGenres = listOf(
    Genre(name = "Action"),
    Genre(name = "Adventure"),
    Genre(name = "Comedy"),
    Genre(name = "Fantasy"),
    Genre(name = "Romance"),
    Genre(name = "Sci-Fi")
)

private val previewSearchResults = listOf(
    SearchResult(
        id = UUID.fromString("11111111-1111-1111-1111-111111111111"),
        title = "Dune",
        posterUrl = "https://images.unsplash.com/photo-1446776811953-b23d57bd21aa",
        type = MediaKind.MOVIE
    ),
    SearchResult(
        id = UUID.fromString("22222222-2222-2222-2222-222222222222"),
        title = "Dune: Part Two",
        posterUrl = "https://images.unsplash.com/photo-1535016120720-40c646be5580",
        type = MediaKind.MOVIE
    )
)

internal const val SearchScreenTag = "search-screen"
internal const val SearchQueryFieldTag = "search-query-field"
internal const val SearchClearButtonTag = "search-clear-button"
internal const val SearchResultsGridTag = "search-results-grid"
internal const val SearchResultItemTagPrefix = "search-result-item-"
internal const val SearchGenreSelectorTag = "search-genre-selector"
internal const val SearchGenreChipTagPrefix = "search-genre-chip-"
internal const val SearchGenreDialogTag = "search-genre-dialog"
internal const val SearchGenreDialogChipTagPrefix = "search-genre-dialog-chip-"
