我完全依靠AI使用Jetpack Compose开发了一个Android选号APP

APP下载:https://download.csdn.net/download/Silence1515/92608868https://download.csdn.net/download/Silence1515/92608868

南来的北往的,走过路过的朋友,请您停停脚、捧个场!

【技术探索】仅用于学习交流:我用Compose与AI开发了一个Android选号应用

在深入学习了Jetpack Compose和现代AI技术集成后,我完成了一个纯属用于学习与交流 的Android应用开发项目。特此声明:本项目及其所有代码绝不涉及任何形式的销售、预测或引导,仅为验证技术可行性的个人练习,旨在探讨如何将前沿工具应用于趣味功能的实现。

项目初衷:当Compose遇见AI提示词

这个项目的核心驱动力来源于我对两个技术方向的好奇:

  1. Jetpack Compose的声明式UI实践:我想脱离传统XML,完全使用Kotlin和Compose来构建一个流畅、现代的Android界面,并实践状态管理、主题定制等完整开发流程。

  2. AI能力与移动端的轻量级结合 :我探索了如何通过设计精妙的提示词(Prompt)与AI大模型进行交互,让其在一个严格限定且纯属娱乐的范围内,生成随机的号码组合,从而模拟一个"智能选号助手"的交互过程。

应用界面与功能亮点(仅供演示)

整个应用完全采用Material Design 3设计语言。

  • 核心界面:主屏幕包含一个清晰的数字展示区域,以及一个醒目的浮动操作按钮(FAB)。

  • "智能"选号流程 :当用户点击按钮后,应用会将我预先设计好的、强调随机性与娱乐性的提示词模板发送给AI接口。AI根据指令返回一组号码,经应用解析后,以优雅的动画效果展示在屏幕上。

  • 技术细节 :我使用了ViewModel来管理UI状态(如当前显示的号码),并利用LaunchedEffect和协程来处理网络请求,确保UI的响应性。所有涉及AI交互的部分都做了异常捕获与友好提示。

挑战、收获与郑重声明

开发中遇到的主要挑战 在于如何精确设计提示词,以严格约束AI的输出格式,并确保其理解这只是一个随机生成游戏。同时,Compose的副作用管理与重组优化也是需要仔细处理的部分。

通过这个项目,我的核心收获包括

  • 掌握了使用Compose独立开发一个完整功能应用的能力。

  • 理解了如何将外部AI服务安全、可控地集成到移动端。

  • 再次深刻认识到,技术本身是中立的,但开发者的意图和项目的公开说明至关重要。

因此,我必须再次明确并强调

本项目及其产生的所有代码,自始至终仅有一个目的:个人技术学习与交流。它所实现的"选号"功能,是构建在"彩票结果完全随机、不可预测"这一绝对前提下的、纯粹的界面与交互技术演示,不含任何实际预测成分。请勿将此项目用于任何学习交流之外的用途。

源码与交流

该项目的完整源码已托管在GitHub上。我非常欢迎对Compose、AI集成或Android开发感兴趣的朋友访问仓库,查看代码细节,或提出改进建议。让我们在技术学习的道路上共同进步。


最后,我想提出一些供讨论的问题

在探索如何将开放性AI能力可控地集成到移动应用这一技术前沿时,除了精心设计提示词(Prompt Engineering)外,我还希望与各位探讨以下更深入的话题:

有哪些经过验证的技术方案或架构设计模式,能够更优雅、可靠地约束AI的输出,并将其安全地融入应用业务流程?

同时,作为一次Android开发的实践,我也非常期待交流:

在Compose声明式UI、ViewModel状态管理、以及协程等现代Android架构组件的应用过程中,您有哪些独到的见解、踩坑心得或最佳实践?

期待您分享真知灼见,让我们在技术道路上共同精进。

仅展示部分代码实现:

复制代码
package com.smallsnailtech.luckyticket.ui.screen.home

import androidx.compose.animation.*
import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
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.ui.draw.clip
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.ArrowDropUp
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.FavoriteBorder
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.*
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import android.content.Context
import android.text.format.DateFormat
import android.widget.Toast
import com.smallsnailtech.luckyticket.R
import com.smallsnailtech.luckyticket.app.LuckyTicketApplication
import com.smallsnailtech.luckyticket.utils.DeepSeekApiUtils
import com.smallsnailtech.luckyticket.utils.LotteryNumberGenerator
import com.smallsnailtech.luckyticket.model.Happy8PlayType
import com.smallsnailtech.luckyticket.model.Happy8AllPlayTypesPrediction
import timber.log.Timber
import java.text.SimpleDateFormat
import java.util.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.launch
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.collectAsState
import com.smallsnailtech.luckyticket.model.LotteryType
import com.smallsnailtech.luckyticket.ui.theme.LuckyBlue40
import com.smallsnailtech.luckyticket.ui.theme.LuckyRed40
import com.smallsnailtech.luckyticket.utils.FontSizeSystem
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import com.smallsnailtech.luckyticket.ui.components.TopNavBar
import com.smallsnailtech.luckyticket.ui.navigation.Destinations



/**
 * 显示格式化的号码,与生成号码和收藏列表的展示效果一致
 */
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun DisplayFormattedNumbers(result: String, lotteryName: String = "彩票", winningNumbers: Set<String> = emptySet()) {
    Column(
        modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally
    ) {
        // 如果未提供彩票名称,尝试从结果中提取
        val finalLotteryName = if (lotteryName == "彩票" && result.isNotEmpty()) {
            // 检查是否是纯数字格式
            val isPureNumbers = result.matches(Regex("""^[0-9\s|]+$"""))
            if (isPureNumbers) {
                // 根据号码格式判断彩票类型
                val parts = result.split("|")
                when {
                    parts.size == 2 && parts[0].trim().split(" ").size == 6 && parts[1].trim()
                        .split(" ").size == 1 -> LotteryType.DOUBLE_COLOR_BALL.chineseName

                    parts.size == 2 && parts[0].trim().split(" ").size == 5 && parts[1].trim()
                        .split(" ").size == 2 -> LotteryType.POWERBALL.chineseName

                    parts.size == 2 && parts[0].trim().split(" ").size == 7 && parts[1].trim()
                        .split(" ").size == 1 -> LotteryType.SEVEN_LE.chineseName

                    parts.size == 1 && parts[0].trim()
                        .split(" ").size == 3 -> LotteryType.THREE_D.chineseName

                    parts.size == 1 && parts[0].trim()
                        .split(" ").size == 5 -> LotteryType.PERMUTATION_5.chineseName

                    parts.size == 1 && parts[0].trim()
                        .split(" ").size == 7 -> LotteryType.SEVEN_STAR.chineseName

                    parts.size == 1 && parts[0].trim()
                        .split(" ").size == 10 -> LotteryType.HAPPY_8.chineseName

                    else -> "彩票"
                }
            } else {
                // 按行处理结果
                val lines = result.split("\n")
                if (lines.isNotEmpty()) {
                    val firstLine = lines.first()
                    // 提取彩票名称(如:双色球、大乐透、福彩3D、七星彩、七乐彩、排列3、排列5、快乐8)
                    val lotteryNames = LotteryType.values().map { it.chineseName }
                    lotteryNames.firstOrNull { firstLine.contains(it) } ?: "彩票"
                } else {
                    "彩票"
                }
            }
        } else {
            lotteryName
        }

        // 添加固定提示语
        Text(
            text = "${finalLotteryName}的号码是随机生成的,任何预测都无法保证准确性。",
            style = MaterialTheme.typography.bodyMedium.copy(
                fontSize = FontSizeSystem.getScaledSp(14.sp)
            ),
            color = MaterialTheme.colorScheme.onSurface,
            textAlign = TextAlign.Center,
            modifier = Modifier
                .fillMaxWidth()
                .padding(bottom = 8.dp)
        )
        Text(
            text = "不过,我可以根据历史数据中的冷热号、遗漏值等常见分析思路,为您提供一组参考号码。",
            style = MaterialTheme.typography.bodyMedium.copy(
                fontSize = FontSizeSystem.getScaledSp(14.sp)
            ),
            color = MaterialTheme.colorScheme.onSurface,
            textAlign = TextAlign.Center,
            modifier = Modifier
                .fillMaxWidth()
                .padding(bottom = 8.dp)
        )
        Text(
            text = "(仅供娱乐)",
            style = MaterialTheme.typography.bodyMedium.copy(
                fontSize = FontSizeSystem.getScaledSp(14.sp)
            ),
            color = MaterialTheme.colorScheme.onSurface,
            textAlign = TextAlign.Center,
            modifier = Modifier
                .fillMaxWidth()
                .padding(bottom = 8.dp)
        )

        // 检查是否是纯数字格式
        val isPureNumbers = result.matches(Regex("""^[0-9\s|]+$"""))
        if (isPureNumbers) {
            // 解析号码字符串,分离前区和后区
            val parts = result.split("|")
            
            // 检查是否是福彩3D、排列3或排列5(通过号码长度判断)
            val is3DOrPermutation = parts[0].trim().split(" ").filter { it.isNotEmpty() }.size in listOf(3, 5)
            
            // 福彩3D、排列3和排列5保持原始顺序,不排序
            val frontBalls = if (is3DOrPermutation) {
                parts[0].trim().split(" ").filter { it.isNotEmpty() }
            } else {
                parts[0].trim().split(" ").filter { it.isNotEmpty() }.sorted()
            }
            
            val backBalls = if (parts.size > 1) parts[1].trim().split(" ")
                .filter { it.isNotEmpty() } else emptyList()

            // 显示号码,使用FlowRow实现自适应布局
            FlowRow(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = 8.dp),
                horizontalArrangement = Arrangement.Center,
                verticalArrangement = Arrangement.spacedBy(8.dp)
            ) {
                // 显示前区号码
                frontBalls.forEachIndexed { index, num ->
                    Box(modifier = Modifier.height(44.dp), contentAlignment = Alignment.Center) {
                        NumberBall(number = num, isFront = true, isPlaceholder = false, isWinning = winningNumbers.contains(num))
                    }
                    if (index < frontBalls.size - 1) {
                        Spacer(modifier = Modifier.width(8.dp))
                    }
                }

                // 如果有后区号码,显示分隔符和后区号码
                if (backBalls.isNotEmpty()) {
                    Spacer(modifier = Modifier.width(16.dp))
                    Box(modifier = Modifier.height(44.dp), contentAlignment = Alignment.Center) {
                        Text(
                            text = "|", style = MaterialTheme.typography.headlineMedium.copy(
                                fontSize = FontSizeSystem.getScaledSp(28.sp)
                            )
                        )
                    }
                    Spacer(modifier = Modifier.width(16.dp))

                    backBalls.forEachIndexed { index, num ->
                        Box(modifier = Modifier.height(44.dp), contentAlignment = Alignment.Center) {
                            NumberBall(number = num, isFront = false, isPlaceholder = false, isWinning = winningNumbers.contains(num))
                        }
                        if (index < backBalls.size - 1) {
                            Spacer(modifier = Modifier.width(8.dp))
                        }
                    }
                }
            }
        } else {
            // 按行处理结果,提取纯数字格式
            val extractedNumbers =
                LotteryNumberGenerator.extractNumbersFromPrediction(result, finalLotteryName)
            // 解析号码字符串,分离前区和后区
            val parts = extractedNumbers.split("|")
            
            // 检查是否是福彩3D、排列3或排列5(通过号码长度判断)
            val is3DOrPermutation = parts[0].trim().split(" ").filter { it.isNotEmpty() }.size in listOf(3, 5)
            
            // 福彩3D、排列3和排列5保持原始顺序,不排序
            val frontBalls = if (is3DOrPermutation) {
                parts[0].trim().split(" ").filter { it.isNotEmpty() }
            } else {
                parts[0].trim().split(" ").filter { it.isNotEmpty() }.sorted()
            }
            
            val backBalls = if (parts.size > 1) parts[1].trim().split(" ")
                .filter { it.isNotEmpty() } else emptyList()

            // 显示号码,使用FlowRow实现自适应布局
            FlowRow(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = 8.dp),
                horizontalArrangement = Arrangement.Center,
                verticalArrangement = Arrangement.spacedBy(8.dp)
            ) {
                // 显示前区号码
                frontBalls.forEachIndexed { index, num ->
                    Box(modifier = Modifier.height(44.dp), contentAlignment = Alignment.Center) {
                        NumberBall(number = num, isFront = true, isPlaceholder = false, isWinning = winningNumbers.contains(num))
                    }
                    if (index < frontBalls.size - 1) {
                        Spacer(modifier = Modifier.width(8.dp))
                    }
                }

                // 如果有后区号码,显示分隔符和后区号码
                if (backBalls.isNotEmpty()) {
                    Spacer(modifier = Modifier.width(16.dp))
                    Box(modifier = Modifier.height(44.dp), contentAlignment = Alignment.Center) {
                        Text(
                            text = "|", style = MaterialTheme.typography.headlineMedium.copy(
                                fontSize = FontSizeSystem.getScaledSp(28.sp)
                            )
                        )
                    }
                    Spacer(modifier = Modifier.width(16.dp))

                    backBalls.forEachIndexed { index, num ->
                        Box(modifier = Modifier.height(44.dp), contentAlignment = Alignment.Center) {
                            NumberBall(number = num, isFront = false, isPlaceholder = false, isWinning = winningNumbers.contains(num))
                        }
                        if (index < backBalls.size - 1) {
                            Spacer(modifier = Modifier.width(8.dp))
                        }
                    }
                }
            }
        }
    }
}

