package com.craigvg.lichun_android.ui.screens.activities import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Bolt 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.graphics.Color import androidx.compose.ui.platform.LocalView import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.craigvg.lichun_android.domain.models.PlayerActivityInfo 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.utils.HapticFeedback import com.craigvg.lichun_android.viewmodel.GameStateViewModel import com.craigvg.lichun_android.viewmodel.PlayerViewModel /** * Perform Activity screen (Wave 2) — proactive-agency UI. The player picks a * player-initiated activity (study / exercise / socialize / sideHustle / hobby) * and the `performActivity` command is sent. Each row shows the rough effect + * energy cost, gated optimistically on usable energy and minimum age (the server * is authoritative and replies with an error toast + refreshed playerObject). * * Android parity with iOS PerformActivityView.swift. */ @OptIn(ExperimentalMaterial3Api::class) @Composable fun PerformActivityScreen( gameStateViewModel: GameStateViewModel = hiltViewModel(), playerViewModel: PlayerViewModel = hiltViewModel(), onBack: () -> Unit = {} ) { val person by playerViewModel.person.collectAsStateWithLifecycle() val usableEnergy = person.calcEnergy val ageYears = person.ageYears val view = LocalView.current Scaffold( topBar = { TopAppBar( title = { Text("Do an Activity", style = AppTypography.headline) }, navigationIcon = { IconButton(onClick = onBack) { Icon(Icons.AutoMirrored.Filled.ArrowBack, "Back", tint = AppColors.primaryText) } }, colors = TopAppBarDefaults.topAppBarColors(containerColor = AppColors.surfaceElevated) ) }, containerColor = AppColors.background ) { padding -> Column( modifier = Modifier .fillMaxSize() .padding(padding) .verticalScroll(rememberScrollState()) .padding(horizontal = AppSpacing.md) ) { Spacer(Modifier.height(AppSpacing.md)) // Header: energy available + intent. Column( modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally ) { Row(verticalAlignment = Alignment.CenterVertically) { Icon(Icons.Default.Bolt, null, tint = AppColors.energy, modifier = Modifier.size(16.dp)) Spacer(Modifier.width(4.dp)) Text("$usableEnergy energy available", style = AppTypography.bodyBold, color = AppColors.primaryText) } Text( "Take charge of your day - pick something to do.", style = AppTypography.caption, color = AppColors.secondaryText ) } Spacer(Modifier.height(AppSpacing.md)) PlayerActivityInfo.all.forEach { activity -> PerformActivityRow( activity = activity, usableEnergy = usableEnergy, ageYears = ageYears, onPerform = { HapticFeedback.medium(view) gameStateViewModel.performActivity(activity.id) // Server reply (toast + playerObject) updates state; close so a // single free slot per action doesn't confuse. onBack() } ) Spacer(Modifier.height(AppSpacing.sm)) } Spacer(Modifier.height(AppSpacing.sm)) Text( "Activities spend a free slot in your day and apply their effects right away.", style = AppTypography.caption, color = AppColors.secondaryText, textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth().padding(horizontal = AppSpacing.lg) ) Spacer(Modifier.height(AppSpacing.xl)) } } } @Composable private fun PerformActivityRow( activity: PlayerActivityInfo, usableEnergy: Int, ageYears: Int, onPerform: () -> Unit ) { val ageLocked = ageYears < activity.minAge val energyLocked = usableEnergy < activity.energyCost val disabled = ageLocked || energyLocked val buttonLabel = when { ageLocked -> "Age ${activity.minAge}+" energyLocked -> "Low energy" else -> "Do it" } Card( modifier = Modifier .fillMaxWidth() .alpha(if (disabled) 0.7f else 1f), shape = RoundedCornerShape(AppSpacing.cornerRadius), colors = CardDefaults.cardColors(containerColor = AppColors.surfaceElevated), elevation = CardDefaults.cardElevation(defaultElevation = 2.dp) ) { Row( modifier = Modifier.padding(AppSpacing.md), verticalAlignment = Alignment.CenterVertically ) { Box( modifier = Modifier .size(48.dp) .background(activity.color.copy(alpha = 0.15f), CircleShape), contentAlignment = Alignment.Center ) { Icon(activity.icon, null, tint = activity.color, modifier = Modifier.size(22.dp)) } Spacer(Modifier.width(AppSpacing.md)) Column(modifier = Modifier.weight(1f)) { Text(activity.title, style = AppTypography.bodyBold, color = AppColors.primaryText) Text(activity.blurb, style = AppTypography.caption, color = AppColors.secondaryText, maxLines = 2) Spacer(Modifier.height(2.dp)) Row(verticalAlignment = Alignment.CenterVertically) { Icon(Icons.Default.Bolt, null, tint = AppColors.energy, modifier = Modifier.size(10.dp)) Spacer(Modifier.width(2.dp)) Text( "${activity.energyCost} energy", style = AppTypography.caption, color = if (energyLocked) AppColors.error else AppColors.secondaryText ) } } Spacer(Modifier.width(AppSpacing.sm)) Button( onClick = onPerform, enabled = !disabled, colors = ButtonDefaults.buttonColors( containerColor = activity.color, contentColor = Color.White, disabledContainerColor = AppColors.disabledText.copy(alpha = 0.4f), disabledContentColor = Color.White ), shape = RoundedCornerShape(AppSpacing.pillCornerRadius), contentPadding = PaddingValues(horizontal = AppSpacing.md, vertical = AppSpacing.sm) ) { Text(buttonLabel, style = AppTypography.captionBold) } } } }