package com.craigvg.lichun_android.ui.screens.retention import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import com.craigvg.lichun_android.domain.models.Achievement import com.craigvg.lichun_android.domain.models.AchievementCategory import com.craigvg.lichun_android.ui.theme.AppColors import com.craigvg.lichun_android.ui.theme.AppSpacing import com.craigvg.lichun_android.ui.theme.AppTypography import com.craigvg.lichun_android.viewmodel.GameStateViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle /** * Achievements screen - Gallery/grid view of all achievements * Ported from iOS AchievementsView.swift */ @OptIn(ExperimentalMaterial3Api::class) @Composable fun AchievementsScreen( gameStateViewModel: GameStateViewModel = hiltViewModel(), onBack: () -> Unit = {}, onAchievementClick: (Achievement) -> Unit = {} ) { val achievements by gameStateViewModel.achievements.collectAsStateWithLifecycle() var selectedCategory by remember { mutableStateOf(null) } val filteredAchievements = if (selectedCategory != null) { achievements.filter { it.category == selectedCategory } } else { achievements } val unlockedCount = achievements.count { it.unlocked } val totalCount = achievements.size Scaffold( topBar = { TopAppBar( title = { Text("Achievements", style = AppTypography.headline) }, navigationIcon = { IconButton(onClick = onBack) { Icon( imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back", tint = AppColors.primaryText ) } }, colors = TopAppBarDefaults.topAppBarColors( containerColor = AppColors.surfaceElevated ) ) }, containerColor = AppColors.background ) { paddingValues -> Column( modifier = Modifier .fillMaxSize() .padding(paddingValues) ) { // Progress Header AchievementsHeader( unlockedCount = unlockedCount, totalCount = totalCount ) // Category filter CategoryFilterRow( selectedCategory = selectedCategory, onCategorySelected = { selectedCategory = it } ) // Achievements grid if (filteredAchievements.isEmpty()) { EmptyAchievementsState() } else { LazyVerticalGrid( columns = GridCells.Fixed(2), modifier = Modifier .fillMaxSize() .padding(AppSpacing.md), horizontalArrangement = Arrangement.spacedBy(AppSpacing.sm), verticalArrangement = Arrangement.spacedBy(AppSpacing.sm) ) { items(filteredAchievements) { achievement -> AchievementCard( achievement = achievement, onClick = { onAchievementClick(achievement) } ) } } } } } LaunchedEffect(Unit) { gameStateViewModel.fetchAchievements() } } @Composable fun AchievementsHeader( unlockedCount: Int, totalCount: Int ) { Card( modifier = Modifier .fillMaxWidth() .padding(AppSpacing.md), shape = RoundedCornerShape(AppSpacing.cornerRadius), colors = CardDefaults.cardColors(containerColor = AppColors.surfaceElevated) ) { Column( modifier = Modifier .fillMaxWidth() .padding(AppSpacing.md), horizontalAlignment = Alignment.CenterHorizontally ) { Text( text = "Achievements", style = AppTypography.title, color = AppColors.primaryText ) Spacer(modifier = Modifier.height(AppSpacing.xs)) Row( verticalAlignment = Alignment.CenterVertically ) { Icon( imageVector = Icons.Default.EmojiEvents, contentDescription = null, tint = AppColors.money, modifier = Modifier.size(24.dp) ) Spacer(modifier = Modifier.width(8.dp)) Text( text = "$unlockedCount / $totalCount unlocked", style = AppTypography.bodyBold, color = AppColors.secondaryText ) } Spacer(modifier = Modifier.height(AppSpacing.xs)) Box( modifier = Modifier .fillMaxWidth() .height(8.dp) .clip(RoundedCornerShape(4.dp)) .background(AppColors.money.copy(alpha = 0.2f)) ) { val progress = if (totalCount > 0) unlockedCount.toFloat() / totalCount else 0f Box( modifier = Modifier .fillMaxWidth(progress) .fillMaxHeight() .clip(RoundedCornerShape(4.dp)) .background(AppColors.money) ) } } } } @Composable fun CategoryFilterRow( selectedCategory: AchievementCategory?, onCategorySelected: (AchievementCategory?) -> Unit ) { val categories = listOf(null) + AchievementCategory.entries val scrollState = rememberScrollState() Row( modifier = Modifier .fillMaxWidth() .horizontalScroll(scrollState) .padding(horizontal = AppSpacing.md), horizontalArrangement = Arrangement.spacedBy(AppSpacing.xs) ) { categories.forEach { category -> FilterChip( selected = selectedCategory == category, onClick = { onCategorySelected(category) }, label = { Text( text = category?.displayName ?: "All", style = AppTypography.caption ) }, colors = FilterChipDefaults.filterChipColors( selectedContainerColor = AppColors.primary, selectedLabelColor = Color.White ) ) } } } @Composable fun AchievementCard( achievement: Achievement, onClick: () -> Unit ) { Card( modifier = Modifier .fillMaxWidth() .clickable(onClick = onClick) .alpha(if (achievement.unlocked) 1f else 0.6f), shape = RoundedCornerShape(AppSpacing.cornerRadius), colors = CardDefaults.cardColors( containerColor = if (achievement.unlocked) { achievement.category.color.copy(alpha = 0.1f) } else { AppColors.surfaceSubtle } ) ) { Column( modifier = Modifier .fillMaxWidth() .padding(AppSpacing.md), horizontalAlignment = Alignment.CenterHorizontally ) { // Icon Box( modifier = Modifier .size(48.dp) .clip(RoundedCornerShape(AppSpacing.smallCornerRadius)) .background( if (achievement.unlocked) { achievement.category.color.copy(alpha = 0.2f) } else { AppColors.disabledText.copy(alpha = 0.2f) } ), contentAlignment = Alignment.Center ) { Icon( imageVector = if (achievement.unlocked) { Icons.Default.EmojiEvents } else { Icons.Default.Lock }, contentDescription = null, tint = if (achievement.unlocked) { achievement.category.color } else { AppColors.disabledText }, modifier = Modifier.size(28.dp) ) } Spacer(modifier = Modifier.height(AppSpacing.xs)) Text( text = achievement.name, style = AppTypography.captionBold, color = AppColors.primaryText, textAlign = TextAlign.Center, maxLines = 2 ) Spacer(modifier = Modifier.height(2.dp)) // Progress or reward if (achievement.unlocked) { Row( verticalAlignment = Alignment.CenterVertically ) { Icon( imageVector = Icons.Default.Diamond, contentDescription = null, tint = AppColors.diamond, modifier = Modifier.size(14.dp) ) Spacer(modifier = Modifier.width(2.dp)) Text( text = "+${achievement.reward}", style = AppTypography.small, color = AppColors.diamond ) } } else if (achievement.progressRequired != null) { // Progress bar val progress = achievement.progressPercentage Box( modifier = Modifier .fillMaxWidth() .height(4.dp) .clip(RoundedCornerShape(2.dp)) .background(AppColors.disabledText.copy(alpha = 0.3f)) ) { Box( modifier = Modifier .fillMaxWidth(progress) .fillMaxHeight() .clip(RoundedCornerShape(2.dp)) .background(achievement.category.color) ) } Text( text = achievement.progressText, style = AppTypography.micro, color = AppColors.secondaryText ) } } } } @Composable fun EmptyAchievementsState() { Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(AppSpacing.xl) ) { Icon( imageVector = Icons.Default.EmojiEvents, contentDescription = null, tint = AppColors.disabledText, modifier = Modifier.size(64.dp) ) Spacer(modifier = Modifier.height(AppSpacing.md)) Text( text = "No achievements yet", style = AppTypography.headline, color = AppColors.secondaryText, textAlign = TextAlign.Center ) Text( text = "Keep playing to unlock achievements", style = AppTypography.body, color = AppColors.disabledText, textAlign = TextAlign.Center ) } } }