/**
 * 显示快乐8所有玩法的预测结果
 */
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun DisplayHappy8AllPlayTypesPrediction(
    happy8Prediction: Happy8AllPlayTypesPrediction,
    homeViewModel: HomeViewModel,
    context: android.content.Context
) {
    Column(
        modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally
    ) {
        // 添加固定提示语
        Text(
            text = "快乐8的号码是随机生成的,任何预测都无法保证准确性。",
            style = MaterialTheme.typography.bodyMedium.copy(
                fontSize = FontSizeSystem.getScaledSp(14.sp)
            ),
            color = MaterialTheme.colorScheme.onSurface,
            textAlign = TextAlign.Center,
            modifier = Modifier
                .fillMaxWidth()
                .padding(bottom = 8.dp)
        )
        Text(
            text = "不过,我可以根据历史数据中的冷热号、遗漏值等常见分析思路,为您提供各玩法的参考号码。",
            style = MaterialTheme.typography.bodyMedium.copy(
                fontSize = FontSizeSystem.getScaledSp(14.sp)
            ),
            color = MaterialTheme.colorScheme.onSurface,
            textAlign = TextAlign.Center,
            modifier = Modifier
                .fillMaxWidth()
                .padding(bottom = 8.dp)
        )
        Text(
            text = "(仅供娱乐)",
            style = MaterialTheme.typography.bodyMedium.copy(
                fontSize = FontSizeSystem.getScaledSp(14.sp)
            ),
            color = MaterialTheme.colorScheme.onSurface,
            textAlign = TextAlign.Center,
            modifier = Modifier
                .fillMaxWidth()
                .padding(bottom = 16.dp)
        )

        // 显示所有玩法的预测结果
        Happy8PlayType.allPlayTypes.forEach { playType ->
            val numbers = happy8Prediction.getNumbersByPlayType(playType)
            if (numbers != null) {
                Column(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(vertical = 8.dp)
                ) {
                    // 玩法名称和收藏图标
                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.SpaceBetween,
                        verticalAlignment = Alignment.CenterVertically
                    ) {
                        Text(
                            text = "${playType.displayName}:",
                            style = MaterialTheme.typography.bodyMedium.copy(
                                fontWeight = FontWeight.Bold,
                                fontSize = FontSizeSystem.getScaledSp(15.sp)
                            ),
                            color = MaterialTheme.colorScheme.onSurface,
                            modifier = Modifier.padding(bottom = 8.dp)
                        )

                        var isCollected by remember { mutableStateOf(false) }
                        LaunchedEffect(numbers, playType) {
                            if (numbers.isNotEmpty()) {
                                isCollected = homeViewModel.isNumberCollected(
                                    "快乐8", numbers, playType.playKey
                                )
                            }
                        }

                        IconButton(
                            onClick = {
                                if (numbers.isNotEmpty()) {
                                    val newCollectedState = !isCollected
                                    isCollected = newCollectedState
                                    homeViewModel.toggleCollectionForNumbers(
                                        "快乐8", numbers, playType.playKey
                                    )

                                    Toast.makeText(
                                        context,
                                        if (newCollectedState) "收藏成功" else "取消收藏",
                                        Toast.LENGTH_SHORT
                                    ).show()
                                }
                            }) {
                            Icon(
                                imageVector = if (isCollected) Icons.Filled.Favorite else Icons.Filled.FavoriteBorder,
                                contentDescription = if (isCollected) "已收藏" else "收藏",
                                tint = if (isCollected) Color(0xFFFFA000) else Color.Gray
                            )
                        }
                    }

                    // 号码显示
                    FlowRow(
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(horizontal = 8.dp),
                        horizontalArrangement = Arrangement.Center,
                        verticalArrangement = Arrangement.spacedBy(8.dp)
                    ) {
                        val numberList = numbers.split(" ").filter { it.isNotEmpty() }
                        numberList.forEachIndexed { index, num ->
                            NumberBall(number = num, isFront = true, isPlaceholder = false)
                            if (index < numberList.size - 1) {
                                Spacer(modifier = Modifier.width(8.dp))
                            }
                        }
                    }
                }
            }
        }

        // 添加预测时间显示
        Spacer(modifier = Modifier.height(16.dp))
        Row(
            modifier = Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text(
                text = "预测时间:${
                    SimpleDateFormat(
                        "yyyy-MM-dd HH:mm:ss", Locale.getDefault()
                    ).format(Date())
                }",
                style = MaterialTheme.typography.bodySmall,
                color = MaterialTheme.colorScheme.onSurfaceVariant
            )

            // 空的IconButton用于平衡布局
            Box(modifier = Modifier.size(48.dp)) {}
        }
    }
}

/**
 * 号码球组件,与生成号码和收藏列表的展示效果一致
 */
@Composable
fun NumberBall(number: String, isFront: Boolean, isPlaceholder: Boolean, isWinning: Boolean = false) {
    // 检查号码是否有效(非空且是数字)
    val isValidNumber = number.isNotBlank() && number.all { it.isDigit() }

    // 当号码无效时,显示--,背景为浅灰色
    val displayNumber = if (isValidNumber) number else "--"
    val ballColor = when {
        !isValidNumber -> Color.LightGray // 无效号码,浅灰色背景
        isPlaceholder -> MaterialTheme.colorScheme.surfaceVariant
        isFront -> LuckyRed40 // 红球保持原来的红色
        else -> LuckyBlue40 // 蓝球使用蓝色
    }

    Box(contentAlignment = Alignment.Center) {
        // 中奖号码的金色圆环和渐变效果
        if (isWinning) {
            Box(
                modifier = Modifier
                    .size(44.dp) // 增大圆环大小,增加间距
                    .border(
                        width = 2.dp,
                        brush = Brush.linearGradient(
                            colors = listOf(
                                Color(0xFFFFF8DC), // 特别浅的金色
                                Color(0xFFFFD700), // 金色
                                Color(0xFFFFA500), // 橙金色
                                Color(0xFFFFD700), // 金色
                                Color(0xFFFFF8DC)  // 特别浅的金色
                            )
                        ),
                        shape = CircleShape
                    )
            ) {}
        }

        Box(
            modifier = Modifier
                .size(32.dp) // 保持球的大小
                .background(
                    ballColor, CircleShape
                )
                .clip(CircleShape), contentAlignment = Alignment.Center
        ) {
            Text(
                text = displayNumber,
                color = Color.White,
                fontWeight = FontWeight.Bold,
                fontSize = 14.sp // 保持字体大小
            )
        }
    }
}

/**
 * 显示带有格式化号码的资讯
 */
@Composable
fun DisplayNewsWithNumbers(news: String) {
    Column(horizontalAlignment = Alignment.Start) {
        // 解析资讯文本,分离文字部分和号码部分
        val parts = news.split(":")
        if (parts.size >= 2) {
            val textPart = parts[0] + ":"
            val numberPart = parts[1].trim()

            // 显示文字部分
            Text(
                text = textPart, style = MaterialTheme.typography.bodyMedium.copy(
                    fontSize = FontSizeSystem.getScaledSp(14.sp)
                )
            )

            // 处理号码部分
            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.Start,
                verticalAlignment = Alignment.CenterVertically
            ) {
                // 检查是否包含分隔符 |
                if ("|" in numberPart) {
                    val numberGroups = numberPart.split("|")
                    val redNumbers = numberGroups[0].trim().split(" ").filter { it.isNotEmpty() }
                    val blueNumbers = numberGroups[1].trim().split(" ").filter { it.isNotEmpty() }

                    // 显示红球
                    redNumbers.forEachIndexed { index, num ->
                        NumberBall(number = num, isFront = true, isPlaceholder = false)
                        if (index < redNumbers.size - 1) {
                            Spacer(modifier = Modifier.width(8.dp))
                        }
                    }

                    // 显示分隔符
                    Spacer(modifier = Modifier.width(12.dp))
                    Text(
                        text = "|", style = MaterialTheme.typography.bodyMedium.copy(
                            fontSize = FontSizeSystem.getScaledSp(14.sp)
                        )
                    )
                    Spacer(modifier = Modifier.width(12.dp))

                    // 显示蓝球
                    blueNumbers.forEachIndexed { index, num ->
                        NumberBall(number = num, isFront = false, isPlaceholder = false)
                        if (index < blueNumbers.size - 1) {
                            Spacer(modifier = Modifier.width(8.dp))
                        }
                    }
                } else {
                    // 只有一组号码(如福彩3D)
                    val numbers = numberPart.split(" ").filter { it.isNotEmpty() }
                    numbers.forEachIndexed { index, num ->
                        NumberBall(number = num, isFront = true, isPlaceholder = false)
                        if (index < numbers.size - 1) {
                            Spacer(modifier = Modifier.width(8.dp))
                        }
                    }
                }
            }
        } else {
            // 如果没有冒号分隔,直接显示
            Text(
                text = news, style = MaterialTheme.typography.bodyMedium.copy(
                    fontSize = FontSizeSystem.getScaledSp(14.sp)
                )
            )
        }
    }
}

