package com.craigvg.lichun_android.ui.screens.settings import android.content.ClipData import android.content.ClipboardManager import android.content.Context import android.content.Intent import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState 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.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel 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 /** * Data export screen for GDPR compliance * Ported from iOS DataExportView.swift */ enum class ExportStatus { IDLE, REQUESTING, PROCESSING, READY, ERROR } @OptIn(ExperimentalMaterial3Api::class) @Composable fun DataExportScreen( gameStateViewModel: GameStateViewModel = hiltViewModel(), onBack: () -> Unit = {} ) { val exportPayload by gameStateViewModel.dataExportPayload.collectAsStateWithLifecycle() val currentError by gameStateViewModel.currentError.collectAsStateWithLifecycle() val context = LocalContext.current var exportStatus by remember { mutableStateOf(ExportStatus.IDLE) } var exportData by remember { mutableStateOf("") } var errorMessage by remember { mutableStateOf("") } Scaffold( topBar = { TopAppBar( title = { Text("Export Data", 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) .verticalScroll(rememberScrollState()) ) { // Header Icon Column( modifier = Modifier .fillMaxWidth() .padding(top = AppSpacing.lg), horizontalAlignment = Alignment.CenterHorizontally ) { Icon( imageVector = Icons.Default.Download, contentDescription = null, tint = AppColors.info, modifier = Modifier.size(60.dp) ) Spacer(modifier = Modifier.height(AppSpacing.md)) Text( text = "Export Your Data", style = AppTypography.title, color = AppColors.primaryText ) Spacer(modifier = Modifier.height(AppSpacing.sm)) Text( text = "Request a copy of all your personal data stored in BaoLife. This includes your account information, character data, gameplay history, and more.", style = AppTypography.body, color = AppColors.secondaryText, textAlign = TextAlign.Center, modifier = Modifier.padding(horizontal = AppSpacing.md) ) } Spacer(modifier = Modifier.height(AppSpacing.lg)) // What's Included Section Column( modifier = Modifier.padding(horizontal = AppSpacing.md) ) { Text( text = "What's Included", style = AppTypography.headline, color = AppColors.primaryText ) Spacer(modifier = Modifier.height(AppSpacing.md)) DataIncludedRow( icon = Icons.Default.Person, title = "Account Information", description = "Username, email, registration date" ) Spacer(modifier = Modifier.height(AppSpacing.sm)) DataIncludedRow( icon = Icons.Default.DirectionsWalk, title = "Character Data", description = "All character details, attributes, appearance" ) Spacer(modifier = Modifier.height(AppSpacing.sm)) DataIncludedRow( icon = Icons.Default.TrendingUp, title = "Game Progress", description = "Achievements, statistics, records" ) Spacer(modifier = Modifier.height(AppSpacing.sm)) DataIncludedRow( icon = Icons.Default.Favorite, title = "Relationships", description = "All relationships and affinity levels" ) Spacer(modifier = Modifier.height(AppSpacing.sm)) DataIncludedRow( icon = Icons.Default.Chat, title = "Conversations", description = "Chat history with characters" ) Spacer(modifier = Modifier.height(AppSpacing.sm)) DataIncludedRow( icon = Icons.Default.ShoppingCart, title = "Purchase History", description = "All in-app purchase records" ) } Spacer(modifier = Modifier.height(AppSpacing.lg)) // Status Display if (exportStatus != ExportStatus.IDLE) { Card( modifier = Modifier .fillMaxWidth() .padding(horizontal = AppSpacing.md), shape = RoundedCornerShape(AppSpacing.cornerRadius), colors = CardDefaults.cardColors( containerColor = AppColors.surfaceElevated ) ) { Column( modifier = Modifier .fillMaxWidth() .padding(AppSpacing.md), horizontalAlignment = Alignment.CenterHorizontally ) { when (exportStatus) { ExportStatus.REQUESTING, ExportStatus.PROCESSING -> { CircularProgressIndicator( color = AppColors.primary, modifier = Modifier.size(40.dp) ) Spacer(modifier = Modifier.height(AppSpacing.md)) Text( text = if (exportStatus == ExportStatus.REQUESTING) "Requesting export..." else "Preparing your data...", style = AppTypography.body, color = AppColors.secondaryText ) if (exportStatus == ExportStatus.PROCESSING) { Text( text = "This may take a few minutes", style = AppTypography.caption, color = AppColors.secondaryText ) } } ExportStatus.READY -> { Icon( imageVector = Icons.Default.CheckCircle, contentDescription = null, tint = AppColors.success, modifier = Modifier.size(50.dp) ) Spacer(modifier = Modifier.height(AppSpacing.sm)) Text( text = "Export Ready!", style = AppTypography.headline, color = AppColors.success ) Spacer(modifier = Modifier.height(AppSpacing.md)) Button( onClick = { val sendIntent = Intent().apply { action = Intent.ACTION_SEND putExtra(Intent.EXTRA_TEXT, exportData) type = "text/plain" } val shareIntent = Intent.createChooser(sendIntent, "Share Export") context.startActivity(shareIntent) }, modifier = Modifier.fillMaxWidth(), colors = ButtonDefaults.buttonColors( containerColor = AppColors.info ), shape = RoundedCornerShape(AppSpacing.cornerRadius) ) { Icon( imageVector = Icons.Default.Share, contentDescription = null ) Spacer(modifier = Modifier.width(AppSpacing.xs)) Text("Share Export") } Spacer(modifier = Modifier.height(AppSpacing.sm)) OutlinedButton( onClick = { val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clip = ClipData.newPlainText("BaoLife Export", exportData) clipboard.setPrimaryClip(clip) }, modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(AppSpacing.cornerRadius) ) { Icon( imageVector = Icons.Default.ContentCopy, contentDescription = null ) Spacer(modifier = Modifier.width(AppSpacing.xs)) Text("Copy to Clipboard") } } ExportStatus.ERROR -> { Icon( imageVector = Icons.Default.Error, contentDescription = null, tint = AppColors.error, modifier = Modifier.size(50.dp) ) Spacer(modifier = Modifier.height(AppSpacing.sm)) Text( text = "Export Failed", style = AppTypography.headline, color = AppColors.error ) Text( text = errorMessage, style = AppTypography.body, color = AppColors.secondaryText, textAlign = TextAlign.Center ) } else -> {} } } } Spacer(modifier = Modifier.height(AppSpacing.md)) } // Request Button if (exportStatus == ExportStatus.IDLE || exportStatus == ExportStatus.ERROR) { Button( onClick = { exportStatus = ExportStatus.REQUESTING errorMessage = "" gameStateViewModel.clearDataExportPayload() gameStateViewModel.requestDataExport() }, modifier = Modifier .fillMaxWidth() .padding(horizontal = AppSpacing.md), colors = ButtonDefaults.buttonColors( containerColor = AppColors.info ), shape = RoundedCornerShape(AppSpacing.cornerRadius) ) { Icon( imageVector = Icons.Default.Download, contentDescription = null ) Spacer(modifier = Modifier.width(AppSpacing.xs)) Text( text = if (exportStatus == ExportStatus.ERROR) "Try Again" else "Request Export" ) } } Spacer(modifier = Modifier.height(AppSpacing.md)) // GDPR Notice Card( modifier = Modifier .fillMaxWidth() .padding(horizontal = AppSpacing.md), shape = RoundedCornerShape(AppSpacing.cornerRadius), colors = CardDefaults.cardColors( containerColor = AppColors.info.copy(alpha = 0.1f) ) ) { Column( modifier = Modifier.padding(AppSpacing.md) ) { Row( verticalAlignment = Alignment.CenterVertically ) { Icon( imageVector = Icons.Default.Info, contentDescription = null, tint = AppColors.info, modifier = Modifier.size(20.dp) ) Spacer(modifier = Modifier.width(AppSpacing.xs)) Text( text = "Your Rights", style = AppTypography.headline, color = AppColors.primaryText ) } Spacer(modifier = Modifier.height(AppSpacing.sm)) Text( text = "Under GDPR and data protection laws, you have the right to access and receive a copy of your personal data in a portable format. This export will be provided in JSON format, which can be opened with any text editor.", style = AppTypography.body, color = AppColors.secondaryText ) Spacer(modifier = Modifier.height(AppSpacing.xs)) Text( text = "Processing time: Usually immediate, up to 30 days for complex requests.", style = AppTypography.caption, color = AppColors.secondaryText ) } } Spacer(modifier = Modifier.height(AppSpacing.xl)) } } LaunchedEffect(exportStatus) { if (exportStatus == ExportStatus.REQUESTING) { exportStatus = ExportStatus.PROCESSING } } LaunchedEffect(exportPayload) { if (!exportPayload.isNullOrBlank()) { exportData = exportPayload!! exportStatus = ExportStatus.READY } } LaunchedEffect(currentError, exportStatus) { if (exportStatus == ExportStatus.REQUESTING || exportStatus == ExportStatus.PROCESSING) { currentError?.let { errorMessage = it.userMessage exportStatus = ExportStatus.ERROR } } } } @Composable private fun DataIncludedRow( icon: ImageVector, title: String, description: String ) { Card( modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(AppSpacing.cornerRadius), colors = CardDefaults.cardColors( containerColor = AppColors.info.copy(alpha = 0.08f) ) ) { Row( modifier = Modifier.padding(AppSpacing.md), verticalAlignment = Alignment.Top ) { Icon( imageVector = icon, contentDescription = null, tint = AppColors.info, modifier = Modifier.size(20.dp) ) Spacer(modifier = Modifier.width(AppSpacing.sm)) Column { Text( text = title, style = AppTypography.bodyBold, color = AppColors.primaryText ) Text( text = description, style = AppTypography.caption, color = AppColors.secondaryText ) } } } }