import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../models/divination_layout.dart'; import '../models/syncretic_session_model.dart'; import '../models/tradition_model.dart'; import '../models/word_model.dart'; import '../services/traditions_repository.dart'; import '../services/words_repository.dart'; import '../widgets/divination_instruction_modal.dart'; import 'lenormand_station_screen.dart'; import '../services/tarot_majors_repository.dart'; class TarotDivinationScreen extends StatefulWidget { final String? initialQuestion; const TarotDivinationScreen({super.key, this.initialQuestion}); @override State createState() => _TarotDivinationScreenState(); } class _TarotDivinationScreenState extends State with SingleTickerProviderStateMixin { late TabController _tabController; final _questionController = TextEditingController(); final _repo = WordsRepository(); List _arcana = []; bool _isShuffling = false; // Layouts: 1 arcanum, 3 arcana, 5 arcana DivinationLayout _selectedLayout = DivinationLayouts.tarotLayouts[0]; int _currentStepIndex = 0; bool _isManualReversed = false; // Drawn results List<({TraditionStation arcanum, bool isReversed, String positionTitle})> _drawnResults = []; bool _savedToSession = false; @override void initState() { super.initState(); _tabController = TabController(length: 2, vsync: this); if (widget.initialQuestion != null) { _questionController.text = widget.initialQuestion!; } _loadArcana(); } void _loadArcana() { final canon = TraditionsRepository().byId('tarot'); if (canon != null) { _arcana = canon.stations; } } @override void dispose() { _tabController.dispose(); _questionController.dispose(); super.dispose(); } void _onLayoutChanged(DivinationLayout layout) { setState(() { _selectedLayout = layout; _currentStepIndex = 0; _drawnResults.clear(); _savedToSession = false; }); } Future _drawRandomCardForStep() async { if (_arcana.isEmpty || _currentStepIndex >= _selectedLayout.count) return; setState(() => _isShuffling = true); HapticFeedback.mediumImpact(); final rnd = Random(); await Future.delayed(const Duration(milliseconds: 350)); final finalIdx = rnd.nextInt(_arcana.length); final isRev = rnd.nextBool() && rnd.nextBool(); // 25% chance final posTitle = _selectedLayout.positionTitles[_currentStepIndex]; if (mounted) { setState(() { _isShuffling = false; _drawnResults.add((arcanum: _arcana[finalIdx], isReversed: isRev, positionTitle: posTitle)); _currentStepIndex++; if (_currentStepIndex >= _selectedLayout.count) { _saveToSession('dice'); } }); HapticFeedback.heavyImpact(); } } void _selectManualArcanum(int idx) { if (idx < 0 || idx >= _arcana.length || _currentStepIndex >= _selectedLayout.count) return; final posTitle = _selectedLayout.positionTitles[_currentStepIndex]; setState(() { _drawnResults.add((arcanum: _arcana[idx], isReversed: _isManualReversed, positionTitle: posTitle)); _isManualReversed = false; _currentStepIndex++; if (_currentStepIndex >= _selectedLayout.count) { _saveToSession('manual'); } }); HapticFeedback.selectionClick(); } void _resetSpread() { setState(() { _currentStepIndex = 0; _drawnResults.clear(); _savedToSession = false; }); } Future _saveToSession(String inputMethod) async { if (_drawnResults.isEmpty) return; final items = _drawnResults.map((r) { WordItem? linkedWord; if (r.arcanum.primary != null) { linkedWord = _repo.getWordById(r.arcanum.primary!.id) ?? _repo.getWordByName(r.arcanum.primary!.word); } return ReadingSignItem( title: '${r.positionTitle}: ${r.arcanum.name}', subtitle: r.arcanum.group, symbol: r.arcanum.symbol.isNotEmpty ? r.arcanum.symbol : '🎴', lore: r.arcanum.lore, isReversed: r.isReversed, linkedWord: linkedWord, ); }).toList(); final reading = SyncreticReading( id: 'reading_${DateTime.now().millisecondsSinceEpoch}', systemId: 'tarot', systemName: 'Таро (Старшие Арканы)', systemIcon: '🎴', timestamp: DateTime.now(), layoutTitle: '${_selectedLayout.title} (${inputMethod == "dice" ? "колода" : "со стола"})', inputMethod: inputMethod, items: items, question: _questionController.text.trim(), ); await _repo.addReadingToSyncreticSession(reading, question: _questionController.text.trim()); if (mounted) { setState(() => _savedToSession = true); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Расклад Таро «${_selectedLayout.title}» сохранён в сессию дня!'), backgroundColor: const Color(0xFF2E7D32), behavior: SnackBarBehavior.floating, duration: const Duration(seconds: 2), ), ); } } @override Widget build(BuildContext context) { final isSpreadComplete = _currentStepIndex >= _selectedLayout.count; return Scaffold( backgroundColor: const Color(0xFF0C0E1A), appBar: AppBar( actions: [ IconButton( tooltip: 'Инструкция по Таро', icon: const Icon(Icons.help_outline_rounded, color: Color(0xFFFFD54F)), onPressed: () => DivinationInstructionModal.show(context, 'tarot'), ), if (_drawnResults.isNotEmpty) IconButton( tooltip: 'Сбросить расклад', icon: const Icon(Icons.refresh_rounded), onPressed: _resetSpread, ), ], title: const Row( children: [ Text('🎴', style: TextStyle(fontSize: 22)), SizedBox(width: 8), Text('Таро (Старшие Арканы)', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 17)), ], ), bottom: TabBar( controller: _tabController, indicatorColor: const Color(0xFFFFD54F), tabs: const [ Tab(icon: Icon(Icons.style_rounded, size: 18), text: 'Жребий колоды'), Tab(icon: Icon(Icons.touch_app_rounded, size: 18), text: 'Выбор из 22 Арканов'), ], ), ), body: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ TextField( controller: _questionController, style: const TextStyle(color: Colors.white, fontSize: 14), decoration: InputDecoration( hintText: 'Вопрос к Старшим Арканам...', hintStyle: const TextStyle(color: Color(0xFF8C93B8), fontSize: 13), prefixIcon: const Icon(Icons.help_outline_rounded, color: Color(0xFFFFD54F), size: 18), filled: true, fillColor: const Color(0xFF151829), border: OutlineInputBorder(borderRadius: BorderRadius.circular(14)), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(14), borderSide: const BorderSide(color: Color(0xFF2A2D4A)), ), ), ), const SizedBox(height: 14), // Layout Picker Row Row( children: [ const Icon(Icons.dashboard_customize_rounded, size: 16, color: Color(0xFFFFD54F)), const SizedBox(width: 6), const Text('РАСКЛАД:', style: TextStyle(color: Color(0xFFFFD54F), fontSize: 11, fontWeight: FontWeight.bold)), const SizedBox(width: 8), Expanded( child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: DivinationLayouts.tarotLayouts.map((l) { final isSel = _selectedLayout.id == l.id; return Padding( padding: const EdgeInsets.only(right: 6), child: ChoiceChip( label: Text(l.title, style: TextStyle(fontSize: 11.5, fontWeight: isSel ? FontWeight.bold : FontWeight.normal)), selected: isSel, selectedColor: const Color(0xFF4A3E1E), side: BorderSide(color: isSel ? const Color(0xFFFFD54F) : const Color(0xFF2A2D4A)), onSelected: (_) => _onLayoutChanged(l), ), ); }).toList(), ), ), ), ], ), const SizedBox(height: 14), // Progress Banner Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), decoration: BoxDecoration( color: const Color(0xFF181C30), borderRadius: BorderRadius.circular(12), border: Border.all(color: const Color(0xFF2A2D4A)), ), child: Row( children: [ Text( 'Позиция: ${_currentStepIndex + 1} из ${_selectedLayout.count}', style: const TextStyle(color: Color(0xFF80DEEA), fontSize: 12, fontWeight: FontWeight.bold), ), const SizedBox(width: 8), if (!isSpreadComplete) Expanded( child: Text( '«${_selectedLayout.positionTitles[_currentStepIndex]}»', style: const TextStyle(color: Color(0xFFFFF9C4), fontSize: 12, fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis, ), ) else const Text('✓ Расклад собран', style: TextStyle(color: Color(0xFF81C784), fontSize: 12, fontWeight: FontWeight.bold)), ], ), ), const SizedBox(height: 14), if (!isSpreadComplete) SizedBox( height: 280, child: TabBarView( controller: _tabController, children: [ _buildDeckTab(), _buildManualTab(), ], ), ), if (isSpreadComplete) ...[ Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: const Color(0xFF1E2238), borderRadius: BorderRadius.circular(14), border: Border.all(color: const Color(0xFF81C784)), ), child: Row( children: [ const Icon(Icons.check_circle_rounded, color: Color(0xFF81C784), size: 20), const SizedBox(width: 10), const Expanded( child: Text( 'Все арканы расклада извлечены и записаны в сессию дня!', style: TextStyle(color: Color(0xFFECEFF1), fontSize: 12.5, fontWeight: FontWeight.bold), ), ), TextButton( onPressed: _resetSpread, child: const Text('Заново', style: TextStyle(color: Color(0xFFFFD54F), fontWeight: FontWeight.bold)), ), ], ), ), ], const SizedBox(height: 16), if (_drawnResults.isNotEmpty) ...[ const Text( 'ВЫПАВШИЕ АРКАНЫ В РАСКЛАДЕ:', style: TextStyle(color: Color(0xFFFFD54F), fontSize: 11.5, fontWeight: FontWeight.w900, letterSpacing: 0.8), ), const SizedBox(height: 10), for (int i = 0; i < _drawnResults.length; i++) ...[ _buildResultArcanumCard(_drawnResults[i], i + 1), const SizedBox(height: 10), ], ], ], ), ), ); } Widget _buildDeckTab() { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF151829), borderRadius: BorderRadius.circular(20), border: Border.all(color: const Color(0xFF2A2D4A)), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 80, height: 115, decoration: BoxDecoration( gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xFF39295C), Color(0xFF1E1738)], ), borderRadius: BorderRadius.circular(14), border: Border.all(color: const Color(0xFFFFD54F), width: 1.5), ), child: const Center( child: Icon(Icons.auto_awesome_rounded, color: Color(0xFFFFD54F), size: 30), ), ), const SizedBox(height: 12), Text( 'Извлечение позиции: «' + _selectedLayout.positionTitles[_currentStepIndex] + '»', textAlign: TextAlign.center, style: const TextStyle(color: Color(0xFF8C93B8), fontSize: 11.5), ), const SizedBox(height: 14), ElevatedButton.icon( style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFFFFD54F), foregroundColor: Colors.black, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), ), onPressed: _isShuffling ? null : _drawRandomCardForStep, icon: const Icon(Icons.style_rounded, size: 20), label: Text( _isShuffling ? 'Тасовка колоды...' : 'Вытянуть для шага ${_currentStepIndex + 1}', style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13.5), ), ), ], ), ); } Widget _buildManualTab() { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: const Color(0xFF151829), borderRadius: BorderRadius.circular(20), border: Border.all(color: const Color(0xFF2A2D4A)), ), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('АРКАН ДЛЯ ШАГА ${_currentStepIndex + 1}:', style: const TextStyle(color: Color(0xFFFFD54F), fontSize: 11, fontWeight: FontWeight.bold)), FilterChip( label: const Text('Перевёрнутый', style: TextStyle(fontSize: 10.5)), selected: _isManualReversed, selectedColor: const Color(0xFFEF5350).withValues(alpha: 0.3), side: BorderSide(color: _isManualReversed ? const Color(0xFFEF5350) : const Color(0xFF2A2D4A)), onSelected: (v) => setState(() => _isManualReversed = v), ), ], ), const SizedBox(height: 8), Expanded( child: ListView.builder( itemCount: _arcana.length, itemBuilder: (ctx, i) { final a = _arcana[i]; return ListTile( dense: true, contentPadding: const EdgeInsets.symmetric(horizontal: 10), leading: Text( '${i == 0 ? "0" : i}', style: const TextStyle(color: Color(0xFF8C93B8), fontWeight: FontWeight.bold), ), title: Text( a.name, style: const TextStyle(color: Color(0xFFE0E2EC), fontSize: 13), ), trailing: const Icon(Icons.touch_app_rounded, size: 16, color: Color(0xFFFFD54F)), onTap: () => _selectManualArcanum(i), ); }, ), ), ], ), ); } Widget _buildResultArcanumCard(({TraditionStation arcanum, bool isReversed, String positionTitle}) res, int index) { final a = res.arcanum; return Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: const Color(0xFF181C30), borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFFFFD54F).withValues(alpha: 0.4)), ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: 44, height: 44, alignment: Alignment.center, decoration: BoxDecoration( color: const Color(0xFF232742), borderRadius: BorderRadius.circular(10), border: Border.all(color: const Color(0xFFFFD54F)), ), child: const Icon(Icons.style_rounded, color: Color(0xFFFFD54F), size: 24), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( '#$index. ${res.positionTitle}: ${a.name}', style: const TextStyle(color: Color(0xFFFFF9C4), fontSize: 14, fontWeight: FontWeight.bold), ), ), if (res.isReversed) Container( padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2), decoration: BoxDecoration( color: const Color(0xFF5C1D1D), borderRadius: BorderRadius.circular(6), border: Border.all(color: const Color(0xFFEF5350)), ), child: const Text('Перевёрнутый', style: TextStyle(color: Color(0xFFFF8A80), fontSize: 9.5, fontWeight: FontWeight.bold)), ), ], ), const SizedBox(height: 4), Text(a.group, style: const TextStyle(color: Color(0xFF80DEEA), fontSize: 11)), const SizedBox(height: 6), Text( a.lore, maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle(color: Color(0xFFD1D5DB), fontSize: 12, height: 1.4), ), ], ), ), ], ), ); } }