/**
 * 首页 - 提供彩票相关信息和AI预测功能
 */
@Composable
fun HomeScreen(
    navController: NavController, homeViewModel: HomeViewModel
) {
    // 使用rememberCoroutineScope代替GlobalScope
    val coroutineScope = rememberCoroutineScope()

    // API调用状态管理 - 从ViewModel获取
    val loadingStates by homeViewModel.loadingStates.collectAsState()

    // 存储不同彩票类型的预测结果 - 从ViewModel获取
    val predictionResults by homeViewModel.predictionResults.collectAsState()



    // 存储用户关注的彩票类型 - 从ViewModel获取
    val favoriteLotteryTypes by homeViewModel.favoriteLotteryTypes.collectAsState()

    // 根据用户关注的彩票类型过滤资讯
    val filteredLotteryNews = homeViewModel.getFilteredLotteryNews(favoriteLotteryTypes)

    // Toast上下文
    val context = LocalContext.current

    // 共享偏好设置,用于持久化存储
    val sharedPreferences =
        remember { context.getSharedPreferences("lottery_prefs", Context.MODE_PRIVATE) }

    // 验证彩票资讯结果是否有效
    fun validateLotteryNewsResult(result: String): Boolean {
        // 基本检查:结果不能为空,不能包含失败信息
        if (result.isBlank() || result.contains("API调用失败") || result.contains("调用失败")) {
            return false
        }

        // 检查是否包含当前年份的数据
        val currentYear = java.time.LocalDate.now().year.toString()
        val yearPattern = Regex("(\\d{4})年")
        val yearMatch = yearPattern.find(result)
        val hasCurrentYear = yearMatch != null && yearMatch.groupValues[1] == currentYear
        if (!hasCurrentYear) {
            return false
        }

        // 检查是否包含有效的号码数据
        // 有效号码格式:包含数字,且至少有一个彩票类型(双色球、大乐透、福彩3D、七星彩)
        val lotteryTypes = listOf("双色球", "大乐透", "福彩3D", "七星彩")
        val hasLotteryType = lotteryTypes.any { result.contains(it) }

        // 检查是否包含数字(至少1位数字即可)
        val numberPattern = Regex("\\d+")
        val hasNumbers = numberPattern.containsMatchIn(result)

        // 同时满足:有彩票类型、有数字,就认为数据有效
        return hasLotteryType && hasNumbers
    }

    // 调用DeepSeek API获取彩票资讯
    fun callDeepSeekApiForLotteryNews(
        forceCall: Boolean = false,
        favoriteTypes: List<String>? = null
    ) {
        // 每次都获取当前日期
        val currentDate = java.time.LocalDate.now().toString()

        // 从SharedPreferences获取最后调用日期
        val lastNewsCallDate = sharedPreferences.getString("last_news_call_date", "") ?: ""

        val shouldCall =
            forceCall || (lastNewsCallDate != currentDate && loadingStates["news"] != true)
        if (!shouldCall) return

        // 设置资讯的加载状态为true
        homeViewModel.updateLoadingState("news", true)
        Timber.d("callDeepSeekApiForLotteryNews API 请求 - favoriteTypes $favoriteTypes")

        coroutineScope.launch {
            try {
                // 调用DeepSeek API获取彩票资讯
                val result = DeepSeekApiUtils.getLotteryNews(favoriteTypes ?: emptyList())

                // 验证返回结果是否有效
                val isValidResult = validateLotteryNewsResult(result)

                if (isValidResult) {
                    // 解析资讯结果,按行分割
                    val newsItems = result.split("\n").filter { it.isNotBlank() }
                    homeViewModel.updateLotteryNews(newsItems)

                    // 保存到SharedPreferences
                    with(sharedPreferences.edit()) {
                        putString("lottery_news", newsItems.joinToString("\n"))
                        putString("news_date", currentDate)
                        putString("last_news_call_date", currentDate)
                        apply()
                    }
                } else {
                    // 如果获取失败或数据无效,清空资讯列表,显示"暂无资讯数据"
                    homeViewModel.updateLotteryNews(emptyList())
                }
            } catch (e: Exception) {
                // 处理异常,清空资讯列表,显示"暂无资讯数据"
                e.printStackTrace()
                homeViewModel.updateLotteryNews(emptyList())
            } finally {
                // 设置资讯的加载状态为false
                homeViewModel.updateLoadingState("news", false)
            }
        }
    }

    // 调用单个彩票类型的预测API
    fun callDeepSeekApiForSingleLottery(typeKey: String) {
        // 根据 typeKey 获取彩票类型枚举
        val lotteryType = LotteryType.fromTypeKey(typeKey) ?: LotteryType.DOUBLE_COLOR_BALL

        // 检查加载状态,避免重复请求
        if (loadingStates[typeKey] == true) return

        // 每次都获取当前日期
        val currentDate = java.time.LocalDate.now().toString()

        // 检查是否已有今天的有效数据
        val savedPredictionDate = sharedPreferences.getString("prediction_${typeKey}_date", "")
        val savedPrediction = sharedPreferences.getString("prediction_${typeKey}", "")

        if (savedPredictionDate == currentDate && !savedPrediction.isNullOrEmpty()) {
            // 已有今天有效数据,直接返回,不调用API
            return
        }

        // 没有今天数据或数据无效,调用API
        homeViewModel.updateLoadingState(typeKey, true)

        coroutineScope.launch {
            try {
                val result = if (lotteryType == LotteryType.HAPPY_8) {
                    DeepSeekApiUtils.predictHappy8AllPlayTypes()
                } else {
                    DeepSeekApiUtils.predictLotteryNumbers(typeKey)
                }

                // 检查返回结果是否包含错误信息
                if (result.contains("API调用失败") || result.contains("调用失败") || result.isBlank()) {
                    homeViewModel.updatePredictionResult(typeKey, "")
                    // 删除可能存在的缓存
                    with(sharedPreferences.edit()) {
                        remove("prediction_${typeKey}")
                        remove("prediction_${typeKey}_date")
                        apply()
                    }
                } else {
                    // 快乐8特殊处理:直接保存所有玩法的预测结果
                    if (lotteryType == LotteryType.HAPPY_8) {
                        // 快乐8的预测结果包含所有玩法,直接保存
                        homeViewModel.updatePredictionResult(typeKey, result)
                        // 保存到SharedPreferences
                        with(sharedPreferences.edit()) {
                            putString("prediction_${typeKey}", result)
                            putString("prediction_${typeKey}_date", currentDate)
                            apply()
                        }
                        // 数据合法且存储成功,自动展开卡片
                        val typeName = lotteryType.chineseName
                        homeViewModel.setPredictionCardExpanded(typeName, true)
                    } else {
                        // 其他彩票类型:抽取号码并验证格式
                        val extractedNumbers =
                            LotteryNumberGenerator.extractNumbersFromPrediction(result, typeKey)
                        Timber.d("抽取到的号码: $extractedNumbers, 彩票类型: $typeKey")
                        if (LotteryNumberGenerator.isValidNumberFormat(extractedNumbers, typeKey)) {
                            homeViewModel.updatePredictionResult(typeKey, result)
                            // 保存到SharedPreferences
                            with(sharedPreferences.edit()) {
                                putString("prediction_${typeKey}", result)
                                putString("prediction_${typeKey}_date", currentDate)
                                apply()
                            }
                            // 数据合法且存储成功,自动展开卡片
                        val typeName = lotteryType.chineseName
                        homeViewModel.setPredictionCardExpanded(typeName, true)
                        } else {
                            // 号码格式不正确,认为请求不成功
                            homeViewModel.updatePredictionResult(typeKey, "")
                            // 删除可能存在的缓存
                            with(sharedPreferences.edit()) {
                                remove("prediction_${typeKey}")
                                remove("prediction_${typeKey}_date")
                                apply()
                            }
                        }
                    }
                }
            } catch (e: Exception) {
                // 如果预测失败,设置为空字符串,后续显示暂无数据
                homeViewModel.updatePredictionResult(typeKey, "")
                // 删除可能存在的缓存
                with(sharedPreferences.edit()) {
                    remove("prediction_${typeKey}")
                    remove("prediction_${typeKey}_date")
                    apply()
                }
            } finally {
                // 设置该彩票类型的加载状态为false
                homeViewModel.updateLoadingState(typeKey, false)
            }
        }
    }

    // 从SharedPreferences加载存储的数据
    LaunchedEffect(Unit) {
        // 初始化用户关注的彩票类型
        homeViewModel.initUserPreferences()

        // 每次都获取当前日期
        val currentDate = java.time.LocalDate.now().toString()

        // 加载彩票资讯
        val savedNews = sharedPreferences.getString("lottery_news", "")
        val savedNewsDate = sharedPreferences.getString("news_date", "")

        // 只有当保存的日期是今天时,才加载保存的资讯
        if (savedNewsDate == currentDate && !savedNews.isNullOrEmpty()) {
            val newsList = savedNews.split("\n").filter { it.isNotBlank() }
            homeViewModel.updateLotteryNews(newsList)
        }

        // 彩票类型列表
        val lotteryTypes = LotteryType.values()

        // 检查并处理预测结果缓存
        lotteryTypes.forEach { lotteryType ->
            val typeKey = lotteryType.typeKey
            val savedPredictionDate = sharedPreferences.getString("prediction_${typeKey}_date", "")

            // 如果缓存的日期不是今天,删除之前的缓存
            if (savedPredictionDate != currentDate) {
                with(sharedPreferences.edit()) {
                    remove("prediction_${typeKey}")
                    remove("prediction_${typeKey}_date")
                    apply()
                }
            } else {
                // 如果是今天的缓存,加载保存的预测结果
                val savedPrediction = sharedPreferences.getString("prediction_${typeKey}", "")
                if (!savedPrediction.isNullOrEmpty()) {
                    // 快乐8特殊处理:直接加载所有玩法的预测结果
                    if (lotteryType == LotteryType.HAPPY_8) {
                        homeViewModel.updatePredictionResult(typeKey, savedPrediction)
                    } else {
                        // 其他彩票类型:验证预测结果是否可以正确抽取号码
                        val extractedNumbers = LotteryNumberGenerator.extractNumbersFromPrediction(
                            savedPrediction, typeKey
                        )
                        if (LotteryNumberGenerator.isValidNumberFormat(extractedNumbers, typeKey)) {
                            homeViewModel.updatePredictionResult(typeKey, savedPrediction)
                        } else {
                            // 如果无法正确抽取号码,清除本地缓存
                            with(sharedPreferences.edit()) {
                                remove("prediction_${typeKey}")
                                remove("prediction_${typeKey}_date")
                                apply()
                            }
                        }
                    }
                }
            }
        }
    }

        // 监听用户关注的彩票类型变化,当变化时重新查询资讯
        LaunchedEffect(favoriteLotteryTypes) {
            val lastNewsCallDate = sharedPreferences.getString("last_news_call_date", "") ?: ""
            val currentDate = java.time.LocalDate.now().toString()
            if (lastNewsCallDate != currentDate && loadingStates["news"] != true && favoriteLotteryTypes.isNotEmpty()) {
                callDeepSeekApiForLotteryNews(favoriteTypes = favoriteLotteryTypes)
            }
        }

        // 每次进入首页时,重置规则卡片展开状态,所有卡片都收起
        LaunchedEffect(Unit) {
            homeViewModel.resetRuleCardsExpandedState()
        }

        Scaffold(
            modifier = Modifier.fillMaxSize(),
            containerColor = MaterialTheme.colorScheme.background,
            content = {
                Column(
                    modifier = Modifier
                        .fillMaxSize()
                        .background(MaterialTheme.colorScheme.background) // 添加背景色,避免透明区域
                ) {
                    // 顶部导航栏
                    TopNavBar(title = "首页")

                    // 内容区域
                    LazyColumn(
                        modifier = Modifier
                            .fillMaxWidth()
                            .weight(1f)
                            .padding(it) // 使用Scaffold提供的padding
                    ) {
                        // 内容正常显示,不添加固定底部边距
                        // 彩票资讯卡片
                        item {
                            Card(
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .padding(16.dp),
                                elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
                                shape = RoundedCornerShape(16.dp),
                                colors = CardDefaults.cardColors(
                                    containerColor = Color.Transparent
                                )
                            ) {
                                Box(
                                    modifier = Modifier.background(Color(0xFFFFF3E0)) // lucky_gold_200 背景
                                ) {
                                    Column(modifier = Modifier.padding(16.dp)) {
                                        // 彩票资讯标题行,包含"更多"入口
                                        Row(
                                            modifier = Modifier.fillMaxWidth(),
                                            horizontalArrangement = Arrangement.SpaceBetween,
                                            verticalAlignment = Alignment.CenterVertically
                                        ) {
                                            Text(
                                                text = "彩票资讯",
                                                style = MaterialTheme.typography.headlineMedium.copy(
                                                    fontWeight = FontWeight.Bold,
                                                    fontSize = FontSizeSystem.getScaledSp(22.sp)
                                                )
                                            )
                                            TextButton(
                                                onClick = {
                                                    navController.navigate(Destinations.MoreNews.route)
                                                },
                                                modifier = Modifier.padding(0.dp),
                                                colors = ButtonDefaults.textButtonColors(
                                                    contentColor = Color(0xFFFFA000) // lucky_gold_500,与幸运之星颜色一致
                                                )
                                            ) {
                                                Text(
                                                    text = "更多",
                                                    style = MaterialTheme.typography.bodyMedium.copy(
                                                        fontSize = FontSizeSystem.getScaledSp(14.sp)
                                                    )
                                                )
                                            }
                                        }
                                        Spacer(modifier = Modifier.height(16.dp))

                                        // 从API获取的资讯列表
                                        if (filteredLotteryNews.isEmpty()) {
                                            // 显示加载中或暂无数据
                                            Card(
                                                modifier = Modifier
                                                    .fillMaxWidth()
                                                    .padding(0.dp),
                                                elevation = CardDefaults.cardElevation(
                                                    defaultElevation = 2.dp
                                                ),
                                                shape = RoundedCornerShape(8.dp),
                                                colors = CardDefaults.cardColors(containerColor = Color.Transparent)
                                            ) {
                                                Box(
                                                    modifier = Modifier
                                                        .fillMaxWidth()
                                                        .background(
                                                            Color.White, RoundedCornerShape(8.dp)
                                                        ) // 白色背景和圆角
                                                        .padding(24.dp),
                                                    contentAlignment = Alignment.Center
                                                ) {
                                                    TextButton(
                                                        onClick = {
                                                            callDeepSeekApiForLotteryNews(
                                                                forceCall = true,
                                                                favoriteTypes = favoriteLotteryTypes
                                                            )
                                                        }, colors = ButtonDefaults.textButtonColors(
                                                            contentColor = if (loadingStates["news"] == true) Color(
                                                                0xFFFFA000
                                                            ) else Color.Black
                                                        )
                                                    ) {
                                                        Text(
                                                            text = if (loadingStates["news"] == true) "加载中..." else "暂无资讯数据,点击重试",
                                                            style = MaterialTheme.typography.bodyMedium.copy(
                                                                fontSize = FontSizeSystem.getScaledSp(
                                                                    14.sp
                                                                )
                                                            ),
                                                            textAlign = TextAlign.Center
                                                        )
                                                    }
                                                }
                                            }
                                        } else {
                                            Card(
                                                modifier = Modifier
                                                    .fillMaxWidth()
                                                    .padding(0.dp),
                                                elevation = CardDefaults.cardElevation(
                                                    defaultElevation = 2.dp
                                                ),
                                                shape = RoundedCornerShape(8.dp),
                                                colors = CardDefaults.cardColors(containerColor = Color.Transparent)
                                            ) {
                                                Box(
                                                    modifier = Modifier
                                                        .fillMaxWidth()
                                                        .background(
                                                            Color.White, RoundedCornerShape(8.dp)
                                                        ) // 白色背景和圆角
                                                        .padding(12.dp)
                                                ) {
                                                    Column(
                                                        modifier = Modifier.fillMaxWidth(),
                                                        verticalArrangement = Arrangement.spacedBy(8.dp)
                                                    ) {
                                                        filteredLotteryNews.forEach { news ->
                                                            DisplayNewsWithNumbers(news)
                                                        }
                                                    }
                                                }
                                            }
                                        }

                                        // 添加小贴士提示文字
                                        Text(
                                            text = "小贴士:资讯可能存在延迟或误差,购彩时请务必核对官方开奖信息。",
                                            style = MaterialTheme.typography.bodySmall,
                                            color = Color.Gray,
                                            textAlign = TextAlign.Center,
                                            modifier = Modifier
                                                .fillMaxWidth()
                                                .padding(top = 16.dp)
                                        )
                                    }
                                }
                            }
                        }

                        // AI预测卡片
                        item {
                            Card(
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .padding(16.dp),
                                elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
                                shape = RoundedCornerShape(16.dp),
                                colors = CardDefaults.cardColors(
                                    containerColor = Color.Transparent
                                )
                            ) {
                                Box(
                                    modifier = Modifier.background(Color(0xFFFFF3E0)) // lucky_gold_200 背景
                                ) {
                                    Column(modifier = Modifier.padding(16.dp)) {
                                        // AI智能预测标题行,包含"更多"入口
                                        Row(
                                            modifier = Modifier.fillMaxWidth(),
                                            horizontalArrangement = Arrangement.SpaceBetween,
                                            verticalAlignment = Alignment.CenterVertically
                                        ) {
                                            Row(
                                                verticalAlignment = Alignment.CenterVertically
                                            ) {
                                                Text(
                                                    text = "AI趣味预测",
                                                    style = MaterialTheme.typography.headlineMedium.copy(
                                                        fontWeight = FontWeight.Bold,
                                                        fontSize = FontSizeSystem.getScaledSp(22.sp)
                                                    )
                                                )
                                                Spacer(modifier = Modifier.width(8.dp))
                                                Text(
                                                    text = "每日一测",
                                                    style = MaterialTheme.typography.bodySmall.copy(
                                                        fontSize = FontSizeSystem.getScaledSp(12.sp)
                                                    ),
                                                    color = Color(0xFFFFA000),
                                                    fontWeight = FontWeight.Medium
                                                )
                                            }
                                            TextButton(
                                                onClick = {
                                                    navController.navigate(Destinations.MorePredictions.route)
                                                },
                                                modifier = Modifier.padding(0.dp),
                                                colors = ButtonDefaults.textButtonColors(
                                                    contentColor = Color(0xFFFFA000) // lucky_gold_500,与幸运之星颜色一致
                                                )
                                            ) {
                                                Text(
                                                    text = "更多",
                                                    style = MaterialTheme.typography.bodyMedium.copy(
                                                        fontSize = FontSizeSystem.getScaledSp(14.sp)
                                                    )
                                                )
                                            }
                                        }
                                        Spacer(modifier = Modifier.height(16.dp))
                                        // AI预测内容 - 只展示用户关注的彩票类型
                                        favoriteLotteryTypes.forEach { typeKey ->
                                            val lotteryType = LotteryType.fromTypeKey(typeKey)
                                            if (lotteryType != null) {
                                                AIpredictionCard(
                                                    typeKey = typeKey,
                                                    predictionResult = predictionResults[typeKey]
                                                        ?: "",
                                                    isLoading = loadingStates[typeKey] ?: false,
                                                    onClick = {
                                                        Timber.d("预测卡片点击事件 -- 卡片外部: $typeKey, 彩票类型: $lotteryType")
                                                        callDeepSeekApiForSingleLottery(
                                                            typeKey
                                                        )
                                                    },
                                                    homeViewModel = homeViewModel
                                                )
                                                Spacer(modifier = Modifier.height(16.dp))
                                            }
                                        }

                                        // 添加AI预测提示文字
                                        Column(
                                            modifier = Modifier.fillMaxWidth(),
                                            horizontalAlignment = Alignment.Start
                                        ) {
                                            Text(
                                                text = "本功能基于算法模型进行历史数据分析,生成趣味预测结果,仅供娱乐参考。",
                                                style = MaterialTheme.typography.bodySmall.copy(
                                                    fontSize = 12.sp
                                                ),
                                                color = Color(0xFF666666),
                                                modifier = Modifier.padding(vertical = 8.dp)
                                            )

                                            Text(
                                                text = "重要提醒:",
                                                style = MaterialTheme.typography.bodySmall.copy(
                                                    fontSize = 12.sp, fontWeight = FontWeight.Bold
                                                ),
                                                color = Color(0xFF666666)
                                            )

                                            Text(
                                                text = "• 彩票开奖是独立的随机事件",
                                                style = MaterialTheme.typography.bodySmall.copy(
                                                    fontSize = 12.sp
                                                ),
                                                color = Color(0xFF666666)
                                            )

                                            Text(
                                                text = "• 历史数据不代表未来趋势",
                                                style = MaterialTheme.typography.bodySmall.copy(
                                                    fontSize = 12.sp
                                                ),
                                                color = Color(0xFF666666)
                                            )

                                            Text(
                                                text = "• 每期开奖所有号码概率均等",
                                                style = MaterialTheme.typography.bodySmall.copy(
                                                    fontSize = 12.sp
                                                ),
                                                color = Color(0xFF666666)
                                            )

                                            Text(
                                                text = "• 无人能够准确预测开奖结果",
                                                style = MaterialTheme.typography.bodySmall.copy(
                                                    fontSize = 12.sp
                                                ),
                                                color = Color(0xFF666666)
                                            )

                                            Text(
                                                text = "愿您在享受AI分析乐趣的同时,保持理性、量力参与。",
                                                style = MaterialTheme.typography.bodySmall.copy(
                                                    fontSize = 12.sp
                                                ),
                                                color = Color(0xFF666666),
                                                modifier = Modifier.padding(top = 8.dp)
                                            )
                                        }
                                    }
                                }
                            }
                        }

                        // 彩票规则卡片
                        item {
                            Card(
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .padding(16.dp)
                                    .padding(bottom = 16.dp),
                                elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
                                shape = RoundedCornerShape(16.dp),
                                colors = CardDefaults.cardColors(
                                    containerColor = Color.Transparent
                                )
                            ) {
                                Box(
                                    modifier = Modifier.background(Color(0xFFFFF3E0)) // lucky_gold_200 背景
                                ) {
                                    Column(modifier = Modifier.padding(16.dp)) {
                                        // 彩票规则标题行,包含"更多"入口
                                        Row(
                                            modifier = Modifier.fillMaxWidth(),
                                            horizontalArrangement = Arrangement.SpaceBetween,
                                            verticalAlignment = Alignment.CenterVertically
                                        ) {
                                            Text(
                                                text = "彩票规则",
                                                style = MaterialTheme.typography.headlineMedium.copy(
                                                    fontWeight = FontWeight.Bold,
                                                    fontSize = FontSizeSystem.getScaledSp(22.sp)
                                                )
                                            )
                                            TextButton(
                                                onClick = {
                                                    navController.navigate(Destinations.MoreRules.route)
                                                },
                                                modifier = Modifier.padding(0.dp),
                                                colors = ButtonDefaults.textButtonColors(
                                                    contentColor = Color(0xFFFFA000) // lucky_gold_500,与幸运之星颜色一致
                                                )
                                            ) {
                                                Text(
                                                    text = "更多",
                                                    style = MaterialTheme.typography.bodyMedium.copy(
                                                        fontSize = FontSizeSystem.getScaledSp(14.sp)
                                                    )
                                                )
                                            }
                                        }
                                        Spacer(modifier = Modifier.height(16.dp))
                                        // 彩票规则折叠面板 - 只展示用户关注的彩票类型
                                        val lotteryRules =
                                            favoriteLotteryTypes.mapNotNull { typeKey ->
                                                val lotteryType = LotteryType.fromTypeKey(typeKey)
                                                if (lotteryType != null) {
                                                    lotteryType to when (lotteryType) {
                                                        LotteryType.DOUBLE_COLOR_BALL -> listOf(
                                                            "玩法规则" to "从1-33中选择6个红球,从1-16中选择1个蓝球。",
                                                            "中奖规则" to listOf(
                                                                "6+1(一等奖)",
                                                                "6+0(二等奖)",
                                                                "5+1(三等奖)",
                                                                "5+0/4+1(四等奖)",
                                                                "4+0/3+1(五等奖)",
                                                                "2+1/1+1/0+1(六等奖)"
                                                            )
                                                        )

                                                        LotteryType.POWERBALL -> listOf(
                                                            "玩法规则" to "从1-35中选择5个前区球,从1-12中选择2个后区球。",
                                                            "中奖规则" to listOf(
                                                                "5+2(一等奖)",
                                                                "5+1(二等奖)",
                                                                "5+0/4+2(三等奖)",
                                                                "4+1/3+2(四等奖)",
                                                                "4+0/3+1/2+2(五等奖)",
                                                                "3+0/1+2/2+1/0+2(六等奖)"
                                                            )
                                                        )

                                                        LotteryType.THREE_D -> listOf(
                                                            "玩法规则" to "从0-9中选择3个数字。",
                                                            "中奖规则" to listOf(
                                                                "单选(数字和顺序完全匹配)",
                                                                "组选3(两个相同数字+一个不同数字,顺序不限)",
                                                                "组选6(三个不同数字,顺序不限)"
                                                            )
                                                        )

                                                        LotteryType.SEVEN_STAR -> listOf(
                                                            "玩法规则" to "从0-9中选择7个数字,前5个为红球,后2个为蓝球。",
                                                            "中奖规则" to listOf(
                                                                "7位数字完全匹配(一等奖)",
                                                                "连续6位数字匹配(二等奖)",
                                                                "连续5位数字匹配(三等奖)",
                                                                "连续4位数字匹配(四等奖)",
                                                                "连续3位数字匹配(五等奖)",
                                                                "连续2位数字匹配(六等奖)"
                                                            )
                                                        )

                                                        LotteryType.SEVEN_LE -> listOf(
                                                            "玩法规则" to "从1-30中选择7个基本号,从1-30中选择1个特别号。",
                                                            "中奖规则" to listOf(
                                                                "7+1(一等奖)",
                                                                "7+0(二等奖)",
                                                                "6+1(三等奖)",
                                                                "6+0(四等奖)",
                                                                "5+1/4+2(五等奖)",
                                                                "5+0/4+1/3+2(六等奖)",
                                                                "4+0/3+1/2+2(七等奖)"
                                                            )
                                                        )

                                                        LotteryType.PERMUTATION_3 -> listOf(
                                                            "玩法规则" to "从0-9中选择3个数字。",
                                                            "中奖规则" to listOf(
                                                                "直选(数字和顺序完全匹配)",
                                                                "组选3(两个相同数字+一个不同数字,顺序不限)",
                                                                "组选6(三个不同数字,顺序不限)"
                                                            )
                                                        )

                                                        LotteryType.PERMUTATION_5 -> listOf(
                                                            "玩法规则" to "从0-9中选择5个数字。",
                                                            "中奖规则" to listOf(
                                                                "直选(数字和顺序完全匹配)",
                                                                "组选120(五个不同数字,顺序不限)",
                                                                "组选60(四个不同数字,顺序不限)",
                                                                "组选30(三个不同数字,顺序不限)",
                                                                "组选20(两个不同数字,顺序不限)",
                                                                "组选10(两个相同数字,顺序不限)",
                                                                "组选5(五个相同数字)"
                                                            )
                                                        )

                                                        LotteryType.HAPPY_8 -> listOf(
                                                            "玩法规则" to "从1-80中选择1-10个号码。",
                                                            "中奖规则" to listOf(
                                                                "选10中10(一等奖)",
                                                                "选10中9(二等奖)",
                                                                "选10中8(三等奖)",
                                                                "选10中7(四等奖)",
                                                                "选10中6(五等奖)",
                                                                "选10中5(六等奖)",
                                                                "选10中4(七等奖)",
                                                                "选10中3(八等奖)",
                                                                "选10中2(九等奖)",
                                                                "选10中1(十等奖)"
                                                            )
                                                        )
                                                    }
                                                } else {
                                                    null
                                                }
                                            }

                                        // 为每种彩票类型创建可折叠的规则卡片
                                        val expandedRuleCards by homeViewModel.expandedRuleCards.collectAsState()
                                        lotteryRules.forEach { (lotteryType, rules) ->
                                            val isExpanded = expandedRuleCards.getOrDefault(
                                                lotteryType.typeKey, false
                                            )

                                            Card(
                                                modifier = Modifier
                                                    .fillMaxWidth()
                                                    .wrapContentHeight()
                                                    .padding(vertical = 8.dp)
                                                    .clickable {
                                                        homeViewModel.toggleRuleCardExpanded(
                                                            lotteryType.typeKey
                                                        )
                                                    },
                                                elevation = CardDefaults.cardElevation(
                                                    defaultElevation = 2.dp
                                                ),
                                                shape = RoundedCornerShape(8.dp),
                                                colors = CardDefaults.cardColors(
                                                    containerColor = Color.Transparent
                                                )
                                            ) {
                                                Box(
                                                    modifier = Modifier
                                                        .fillMaxWidth()
                                                        .background(
                                                            Color.White, RoundedCornerShape(8.dp)
                                                        )
                                                        .padding(16.dp)
                                                ) {
                                                    Column {
                                                        Row(
                                                            modifier = Modifier.fillMaxWidth(),
                                                            horizontalArrangement = Arrangement.SpaceBetween,
                                                            verticalAlignment = Alignment.CenterVertically
                                                        ) {
                                                            Text(
                                                                text = lotteryType.chineseName,
                                                                style = MaterialTheme.typography.bodyLarge.copy(
                                                                    fontWeight = FontWeight.Bold
                                                                )
                                                            )
                                                            Icon(
                                                                imageVector = if (isExpanded) Icons.Filled.ArrowDropUp else Icons.Filled.ArrowDropDown,
                                                                contentDescription = if (isExpanded) "收起" else "展开"
                                                            )
                                                        }

                                                        AnimatedVisibility(
                                                            visible = isExpanded,
                                                            enter = expandVertically(
                                                                animationSpec = tween(
                                                                    durationMillis = 300,
                                                                    easing = FastOutSlowInEasing
                                                                )
                                                            ) + fadeIn(
                                                                animationSpec = tween(
                                                                    durationMillis = 300,
                                                                    easing = FastOutSlowInEasing
                                                                )
                                                            ),
                                                            exit = shrinkVertically(
                                                                animationSpec = tween(
                                                                    durationMillis = 300,
                                                                    easing = FastOutLinearInEasing
                                                                )
                                                            ) + fadeOut(
                                                                animationSpec = tween(
                                                                    durationMillis = 300,
                                                                    easing = FastOutLinearInEasing
                                                                )
                                                            )
                                                        ) {
                                                            Column(modifier = Modifier.fillMaxWidth()) {
                                                                rules.forEachIndexed { index, (ruleTitle, ruleContent) ->
                                                                    // 每个规则项使用独立的Column,确保布局隔离
                                                                    Column(
                                                                        modifier = Modifier
                                                                            .fillMaxWidth()
                                                                            .wrapContentHeight()
                                                                    ) {
                                                                        // 标题部分
                                                                        Text(
                                                                            text = ruleTitle,
                                                                            style = MaterialTheme.typography.bodyMedium.copy(
                                                                                fontWeight = FontWeight.SemiBold
                                                                            ),
                                                                            modifier = Modifier
                                                                                .fillMaxWidth()
                                                                                .padding(vertical = 12.dp)
                                                                        )

                                                                        Spacer(
                                                                            modifier = Modifier.height(
                                                                                8.dp
                                                                            )
                                                                        )
                                                                        when (ruleContent) {
                                                                            is String -> {
                                                                                Text(
                                                                                    text = ruleContent,
                                                                                    style = MaterialTheme.typography.bodyMedium,
                                                                                    modifier = Modifier
                                                                                        .fillMaxWidth()
                                                                                        .padding(
                                                                                            start = 12.dp,
                                                                                            bottom = 8.dp
                                                                                        ),
                                                                                    // 移除 maxLines = Int.MAX_VALUE,让文本自然换行
                                                                                    // lineHeight 可以帮助改善多行文本的显示
                                                                                    lineHeight = 20.sp
                                                                                )
                                                                            }

                                                                            is List<*> -> {
                                                                                Column(
                                                                                    modifier = Modifier
                                                                                        .fillMaxWidth()
                                                                                        .padding(
                                                                                            start = 12.dp,
                                                                                            bottom = 12.dp
                                                                                        )
                                                                                ) {
                                                                                    ruleContent.forEach { item ->
                                                                                        if (item is String) {
                                                                                            Text(
                                                                                                text = "• $item",
                                                                                                style = MaterialTheme.typography.bodyMedium,
                                                                                                modifier = Modifier
                                                                                                    .fillMaxWidth()
                                                                                                    .padding(
                                                                                                        bottom = 4.dp
                                                                                                    ),
                                                                                                lineHeight = 20.sp
                                                                                            )
                                                                                        }
                                                                                    }
                                                                                }
                                                                            }
                                                                        }

                                                                        if (index < rules.size - 1) {
                                                                            Spacer(
                                                                                modifier = Modifier.height(
                                                                                    16.dp
                                                                                )
                                                                            )
                                                                        }
                                                                    }

                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }

                                        // 添加温馨提示文字
                                        Text(
                                            text = "温馨提示:信息可能存在时间延迟或区域差异,请务必以彩票官方最新公告为准。",
                                            style = MaterialTheme.typography.bodySmall,
                                            color = Color.Gray,
                                            textAlign = TextAlign.Center,
                                            modifier = Modifier
                                                .fillMaxWidth()
                                                .padding(top = 16.dp)
                                        )
                                    }
                                }
                            }
                        }
                    }
                }

            })
    }

/**
 * AI预测卡片
 */
@Composable
fun AIpredictionCard(
    typeKey: String,
    predictionResult: String = "",
    isLoading: Boolean = false,
    onClick: () -> Unit = {},
    homeViewModel: HomeViewModel
) {
    // 根据 typeKey 获取彩票类型枚举
    val lotteryType = LotteryType.fromTypeKey(typeKey) ?: LotteryType.DOUBLE_COLOR_BALL
    val typeName = lotteryType.chineseName

    // 使用ViewModel中的状态管理展开状态,使用collectAsState()监听变化
    val expandedCards by homeViewModel.expandedPredictionCards.collectAsState()
    val isExpanded = expandedCards.getOrDefault(typeName, false)
    var isCollected by remember { mutableStateOf(false) } // 收藏状态
    val context = LocalContext.current // 移到Composable函数顶部

    // 当预测结果变化时,检查号码是否已被收藏
    LaunchedEffect(predictionResult, typeKey) {
        if (predictionResult.isNotEmpty()) {
            val extractedNumbers = LotteryNumberGenerator.extractNumbersFromPrediction(
                predictionResult, typeKey
            )
            if (LotteryNumberGenerator.isValidNumberFormat(extractedNumbers, typeKey)) {
                isCollected = homeViewModel.isNumberCollected(typeName, extractedNumbers)
            }
        } else {
            isCollected = false
        }
    }

    Card(
        colors = CardDefaults.cardColors(
            containerColor = Color.Transparent // 卡片背景设置为透明,让内层Box的背景色生效
        ),
        modifier = Modifier
            .fillMaxWidth()
            .wrapContentHeight()
            .clickable {
                Timber.d("预测卡片点击事件 -- AIpredictionCard内部: $typeKey, 彩票类型: $lotteryType")
                if (predictionResult.isEmpty() && !isLoading) {
                    // 如果没有预测结果且不在加载中,调用API
                    onClick()
                } else {
                    // 否则切换展开状态
                    homeViewModel.togglePredictionCardExpanded(typeName)
                }
            },
        elevation = CardDefaults.cardElevation(defaultElevation = 2.dp),
        shape = RoundedCornerShape(8.dp)
    ) {
        // 添加一层Box来设置白色背景,这层Box将覆盖整个Card内容区域
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .background(Color.White, RoundedCornerShape(8.dp)) // 白色背景和圆角
                .padding(12.dp)
        ) {
            Column {
                // 标题行
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.Center,
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Text(
                        text = "${typeName}下期预测",
                        style = MaterialTheme.typography.bodyLarge.copy(
                            fontWeight = FontWeight.Bold,
                            fontSize = FontSizeSystem.getScaledSp(16.sp)
                        )
                    )
                }

                Spacer(modifier = Modifier.height(8.dp))

                // 加载状态显示 - 不依赖于展开状态
                if (isLoading) {
                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.Center,
                        verticalAlignment = Alignment.CenterVertically
                    ) {
                        CircularProgressIndicator(modifier = Modifier.size(32.dp))
                        Spacer(modifier = Modifier.width(8.dp))
                        Text(
                            text = "正在获取预测数据...",
                            style = MaterialTheme.typography.bodyMedium,
                            color = Color(0xFFFFA000)
                        )
                    }
                    Spacer(modifier = Modifier.height(16.dp))
                } else {
                    AnimatedVisibility(
                        visible = isExpanded, enter = expandVertically(
                            animationSpec = tween(
                                durationMillis = 300, easing = FastOutSlowInEasing
                            )
                        ) + fadeIn(
                            animationSpec = tween(
                                durationMillis = 300, easing = FastOutSlowInEasing
                            )
                        ), exit = shrinkVertically(
                            animationSpec = tween(
                                durationMillis = 300, easing = FastOutLinearInEasing
                            )
                        ) + fadeOut(
                            animationSpec = tween(
                                durationMillis = 300, easing = FastOutLinearInEasing
                            )
                        )
                    ) {
                        Column(
                            modifier = Modifier
                                .fillMaxWidth()
                                .background(
                                    Color.White, // 白色背景
                                    RoundedCornerShape(8.dp) // 添加圆角效果
                                ),
                            verticalArrangement = Arrangement.Top,
                            horizontalAlignment = Alignment.CenterHorizontally
                        ) {
                            if (predictionResult.isNotEmpty()) {
                                val extractedNumbers =
                                    LotteryNumberGenerator.extractNumbersFromPrediction(
                                        predictionResult, typeKey
                                    )

                                if (lotteryType == LotteryType.HAPPY_8) {
                                    val happy8Prediction =
                                        LotteryNumberGenerator.extractHappy8AllPlayTypesPrediction(
                                            predictionResult
                                        )
                                    DisplayHappy8AllPlayTypesPrediction(
                                        happy8Prediction, homeViewModel, context
                                    )
                                } else {
                                    DisplayFormattedNumbers(extractedNumbers, typeName)
                                }

                                Spacer(modifier = Modifier.height(16.dp))

                                if (lotteryType != LotteryType.HAPPY_8) {
                                    Row(
                                        modifier = Modifier.fillMaxWidth(),
                                        horizontalArrangement = Arrangement.SpaceBetween,
                                        verticalAlignment = Alignment.CenterVertically
                                    ) {
                                        Text(
                                            text = "预测时间:${
                                                SimpleDateFormat(
                                                    "yyyy-MM-dd HH:mm:ss", Locale.getDefault()
                                                ).format(Date())
                                            }",
                                            style = MaterialTheme.typography.bodySmall,
                                            color = MaterialTheme.colorScheme.onSurfaceVariant
                                        )

                                        IconButton(
                                            onClick = {
                                                if (LotteryNumberGenerator.isValidNumberFormat(
                                                        extractedNumbers, typeKey
                                                    )
                                                ) {
                                                    val newCollectedState = !isCollected
                                                    isCollected = newCollectedState

                                                    homeViewModel.toggleCollectionForNumbers(
                                                        typeName, extractedNumbers
                                                    )

                                                    Toast.makeText(
                                                        context,
                                                        if (newCollectedState) "收藏成功" else "取消收藏",
                                                        Toast.LENGTH_SHORT
                                                    ).show()
                                                } else {
                                                    Toast.makeText(
                                                        context,
                                                        "号码格式不正确,收藏失败",
                                                        Toast.LENGTH_SHORT
                                                    ).show()
                                                }
                                            }) {
                                                Icon(
                                                    imageVector = if (isCollected) Icons.Filled.Favorite else Icons.Filled.FavoriteBorder,
                                                    contentDescription = if (isCollected) "已收藏" else "收藏",
                                                    tint = if (isCollected) Color(0xFFFFA000) else Color.Gray
                                                )
                                            }
                                    }
                                }
                            }
                        }
                    }
                }

                if (!isLoading && !isExpanded) {
                    if (predictionResult.isNotEmpty()) {
                        Text(
                            text = "点击收起/展开预测结果",
                            style = MaterialTheme.typography.bodyMedium,
                            color = MaterialTheme.colorScheme.onSurfaceVariant,
                            modifier = Modifier.fillMaxWidth(),
                            textAlign = TextAlign.Center
                        )
                    } else {
                        Text(
                            text = "点击获取预测数据",
                            style = MaterialTheme.typography.bodyMedium,
                            color = Color(0xFFFFA000),
                            fontWeight = FontWeight.Bold,
                            modifier = Modifier.fillMaxWidth(),
                            textAlign = TextAlign.Center
                        )
                    }

                }
            }
        }
    }
}

package com.smallsnailtech.luckyticket.ui.screen.collections

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.FavoriteBorder
import androidx.compose.material.icons.filled.Home
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import android.widget.Toast
import com.smallsnailtech.luckyticket.data.local.entity.LotteryNumber
import com.smallsnailtech.luckyticket.model.GenerationType
import com.smallsnailtech.luckyticket.model.LotteryType
import com.smallsnailtech.luckyticket.model.Happy8PlayType
import com.smallsnailtech.luckyticket.ui.components.TopNavBar
import kotlinx.coroutines.launch
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.style.TextAlign
import com.smallsnailtech.luckyticket.ui.theme.LuckyBlue40
import com.smallsnailtech.luckyticket.ui.theme.LuckyRed40
import com.smallsnailtech.luckyticket.utils.FontSizeSystem
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.ExperimentalLayoutApi

@Composable
fun CollectionsScreen(navController: NavController) {
    val viewModel: CollectionsViewModel = hiltViewModel()
    val allNumbers by viewModel.collectedNumbers.collectAsState(emptyList())
    val isLoading by viewModel.isLoading.collectAsState(false)
    val hasMoreData by viewModel.hasMoreData.collectAsState(true)
    val coroutineScope = rememberCoroutineScope()
    
    // Toast上下文
    val context = LocalContext.current
    
    // 搜索状态
    var searchQuery by remember { mutableStateOf("") }
    var selectedType by remember { mutableStateOf("全部") }
    
    // 管理模式状态
    var isManageMode by remember { mutableStateOf(false) }
    var selectedNumbers by remember { mutableStateOf(emptySet<Long>()) } // 使用Long类型的id作为唯一标识
    
    // 切换选择状态
    fun toggleSelect(id: Long) {
        selectedNumbers = if (selectedNumbers.contains(id)) {
            selectedNumbers - id
        } else {
            selectedNumbers + id
        }
    }
    
    // 确认弹框状态
    var showConfirmDialog by remember { mutableStateOf(false) }
    var confirmAction by remember { mutableStateOf<String?>(null) } // "uncollectSelected", "clearAll", "uncollectSingle"
    var numberToUncollect by remember { mutableStateOf<LotteryNumber?>(null) }
    
    // 过滤后的号码列表
    val filteredNumbers by remember(allNumbers, searchQuery, selectedType) {
        derivedStateOf {
            allNumbers.filter { number ->
                // 根据号码搜索 - 支持多个号码用逗号分隔,AND 关系
                val matchesSearch = if (searchQuery.isBlank()) {
                    true
                } else {
                    // 分割搜索号码,支持逗号分隔
                    val searchNumbers = searchQuery.split(",").map { it.trim() }.filter { it.isNotEmpty() }
                    // 检查号码是否包含所有搜索号码(AND 关系)
                    searchNumbers.all { searchNum -> number.numbers.contains(searchNum) }
                }
                // 根据类型搜索
                val matchesType = selectedType == "全部" || LotteryType.fromTypeValue(number.type)?.typeKey == selectedType
                matchesSearch && matchesType
            }
        }
    }
    
    // 彩票类型列表
    val lotteryTypes = listOf(
        "全部" to "全部"
    ) + LotteryType.allTypes
    
    // 下拉菜单状态
    var expanded by remember { mutableStateOf(false) }

    Scaffold(
        modifier = Modifier.fillMaxSize(),
        containerColor = MaterialTheme.colorScheme.background,
        topBar = {
            TopNavBar(
                title = "我的收藏",
                showBackButton = true,
                onBackClick = { navController.navigateUp() }
            )
        },
        floatingActionButton = {
            FloatingActionButton(
                onClick = { 
                    // 返回到主屏幕,保持之前的导航状态
                    navController.popBackStack("home", inclusive = false)
                },
                containerColor = Color(0xFFFFF3E0), // lucky_gold_200 背景
                contentColor = Color(0xFFFFA000), // lucky_gold_500 图标颜色
                shape = CircleShape,
                modifier = Modifier.padding(bottom = 88.dp)
            ) {
                Icon(Icons.Filled.Home, contentDescription = "返回首页")
            }
        }
    ) {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(it)
        ) {
            // 搜索和管理区域 - 固定在顶部
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(start = 16.dp, top = 16.dp, end = 16.dp, bottom = 8.dp)
                    .background(MaterialTheme.colorScheme.background)
            ) {
                Column {
                    // 搜索框和管理按钮行
                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        verticalAlignment = Alignment.CenterVertically,
                        horizontalArrangement = Arrangement.spacedBy(8.dp)
                    ) {
                        // 号码搜索框
                        OutlinedTextField(
                            value = searchQuery,
                            onValueChange = {
                                searchQuery = it
                                // 搜索时重置分页
                                coroutineScope.launch {
                                    viewModel.loadFirstPage()
                                }
                            },
                            label = { 
                                Text(
                                    text = "搜索号码",
                                    color = Color(0xFFFFA000), // 标签文字颜色改为金色
                                    fontSize = FontSizeSystem.getScaledSp(14.sp)
                                )
                            },
                            placeholder = { 
                                Text(
                                    text = "多个数字用逗号分隔,如:01,02,03",
                                    color = Color(0xFFFFA000), // 提示文字颜色改为金色
                                    fontSize = FontSizeSystem.getScaledSp(14.sp)
                                )
                            },
                            modifier = Modifier.weight(1f),
                            shape = RoundedCornerShape(8.dp),
                            colors = OutlinedTextFieldDefaults.colors(
                                focusedBorderColor = Color(0xFFFFA000),
                                unfocusedBorderColor = Color.Gray
                            )
                        )
                        
                        // 管理按钮
                        TextButton(
                            onClick = {
                                isManageMode = !isManageMode
                                // 退出管理模式时清空选择
                                if (!isManageMode) {
                                    selectedNumbers = emptySet()
                                }
                            }
                        ) {
                            Text(
                                text = if (isManageMode) "取消" else "管理",
                                color = Color.Black,
                                fontSize = FontSizeSystem.getScaledSp(18.sp) // 增大字体大小
                            )
                        }
                    }
                    
                    Spacer(modifier = Modifier.height(8.dp))
                    
                    // 类型选择器
                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.SpaceBetween,
                        verticalAlignment = Alignment.CenterVertically
                    ) {
                        Text(
                            text = "按类型筛选:",
                            style = MaterialTheme.typography.bodyMedium.copy(fontSize = FontSizeSystem.getScaledSp(14.sp)),
                            modifier = Modifier.align(Alignment.CenterVertically)
                        )
                        
                        Box {
                            TextButton(
                                onClick = { expanded = true },
                                modifier = Modifier
                                    .height(32.dp), // 减小高度
                                contentPadding = PaddingValues(0.dp) // 移除默认padding
                            ) {
                                Row(
                                    verticalAlignment = Alignment.CenterVertically,
                                    horizontalArrangement = Arrangement.spacedBy(2.dp) // 进一步减小间距
                                ) {
                                    Text(
                                        text = LotteryType.values().find { it.typeKey == selectedType }?.chineseName ?: selectedType,
                                        style = MaterialTheme.typography.labelLarge.copy(fontSize = FontSizeSystem.getScaledSp(14.sp)), // 字体大小与其他操作按钮一致
                                        fontWeight = FontWeight.Bold, // 文字加粗
                                        color = Color.Black,
                                    )
                                    Icon(
                                        imageVector = Icons.Filled.ArrowDropDown,
                                        contentDescription = "展开类型选择",
                                        modifier = Modifier.size(14.dp) // 进一步减小图标大小
                                    )
                                }
                            }
                            
                            DropdownMenu(
                                expanded = expanded,
                                onDismissRequest = { expanded = false },
                                modifier = Modifier
                                    .background(Color.White) // 下拉菜单背景色设置为白色
                                    .wrapContentWidth()
                            ) {
                                lotteryTypes.forEach { (typeValue, displayName) ->
                                    DropdownMenuItem(
                                        text = { Text(text = displayName, fontSize = FontSizeSystem.getScaledSp(14.sp)) },
                                        onClick = {
                                            selectedType = typeValue
                                            // 类型改变时重置分页
                                            coroutineScope.launch {
                                                viewModel.loadFirstPage()
                                            }
                                            expanded = false
                                        }
                                    )
                                }
                            }
                        }
                    }
                    
                    // 管理模式下显示操作按钮
                    if (isManageMode) {
                        Spacer(modifier = Modifier.height(8.dp))
                        Row(
                            modifier = Modifier.fillMaxWidth(),
                            horizontalArrangement = Arrangement.SpaceBetween
                        ) {
                            TextButton(
                                onClick = {
                                    if (selectedNumbers.isNotEmpty()) {
                                        confirmAction = "uncollectSelected"
                                        showConfirmDialog = true
                                    } else {
                                        Toast.makeText(context, "请选择要取消收藏的号码", Toast.LENGTH_SHORT).show()
                                    }
                                },
                                modifier = Modifier.weight(1f),
                                enabled = selectedNumbers.isNotEmpty()
                            ) {
                                Text(
                                        text = "取消所选收藏 (${selectedNumbers.size})",
                                        color = Color.Black,
                                        fontSize = FontSizeSystem.getScaledSp(14.sp)
                                    )
                            }
                            
                            Spacer(modifier = Modifier.width(8.dp))
                            
                            TextButton(
                                onClick = {
                                    if (allNumbers.isNotEmpty()) {
                                        confirmAction = "clearAll"
                                        showConfirmDialog = true
                                    } else {
                                        Toast.makeText(context, "没有可清空的收藏", Toast.LENGTH_SHORT).show()
                                    }
                                },
                                modifier = Modifier.weight(1f),
                                enabled = allNumbers.isNotEmpty()
                            ) {
                                Text(
                                    text = "清空收藏",
                                    color = Color.Black,
                                    fontSize = FontSizeSystem.getScaledSp(14.sp)
                                )
                            }
                        }
                    }
                }
            }
            
            // 分隔线
            HorizontalDivider()
            
            // 号码列表 - 支持滚动和分页加载
            val listState = rememberLazyListState()
            
            // 监听滚动到底部事件
            LaunchedEffect(listState) {
                snapshotFlow { listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index }
                    .collect {
                        if (it != null && it >= filteredNumbers.size - 1 && hasMoreData && !isLoading) {
                            coroutineScope.launch {
                                viewModel.loadNextPage()
                            }
                        }
                    }
                }
            
            LazyColumn(
                modifier = Modifier.fillMaxSize(),
                state = listState
            ) {
                if (filteredNumbers.isEmpty() && !isLoading) {
                    // 当没有数据且不在加载中时显示空状态
                    item {
                        Box(
                            modifier = Modifier
                                .fillMaxSize()
                                .padding(16.dp),
                            contentAlignment = Alignment.Center
                        ) {
                            Text(
                                text = "暂无收藏号码",
                                style = MaterialTheme.typography.bodyMedium.copy(fontSize = FontSizeSystem.getScaledSp(14.sp)),
                                color = Color.Gray,
                                textAlign = TextAlign.Center
                            )
                        }
                    }
                } else {
                    items(filteredNumbers) { lotteryNumber ->
                        LotteryNumberItem(
                            number = lotteryNumber,
                            isManageMode = isManageMode,
                            isSelected = selectedNumbers.contains(lotteryNumber.id),
                            onToggleFavorite = {
                                // 只在取消收藏时显示确认弹框
                                if (lotteryNumber.isCollected) {
                                    confirmAction = "uncollectSingle"
                                    numberToUncollect = lotteryNumber
                                    showConfirmDialog = true
                                } else {
                                    coroutineScope.launch {
                                        viewModel.toggleFavorite(lotteryNumber)
                                        // 显示收藏成功提示
                                        Toast.makeText(context, "收藏成功", Toast.LENGTH_SHORT).show()
                                    }
                                }
                            },
                            onToggleSelect = { toggleSelect(lotteryNumber.id) }
                        )
                    }
                    
                    // 加载更多指示器
                    if (isLoading) {
                        item {
                            Box(
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .padding(16.dp),
                                contentAlignment = Alignment.Center
                            ) {
                                CircularProgressIndicator()
                            }
                        }
                    }
                }
            }
        }
    }
    
    // 确认弹框
    if (showConfirmDialog) {
        AlertDialog(
            containerColor = Color.White,
            onDismissRequest = {
                showConfirmDialog = false
                confirmAction = null
                numberToUncollect = null
            },
            title = {
                Text(
                    text = when (confirmAction) {
                        "uncollectSingle" -> "取消收藏"
                        "uncollectSelected" -> "取消所选收藏"
                        "clearAll" -> "清空所有收藏"
                        else -> "确认操作"
                    },
                    fontSize = FontSizeSystem.getScaledSp(18.sp)
                )
            },
            text = {
                Text(
                    text = when (confirmAction) {
                        "uncollectSingle" -> "确定要取消收藏这个号码吗?"
                        "uncollectSelected" -> "确定要取消所选的 ${selectedNumbers.size} 个收藏吗?"
                        "clearAll" -> "确定要清空所有收藏吗?此操作不可恢复。"
                        else -> "确定要执行此操作吗?"
                    },
                    fontSize = FontSizeSystem.getScaledSp(14.sp)
                )
            },
            confirmButton = {
                Button(
                    onClick = {
                        coroutineScope.launch {
                            when (confirmAction) {
                                "uncollectSingle" -> {
                                    // 单个取消收藏
                                    numberToUncollect?.let {
                                        viewModel.toggleFavorite(it)
                                        Toast.makeText(context, "取消收藏成功", Toast.LENGTH_SHORT).show()
                                    }
                                }
                                "uncollectSelected" -> {
                                    // 批量取消所选收藏
                                    viewModel.uncollectNumbers(selectedNumbers.toList())
                                    selectedNumbers = emptySet()
                                    Toast.makeText(context, "取消所选收藏成功", Toast.LENGTH_SHORT).show()
                                }
                                "clearAll" -> {
                                    // 清空所有收藏
                                    viewModel.clearAllCollections()
                                    Toast.makeText(context, "收藏已清空", Toast.LENGTH_SHORT).show()
                                }
                            }
                            showConfirmDialog = false
                            confirmAction = null
                            numberToUncollect = null
                        }
                    },
                    colors = ButtonDefaults.buttonColors(
                        containerColor = Color(0xFFFFA000),
                        contentColor = Color.White
                    )
                ) {
                    Text(text = "确定", fontSize = FontSizeSystem.getScaledSp(14.sp))
                }
            },
            dismissButton = {
                Button(
                    onClick = {
                        showConfirmDialog = false
                        confirmAction = null
                        numberToUncollect = null
                    },
                    colors = ButtonDefaults.buttonColors(
                        containerColor = Color(0xFFF5F5F5),
                        contentColor = Color.Black
                    )
                ) {
                    Text(text = "取消", fontSize = FontSizeSystem.getScaledSp(14.sp))
                }
            },

        )
    }
}

