package ui import androidx.compose.foundation.border import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.DragData import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.onExternalDrag import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import kotlinx.coroutines.launch import model.AppConfig import model.KisSession import network.KisAuthService import network.KisTradeService import org.jetbrains.exposed.sql.deleteAll import org.jetbrains.exposed.sql.insert import org.jetbrains.exposed.sql.transactions.transaction // src/main/kotlin/ui/SettingsScreen.kt @OptIn(ExperimentalComposeUiApi::class) @Composable fun SettingsScreen(onAuthSuccess: () -> Unit) { val scope = rememberCoroutineScope() var config by remember { mutableStateOf(KisSession.config) } var statusMessage by remember { mutableStateOf("정보를 입력하세요.") } // 계좌번호 입력 시 데이터 자동 로드 함수 fun checkAndLoadConfig(accountNo: String, isReal: Boolean) { val loaded = DatabaseFactory.findConfigByAccount(accountNo) if (loaded != null) { config = loaded statusMessage = "✅ 기존 데이터를 불러왔습니다." } } LazyColumn(modifier = Modifier.fillMaxSize().padding(24.dp)) { item { Text("거래 방식 선택", style = MaterialTheme.typography.h6) Row(verticalAlignment = Alignment.CenterVertically) { RadioButton(selected = !config.isSimulation, onClick = { config = config.copy(isSimulation = false) }) Text("실전투자") Spacer(Modifier.width(16.dp)) RadioButton(selected = config.isSimulation, onClick = { config = config.copy(isSimulation = true) }) Text("모의투자") } Divider(Modifier.padding(vertical = 12.dp)) // 실전 3종 입력 Text("실전투자 정보 (시세 조회 필수)", fontWeight = FontWeight.Bold) OutlinedTextField(value = config.realAccountNo, onValueChange = { config = config.copy(realAccountNo = it) if(it.length >= 8) checkAndLoadConfig(it, true) }, label = { Text("실전 계좌번호") }, modifier = Modifier.fillMaxWidth()) OutlinedTextField(value = config.realAppKey, onValueChange = { config = config.copy(realAppKey = it) }, label = { Text("실전 App Key") }, modifier = Modifier.fillMaxWidth()) OutlinedTextField(value = config.realSecretKey, onValueChange = { config = config.copy(realSecretKey = it) }, label = { Text("실전 Secret Key") }, modifier = Modifier.fillMaxWidth(), visualTransformation = PasswordVisualTransformation()) Spacer(Modifier.height(16.dp)) // 모의 3종 입력 Text("모의투자 정보", fontWeight = FontWeight.Bold) OutlinedTextField(value = config.vtsAccountNo, onValueChange = { config = config.copy(vtsAccountNo = it) if(it.length >= 8) checkAndLoadConfig(it, false) }, label = { Text("모의 계좌번호") }, modifier = Modifier.fillMaxWidth()) OutlinedTextField(value = config.vtsAppKey, onValueChange = { config = config.copy(vtsAppKey = it) }, label = { Text("모의 App Key") }, modifier = Modifier.fillMaxWidth()) OutlinedTextField(value = config.vtsSecretKey, onValueChange = { config = config.copy(vtsSecretKey = it) }, label = { Text("모의 Secret Key") }, modifier = Modifier.fillMaxWidth(), visualTransformation = PasswordVisualTransformation()) Divider(Modifier.padding(vertical = 16.dp)) // AI 모델 경로 및 드래그 앤 드롭 Text("AI 모델 설정", fontWeight = FontWeight.Bold) Box( modifier = Modifier.fillMaxWidth().height(100.dp).border(1.dp, Color.Gray, RoundedCornerShape(8.dp)) .onExternalDrag(onDrop = { state -> val data = state.dragData if (data is DragData.FilesList) { val path = data.readFiles().firstOrNull()?.removePrefix("file:") if (path?.endsWith(".gguf") == true) config = config.copy(modelPath = path) } }), contentAlignment = Alignment.Center ) { Text(if(config.modelPath.isEmpty()) "GGUF 모델 파일을 여기로 드래그하세요" else config.modelPath, fontSize = 12.sp) } Spacer(Modifier.height(24.dp)) Button( modifier = Modifier.fillMaxWidth().height(50.dp), onClick = { scope.launch { // isLoading = true // 1. KisSession.config 업데이트 및 DB 저장 KisSession.config = config DatabaseFactory.saveConfig(config) val authService = KisAuthService() val tradeService = KisTradeService() val authSuccess = authService.refreshAllTokens() val wsKeySuccess = tradeService.refreshWebsocketKey() if (authSuccess && wsKeySuccess) { statusMessage = "✅ 인증 성공! LLM 시작 중..." onAuthSuccess() } else { statusMessage = "❌ 인증 실패. 키 정보를 확인하세요." } // isLoading = false } } ) { Text("설정 저장 및 실행") } Text(statusMessage, color = Color.Gray, modifier = Modifier.padding(top = 8.dp)) } } }