import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../models/history_entry_model.dart'; import '../models/word_model.dart'; import '../services/words_repository.dart'; import '../widgets/gradient_star_badge.dart'; import '../widgets/word_card_widget.dart'; class HomeSearchScreen extends StatefulWidget { const HomeSearchScreen({super.key}); @override State createState() => _HomeSearchScreenState(); } class _HomeSearchScreenState extends State { final _repo = WordsRepository(); final _searchController = TextEditingController(); final _questionController = TextEditingController(); final _focusNode = FocusNode(); WordItem? _currentWord; String? _activeHistoryId; List _searchResults = []; bool _showKeypad = true; @override void initState() { super.initState(); // Do NOT show word by default until user interacts or starts a session _searchController.addListener(_onSearchChanged); } @override void dispose() { _searchController.removeListener(_onSearchChanged); _searchController.dispose(); _questionController.dispose(); _focusNode.dispose(); super.dispose(); } void _onSearchChanged() { final query = _searchController.text.trim(); if (query.isEmpty) { setState(() { _searchResults = []; }); return; } final results = _repo.search(query); setState(() { _searchResults = results; if (results.isNotEmpty) { _setWord(results.first, recordHistory: true); } }); } Future _setWord(WordItem word, {bool updateText = false, bool recordHistory = true}) async { final question = _questionController.text.trim(); String? newHistId; if (recordHistory) { newHistId = DateTime.now().millisecondsSinceEpoch.toString(); final entry = HistoryEntry( id: newHistId, timestamp: DateTime.now(), word: word.word, sessionNumber: word.sessionNumber, partOfSpeech: word.partOfSpeech, symbolicMeaning: word.description, readingType: 'Жребий номера', question: question.isNotEmpty ? question : null, ); await _repo.addHistoryEntry(entry); } setState(() { _currentWord = word; _activeHistoryId = newHistId ?? _activeHistoryId; if (updateText) { _searchController.text = word.sessionNumber.toString(); } }); HapticFeedback.selectionClick(); } void _onKeypadTap(String val) { HapticFeedback.lightImpact(); String currentText = _searchController.text; if (val == 'C') { _searchController.clear(); return; } if (val == '⌫') { if (currentText.isNotEmpty) { _searchController.text = currentText.substring(0, currentText.length - 1); _searchController.selection = TextSelection.fromPosition( TextPosition(offset: _searchController.text.length), ); } return; } // Append digit if (currentText.length >= 3) { _searchController.text = val; } else { final newText = currentText + val; final num = int.tryParse(newText); if (num != null && num <= 999) { _searchController.text = newText; } else { _searchController.text = val; } } _searchController.selection = TextSelection.fromPosition( TextPosition(offset: _searchController.text.length), ); } void _startNewSessionDraw() { final rnd = _repo.getRandomWord(); _setWord(rnd, updateText: true, recordHistory: true); } void _resetToWelcome() { setState(() { _currentWord = null; _searchController.clear(); _searchResults = []; }); } void _goToNext() { if (_currentWord == null) return; final next = _repo.getNextWord(_currentWord!.sessionNumber); _setWord(next, updateText: true, recordHistory: false); } void _goToPrevious() { if (_currentWord == null) return; final prev = _repo.getPreviousWord(_currentWord!.sessionNumber); _setWord(prev, updateText: true, recordHistory: false); } void _reshuffleSession() { _repo.reshuffleSessionNumbers(); setState(() { _currentWord = null; _searchController.clear(); _searchResults = []; }); HapticFeedback.heavyImpact(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Row( children: [ GradientStar(size: 20), SizedBox(width: 10), Expanded( child: Text( 'Случайный порядок 1000 чисел перетасован заново!', style: TextStyle(fontWeight: FontWeight.w600), ), ), ], ), duration: const Duration(seconds: 2), behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), ), ); } @override Widget build(BuildContext context) { final theme = Theme.of(context); final isDark = theme.brightness == Brightness.dark; return Scaffold( appBar: AppBar( title: const Row( children: [ GradientStar(size: 24), SizedBox(width: 10), Text( 'Оракул 999', style: TextStyle( fontSize: 20, fontWeight: FontWeight.w900, letterSpacing: 0.5, ), ), ], ), actions: [ if (_currentWord != null) IconButton( tooltip: 'Новый вопрос / Сброс', icon: const Icon(Icons.refresh_rounded), onPressed: _resetToWelcome, ), IconButton( tooltip: 'Перетасовать числа (#000..#999)', icon: const Icon(Icons.shuffle_rounded), onPressed: _reshuffleSession, ), IconButton( tooltip: _showKeypad ? 'Скрыть панель' : 'Показать панель', icon: Icon(_showKeypad ? Icons.dialpad_outlined : Icons.dialpad), onPressed: () { setState(() => _showKeypad = !_showKeypad); }, ), ], ), body: SafeArea( child: SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Question Input Field TextField( controller: _questionController, decoration: InputDecoration( labelText: 'Ваш вопрос или намерение (необязательно)', hintText: 'Например: В чем скрытый потенциал ситуации?', prefixIcon: const Icon(Icons.help_outline_rounded, color: Color(0xFFFFD54F), size: 20), suffixIcon: _questionController.text.isNotEmpty ? IconButton( icon: const Icon(Icons.clear, size: 18), onPressed: () => _questionController.clear(), ) : null, filled: true, fillColor: isDark ? const Color(0xFF17192C) : theme.colorScheme.surfaceContainerHigh, border: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: BorderSide( color: const Color(0xFFFFD54F).withValues(alpha: 0.25), ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: BorderSide( color: const Color(0xFFFFD54F).withValues(alpha: 0.2), ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: const BorderSide( color: Color(0xFFFFD54F), width: 1.5, ), ), ), ), const SizedBox(height: 10), // Search & Number Input Bar TextField( controller: _searchController, focusNode: _focusNode, keyboardType: TextInputType.text, decoration: InputDecoration( labelText: 'Введите число (#000..#999) или слово', hintText: 'Например: 777 или огонь', prefixIcon: const Padding( padding: EdgeInsets.symmetric(horizontal: 12), child: GradientStar(size: 18), ), suffixIcon: _searchController.text.isNotEmpty ? IconButton( icon: const Icon(Icons.clear_rounded), onPressed: () { _searchController.clear(); _focusNode.unfocus(); }, ) : null, filled: true, fillColor: isDark ? const Color(0xFF1B1D30) : theme.colorScheme.surfaceContainerHighest.withValues(alpha: 0.4), border: OutlineInputBorder( borderRadius: BorderRadius.circular(18), borderSide: BorderSide( color: const Color(0xFFFFD54F).withValues(alpha: 0.3), ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(18), borderSide: BorderSide( color: const Color(0xFFFFD54F).withValues(alpha: 0.25), ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(18), borderSide: const BorderSide( color: Color(0xFFFFD54F), width: 1.8, ), ), ), ), // Search matches dropdown list if (_searchResults.length > 1 && _searchController.text.isNotEmpty) ...[ const SizedBox(height: 8), Container( constraints: const BoxConstraints(maxHeight: 140), decoration: BoxDecoration( color: isDark ? const Color(0xFF161828) : theme.colorScheme.surface, borderRadius: BorderRadius.circular(16), border: Border.all( color: const Color(0xFFFFD54F).withValues(alpha: 0.3), ), ), child: ListView.separated( shrinkWrap: true, padding: const EdgeInsets.symmetric(vertical: 4), itemCount: _searchResults.length > 10 ? 10 : _searchResults.length, separatorBuilder: (_, _) => const Divider(height: 1), itemBuilder: (context, idx) { final item = _searchResults[idx]; final isSelected = item.sessionNumber == _currentWord?.sessionNumber; return ListTile( dense: true, selected: isSelected, leading: GradientStarBadge( text: '#${item.formattedSessionNumber}', size: 14, ), title: Text( item.word.toUpperCase(), style: const TextStyle(fontWeight: FontWeight.bold), ), subtitle: Text( item.description, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 11), ), trailing: Text( item.partOfSpeech, style: TextStyle( fontSize: 11, color: theme.colorScheme.outline, ), ), onTap: () { _setWord(item, updateText: false, recordHistory: true); }, ); }, ), ), ], const SizedBox(height: 14), // Welcome Idle State OR Active Word Card if (_currentWord == null) ...[ // Welcome Initiation Card Container( padding: const EdgeInsets.all(22), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ const Color(0xFF1F223B), const Color(0xFF131526), ], ), borderRadius: BorderRadius.circular(24), border: Border.all( color: const Color(0xFFFFD54F).withValues(alpha: 0.35), width: 1.2, ), boxShadow: [ BoxShadow( color: const Color(0xFFFFD54F).withValues(alpha: 0.1), blurRadius: 16, ), ], ), child: Column( children: [ const GradientStar(size: 44), const SizedBox(height: 14), const Text( 'СВЯЩЕННЫЙ ЖРЕБИЙ ОРАКУЛА', textAlign: TextAlign.center, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w900, letterSpacing: 1.0, color: Color(0xFFFFF9C4), ), ), const SizedBox(height: 10), Text( 'Сформулируйте в мыслях или запишите сверху ваш вопрос, затем нажмите кнопку или наберите номер от 000 до 999 для раскрытия символического ключа.', textAlign: TextAlign.center, style: TextStyle( fontSize: 13, height: 1.45, color: theme.colorScheme.onSurface.withValues(alpha: 0.85), ), ), const SizedBox(height: 18), FilledButton.icon( onPressed: _startNewSessionDraw, icon: const GradientStar(size: 20), label: const Text( 'НАЧАТЬ СЕССИЮ / БРОСИТЬ ЖРЕБИЙ', style: TextStyle( fontWeight: FontWeight.w900, letterSpacing: 0.5, ), ), style: FilledButton.styleFrom( backgroundColor: const Color(0xFF2A2D4A), foregroundColor: const Color(0xFFFFF9C4), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(18), side: const BorderSide( color: Color(0xFFFFD54F), width: 1.2, ), ), ), ), ], ), ), ] else ...[ // Main Sacred Word Card Display AnimatedSwitcher( duration: const Duration(milliseconds: 300), transitionBuilder: (child, animation) { return FadeTransition( opacity: animation, child: SlideTransition( position: Tween( begin: const Offset(0.0, 0.04), end: Offset.zero, ).animate(animation), child: child, ), ); }, child: WordCardWidget( key: ValueKey('${_currentWord!.id}_${_currentWord!.sessionNumber}'), word: _currentWord!, question: _questionController.text.trim(), historyEntryId: _activeHistoryId, onNext: _goToNext, onPrevious: _goToPrevious, onRandom: _startNewSessionDraw, ), ), ], const SizedBox(height: 14), // Interactive Numeric Keypad if (_showKeypad) ...[ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: isDark ? const Color(0xFF17192A) : theme.colorScheme.surfaceContainerLow, borderRadius: BorderRadius.circular(20), border: Border.all( color: const Color(0xFFFFD54F).withValues(alpha: 0.25), ), ), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Row( children: [ GradientStar(size: 14), SizedBox(width: 6), Text( 'Случайный жребий числа:', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w700, color: Color(0xFFFFD54F), ), ), ], ), Text( '000 — 999', style: TextStyle( fontSize: 11, color: theme.colorScheme.outline, ), ), ], ), const SizedBox(height: 10), _buildKeypadRow(['1', '2', '3']), const SizedBox(height: 8), _buildKeypadRow(['4', '5', '6']), const SizedBox(height: 8), _buildKeypadRow(['7', '8', '9']), const SizedBox(height: 8), _buildKeypadRow(['C', '0', '⌫']), ], ), ), const SizedBox(height: 16), ], ], ), ), ), ); } Widget _buildKeypadRow(List keys) { return Row( children: keys.map((key) { final isSpecial = key == 'C' || key == '⌫'; final theme = Theme.of(context); final isDark = theme.brightness == Brightness.dark; return Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 4), child: Material( color: isSpecial ? const Color(0xFF2A2D4A) : isDark ? const Color(0xFF20233A) : theme.colorScheme.surface, borderRadius: BorderRadius.circular(14), elevation: 1, child: InkWell( onTap: () => _onKeypadTap(key), borderRadius: BorderRadius.circular(14), child: Container( height: 46, alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(14), border: Border.all( color: isSpecial ? const Color(0xFFFFD54F).withValues(alpha: 0.4) : Colors.white10, ), ), child: Text( key, style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: isSpecial ? const Color(0xFFFFF9C4) : isDark ? Colors.white : Colors.black87, ), ), ), ), ), ), ); }).toList(), ); } }