@Composable
fun LotteryNumberItem(
    number: LotteryNumber,
    isManageMode: Boolean,
    isSelected: Boolean,
    onToggleFavorite: () -> Unit,
    onToggleSelect: () -> Unit
) {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .padding(horizontal = 16.dp, vertical = 8.dp),
        elevation = CardDefaults.cardElevation(defaultElevation = 2.dp),
        shape = RoundedCornerShape(8.dp),
        colors = CardDefaults.cardColors(
            containerColor = Color.Transparent
        )
    ) {
        Box(
            modifier = Modifier.background(Color(0xFFFFF3E0))
        ) {
            Column(
                modifier = Modifier.padding(16.dp)
            ) {
                // 管理模式下显示选择框
                if (isManageMode) {
                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.Start,
                        verticalAlignment = Alignment.CenterVertically,
                        content = {
                            Checkbox(
                                checked = isSelected,
                                onCheckedChange = { onToggleSelect() },
                                colors = CheckboxDefaults.colors(
                                    checkedColor = Color(0xFFFFA000), // 选中状态背景色改为金色
                                    checkmarkColor = Color.White // 勾选标记颜色为白色,增强对比度
                                )
                            )
                            Spacer(modifier = Modifier.width(8.dp))
                            Text(text = "选择该号码", fontSize = FontSizeSystem.getScaledSp(14.sp))
                        }
                    )
                    Spacer(modifier = Modifier.height(8.dp))
                }
                
                // 号码信息 - 使用带背景颜色的展示
                DisplayLotteryNumbers(number = number.numbers, isPlaceholder = false)
                
                // 生成时间、彩票类型和生成方式
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.SpaceBetween,
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Column {
                        Text(
                            text = "生成时间: ${formatDate(number.createdAt)}",
                            style = MaterialTheme.typography.bodySmall.copy(fontSize = FontSizeSystem.getScaledSp(12.sp)),
                            color = Color.Gray
                        )
                        // 彩票类型和生成方式放在同一排
                        Row {
                            Text(
                                text = "彩票类型: ${getLotteryTypeName(number)}",
                                style = MaterialTheme.typography.bodySmall.copy(fontSize = FontSizeSystem.getScaledSp(12.sp)),
                                color = Color.Gray,
                                modifier = Modifier.padding(end = 12.dp)
                            )
                            Text(
                                text = "生成方式: ${GenerationType.fromTypeKey(number.generationType)?.displayName ?: number.generationType}",
                                style = MaterialTheme.typography.bodySmall.copy(fontSize = FontSizeSystem.getScaledSp(12.sp)),
                                color = Color.Gray
                            )
                        }
                    }
                    
                    // 非管理模式下显示收藏按钮
                    if (!isManageMode) {
                        IconButton(onClick = onToggleFavorite) {
                            Icon(
                                imageVector = if (number.isCollected) {
                                    Icons.Filled.Favorite
                                } else {
                                    Icons.Filled.FavoriteBorder
                                },
                                contentDescription = if (number.isCollected) "取消收藏" else "收藏",
                                tint = if (number.isCollected) Color(0xFFFFA000) else Color.Gray
                            )
                        }
                    }
                }
            }
        }
    }
}

