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/cleromancy_dialog.dart'; import '../widgets/d10_dice_roller_widget.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(); final _diceRollerKey = GlobalKey(); WordItem? _currentWord; String? _activeHistoryId; List _searchResults = []; bool _showKeypad = true; String _currentDigits = ''; // Tracks 0..3 digits input for d10 dice cleromancy @override void initState() { super.initState(); _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 = []; _currentDigits = ''; }); return; } final isNumeric = int.tryParse(query) != null; if (isNumeric) { setState(() { _currentDigits = query; }); // In Cleromancy, exactly 3 digits (d10x3) are required to resolve a number 000..999 if (query.length >= 3) { final num = int.tryParse(query); if (num != null && num <= 999) { final word = _repo.getWordBySessionNumber(num); if (word != null) { _setWord(word, recordHistory: true); } } } } else { // Text search by word final results = _repo.search(query); setState(() { _searchResults = results; _currentDigits = ''; 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: 'Клеромантия (3× d10)', question: question.isNotEmpty ? question : null, ); await _repo.addHistoryEntry(entry); } setState(() { _currentWord = word; _activeHistoryId = newHistId ?? _activeHistoryId; _currentDigits = word.formattedSessionNumber; if (updateText) { _searchController.text = word.formattedSessionNumber; } }); HapticFeedback.selectionClick(); } void _onKeypadTap(String val) { HapticFeedback.lightImpact(); String currentText = _searchController.text; if (val == 'C') { _searchController.clear(); setState(() => _currentDigits = ''); return; } if (val == '⌫') { if (currentText.isNotEmpty) { _searchController.text = currentText.substring(0, currentText.length - 1); _searchController.selection = TextSelection.fromPosition( TextPosition(offset: _searchController.text.length), ); } return; } // In Cleromancy, append digit up to 3 digits if (currentText.length >= 3) { _searchController.text = val; } else { _searchController.text = currentText + val; } _searchController.selection = TextSelection.fromPosition( TextPosition(offset: _searchController.text.length), ); } void _onVirtualRollCompleted(int finalNumber) { final word = _repo.getWordBySessionNumber(finalNumber); if (word != null) { _setWord(word, updateText: true, recordHistory: true); } } void _resetToWelcome() { setState(() { _currentWord = null; _searchController.clear(); _searchResults = []; _currentDigits = ''; }); } 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(); _resetToWelcome(); 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: [ IconButton( tooltip: 'Инструкция: Клеромантия и кубики d10', icon: const Icon(Icons.help_outline_rounded, color: Color(0xFFFFD54F)), onPressed: () => CleromancyGuideDialog.show(context), ), 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: 12), // D10 Dice Roller Slots Widget D10DiceRollerWidget( key: _diceRollerKey, digits: _currentDigits, isRolling: false, onRollPressed: () {}, onRollCompleted: _onVirtualRollCompleted, ), const SizedBox(height: 10), // Search & Direct 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(); setState(() => _currentDigits = ''); }, ) : 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 (for text search) if (_searchResults.length > 1 && _searchController.text.isNotEmpty && int.tryParse(_searchController.text) == null) ...[ 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: true, recordHistory: true); }, ); }, ), ), ], const SizedBox(height: 14), // Welcome Idle State OR Active Word Card if (_currentWord == null) ...[ // Welcome Initiation Card Container( padding: const EdgeInsets.all(20), 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, ), ), child: Column( children: [ const GradientStar(size: 38), const SizedBox(height: 12), const Text( 'СВЯЩЕННЫЙ ЖРЕБИЙ КЛЕРОМАНТИИ', textAlign: TextAlign.center, style: TextStyle( fontSize: 15, fontWeight: FontWeight.w900, letterSpacing: 0.8, color: Color(0xFFFFF9C4), ), ), const SizedBox(height: 8), Text( 'Сформулируйте вопрос, затем совершите 3 броска кубика d10 кнопкой выше или введите 3 выпавших числа (Сотни, Десятки, Единицы).', textAlign: TextAlign.center, style: TextStyle( fontSize: 13, height: 1.4, color: theme.colorScheme.onSurface.withValues(alpha: 0.85), ), ), const SizedBox(height: 14), OutlinedButton.icon( onPressed: () => CleromancyGuideDialog.show(context), icon: const Icon(Icons.menu_book_rounded, size: 16, color: Color(0xFFFFD54F)), label: const Text( 'Инструкция по броскам d10', style: TextStyle( color: Color(0xFFFFD54F), fontWeight: FontWeight.bold, fontSize: 12.5, ), ), style: OutlinedButton.styleFrom( side: const BorderSide(color: Color(0xFFFFD54F), width: 1), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14), ), ), ), ], ), ), ] 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: () { _diceRollerKey.currentState?.triggerRollAnimation(); }, ), ), ], 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: [ Icon(Icons.casino_outlined, size: 15, color: Color(0xFFFFD54F)), SizedBox(width: 6), Text( 'Ввод граней d10 (3 цифры):', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w700, color: Color(0xFFFFD54F), ), ), ], ), Text( _currentDigits.length == 3 ? 'Код: #$_currentDigits' : 'Введено: ${_currentDigits.length} из 3', style: TextStyle( fontSize: 11, fontWeight: FontWeight.bold, color: _currentDigits.length == 3 ? const Color(0xFFFFD54F) : 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(), ); } }