/**
 * 显示彩票号码的组件
 */
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun DisplayLotteryNumbers(number: String, isPlaceholder: Boolean) {
    // 解析号码字符串,分离红球和蓝球
    val parts = number.split("|")
    val redBalls = parts[0].trim().split(" ").filter { it.isNotEmpty() }
    val blueBalls = if (parts.size > 1) parts[1].trim().split(" ").filter { it.isNotEmpty() } else emptyList()

    FlowRow(
        modifier = Modifier
            .fillMaxWidth()
            .padding(bottom = 12.dp)
            .padding(horizontal = 8.dp),
        horizontalArrangement = Arrangement.Center,
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        // 显示红球
        redBalls.forEachIndexed { index, num ->
            NumberBall(number = num, isFront = true, isPlaceholder = isPlaceholder)
            if (index < redBalls.size - 1) {
                Spacer(modifier = Modifier.width(8.dp))
            }
        }

        // 如果有蓝球,显示分隔符和蓝球
        if (blueBalls.isNotEmpty()) {
            Spacer(modifier = Modifier.width(16.dp))
            Text(text = "|", style = MaterialTheme.typography.headlineMedium.copy(fontSize = FontSizeSystem.getScaledSp(28.sp)))
            Spacer(modifier = Modifier.width(16.dp))

            blueBalls.forEachIndexed { index, num ->
                NumberBall(number = num, isFront = false, isPlaceholder = isPlaceholder)
                if (index < blueBalls.size - 1) {
                    Spacer(modifier = Modifier.width(8.dp))
                }
            }
        }
    }
}

/**
 * 号码球组件
 */
@Composable
fun NumberBall(number: String, isFront: Boolean, isPlaceholder: Boolean) {
    // 检查号码是否有效(非空且是数字)
    val isValidNumber = number.isNotBlank() && number.all { it.isDigit() }
    
    // 当号码无效时,显示--,背景为浅灰色
    val displayNumber = if (isValidNumber) number else "--"
    val ballColor = when {
        !isValidNumber -> Color.LightGray // 无效号码,浅灰色背景
        isPlaceholder -> MaterialTheme.colorScheme.surfaceVariant
        isFront -> LuckyRed40 // 红球保持原来的红色
        else -> LuckyBlue40 // 蓝球使用蓝色
    }
    
    Box(
        modifier = Modifier
            .size(32.dp) // 统一大小为32.dp
            .background(
                ballColor,
                CircleShape
            )
            .clip(CircleShape),
        contentAlignment = Alignment.Center
    ) {
        Text(
            text = displayNumber,
            color = Color.White,
            fontWeight = FontWeight.Bold,
            fontSize = FontSizeSystem.getScaledSp(14.sp) // 统一字体大小为14.sp
        )
    }
}

// 格式化日期
private fun formatDate(date: java.util.Date): String {
    val formatter = java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss", java.util.Locale.getDefault())
    return formatter.format(date)
}

// 获取彩票类型名称
private fun getLotteryTypeName(number: LotteryNumber): String {
    val typeName = LotteryType.fromTypeValue(number.type)?.chineseName ?: number.type
    // 如果是快乐8且有玩法信息,则显示玩法名称
    return if (number.type == LotteryType.HAPPY_8.typeKey && number.happy8PlayType != null) {
        val playType = Happy8PlayType.fromPlayKey(number.happy8PlayType)
        "$typeName (${playType?.displayName ?: ""})"
    } else {
        typeName
    }
}

// 获取彩票类型内部值
private fun getLotteryTypeValue(typeName: String): String {
    return LotteryType.values().find { it.chineseName == typeName }?.typeKey ?: typeName
}

南来的北往的,走过路过的朋友,请您停停脚、捧个场!

相关推荐
Juicedata4 小时前
JuiceFS 企业版 5.3 特性详解:单文件系统支持超 5,000 亿文件,首次引入 RDMA
大数据·人工智能·机器学习·性能优化·开源
2501_944525544 小时前
Flutter for OpenHarmony 个人理财管理App实战 - 预算详情页面
android·开发语言·前端·javascript·flutter·ecmascript
Piar1231sdafa4 小时前
蓝莓目标检测——改进YOLO11-C2TSSA-DYT-Mona模型实现
人工智能·目标检测·计算机视觉
愚公搬代码4 小时前
【愚公系列】《AI短视频创作一本通》002-AI引爆短视频创作革命(短视频创作者必备的能力)
人工智能
数据猿视觉4 小时前
新品上市|奢音S5耳夹耳机:3.5g无感佩戴,178.8元全场景适配
人工智能
蚁巡信息巡查系统4 小时前
网站信息发布再巡查机制怎么建立?
大数据·人工智能·数据挖掘·内容运营
AI浩4 小时前
C-RADIOv4(技术报告)
人工智能·目标检测
Purple Coder4 小时前
AI赋予超导材料预测论文初稿
人工智能
Data_Journal4 小时前
Scrapy vs. Crawlee —— 哪个更好?!
运维·人工智能·爬虫·媒体·社媒营销
云边云科技_云网融合4 小时前
AIoT智能物联网平台:架构解析与边缘应用新图景
大数据·网络·人工智能·安全