import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../models/history_entry_model.dart'; import '../models/word_of_day_model.dart'; import '../services/word_of_the_day_service.dart'; import '../services/words_repository.dart'; import '../widgets/gradient_star_badge.dart'; import 'session_result_screen.dart'; import 'word_of_the_day_screen.dart'; class HistoryScreen extends StatefulWidget { const HistoryScreen({super.key}); @override State createState() => _HistoryScreenState(); } class _HistoryScreenState extends State { final _repo = WordsRepository(); final _wordOfDayService = WordOfTheDayService(); void _confirmClearHistory() { showDialog( context: context, builder: (ctx) => AlertDialog( backgroundColor: const Color(0xFF151829), title: const Row( children: [ GradientStar(size: 20), SizedBox(width: 8), Text('Очистить историю сессий?', style: TextStyle(color: Color(0xFFFFF9C4))), ], ), content: const Text( 'Все сохранённые сессии гаданий и AI-трактовки будут удалены. (Хроника Слов Дня в календаре сохранится).', style: TextStyle(color: Color(0xFF8C93B8)), ), actions: [ TextButton( onPressed: () => Navigator.pop(ctx), child: const Text('Отмена'), ), FilledButton( onPressed: () async { Navigator.pop(ctx); await _repo.clearHistory(); setState(() {}); HapticFeedback.mediumImpact(); }, style: FilledButton.styleFrom(backgroundColor: Colors.red.shade800), child: const Text('Очистить'), ), ], ), ); } void _copySession(OracleSession session) { final buf = StringBuffer(); buf.writeln('✨ Оракул — Запись Сессии ✨'); buf.writeln('Дата: ${session.formattedDate}'); if (session.question != null && session.question!.isNotEmpty) { buf.writeln('Вопрос: «${session.question}»'); } buf.writeln('Знаков: ${session.words.length}\n'); for (int i = 0; i < session.words.length; i++) { final w = session.words[i]; buf.writeln('🔮 Знак #${w.formattedSessionNumber}: ${w.word.toUpperCase()}'); buf.writeln(' ${w.symbolicMeaning}\n'); } if (session.aiInterpretation != null && session.aiInterpretation!.isNotEmpty) { buf.writeln('🤖 AI Трактовка:'); buf.writeln(session.aiInterpretation); } Clipboard.setData(ClipboardData(text: buf.toString())); HapticFeedback.lightImpact(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Row( children: [ GradientStar(size: 18), SizedBox(width: 10), Expanded(child: Text('Сессия скопирована в буфер обмена')), ], ), duration: const Duration(seconds: 2), behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), ), ); } String _formatDateString(String dateKey) { final d = WordOfTheDayService.parseDateKey(dateKey); const months = [ 'января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря' ]; const weekdays = [ 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота', 'Воскресенье' ]; return '${d.day} ${months[d.month - 1]} ${d.year} • ${weekdays[d.weekday - 1]}'; } @override Widget build(BuildContext context) { final sessionsList = _repo.sessions; final dayRecords = _wordOfDayService.getAllRecords(); return DefaultTabController( length: 2, child: Scaffold( backgroundColor: const Color(0xFF0C0E1A), appBar: AppBar( title: const Row( children: [ GradientStar(size: 24), SizedBox(width: 10), Text( 'История & Календарь', style: TextStyle(fontWeight: FontWeight.w900), ), ], ), actions: [ if (sessionsList.isNotEmpty) IconButton( tooltip: 'Очистить историю сессий', icon: const Icon(Icons.delete_outline_rounded), onPressed: _confirmClearHistory, ), IconButton( tooltip: 'Открыть календарь Слов Дня', icon: const Icon(Icons.today_rounded, color: Color(0xFFFFD54F)), onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (_) => const WordOfTheDayScreen()), ).then((_) => setState(() {})); }, ), ], bottom: const TabBar( indicatorColor: Color(0xFFFFD54F), indicatorSize: TabBarIndicatorSize.tab, dividerColor: Color(0xFF1E2238), labelColor: Color(0xFFFFD54F), unselectedLabelColor: Color(0xFF8C93B8), labelStyle: TextStyle(fontWeight: FontWeight.bold, fontSize: 13), tabs: [ Tab( icon: Icon(Icons.history_edu_rounded, size: 18), text: 'Сессии Раскладов', ), Tab( icon: Icon(Icons.calendar_month_rounded, size: 18), text: 'Хроника Слов Дня', ), ], ), ), body: TabBarView( children: [ // Tab 1: Oracle Divination Sessions _buildSessionsTab(sessionsList), // Tab 2: Word of the Day Chronicle / Calendar _buildWordOfDayChronicleTab(dayRecords), ], ), ), ); } Widget _buildSessionsTab(List sessionsList) { if (sessionsList.isEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const GradientStar(size: 48), const SizedBox(height: 16), const Text( 'История сессий пока пуста', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16, color: Colors.white), ), const SizedBox(height: 8), const Padding( padding: EdgeInsets.symmetric(horizontal: 32), child: Text( 'Задайте вопрос, совершите броски и откройте знаки, чтобы сохранить сессию здесь.', textAlign: TextAlign.center, style: TextStyle(color: Color(0xFF8C93B8)), ), ), ], ), ); } return ListView.separated( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), itemCount: sessionsList.length, separatorBuilder: (_, _) => const SizedBox(height: 12), itemBuilder: (context, index) { final session = sessionsList[index]; return Card( elevation: 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), side: BorderSide( color: const Color(0xFFFFD54F).withValues(alpha: 0.25), ), ), color: const Color(0xFF1B1D30), child: InkWell( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (_) => SessionResultScreen( session: session, ), ), ).then((_) => setState(() {})); }, borderRadius: BorderRadius.circular(20), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ const Icon( Icons.calendar_today_rounded, size: 14, color: Color(0xFFFFD54F), ), const SizedBox(width: 6), Text( session.formattedDate, style: const TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFFFFD54F), ), ), ], ), Row( children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: const Color(0xFF2A2D4A), borderRadius: BorderRadius.circular(8), border: Border.all( color: const Color(0xFFFFD54F).withValues(alpha: 0.3), ), ), child: Text( '${session.words.length} ${session.words.length == 1 ? "знак" : (session.words.length < 5 ? "знака" : "знаков")}', style: const TextStyle( fontSize: 11, color: Color(0xFFFFF9C4), fontWeight: FontWeight.bold, ), ), ), const SizedBox(width: 4), IconButton( icon: const Icon(Icons.copy_rounded, size: 16), tooltip: 'Скопировать', onPressed: () => _copySession(session), ), IconButton( icon: const Icon(Icons.delete_outline, size: 16), tooltip: 'Удалить', onPressed: () async { await _repo.deleteSession(session.id); setState(() {}); }, ), ], ), ], ), if (session.question != null && session.question!.isNotEmpty) ...[ const SizedBox(height: 8), Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), decoration: BoxDecoration( color: const Color(0xFF22243C), borderRadius: BorderRadius.circular(10), ), child: Row( children: [ const Icon(Icons.help_outline_rounded, size: 14, color: Color(0xFFFFD54F)), const SizedBox(width: 6), Expanded( child: Text( 'Вопрос: «${session.question}»', style: const TextStyle( fontSize: 12.5, fontStyle: FontStyle.italic, color: Color(0xFFFFF9C4), ), ), ), ], ), ), ], const SizedBox(height: 10), Wrap( spacing: 6, runSpacing: 6, children: session.words.map((w) { return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: const Color(0xFF131524), borderRadius: BorderRadius.circular(10), border: Border.all( color: const Color(0xFFFFD54F).withValues(alpha: 0.3), ), ), child: Text( '#${w.formattedSessionNumber} ${w.word.toUpperCase()}', style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 12, color: Color(0xFFFFD54F), ), ), ); }).toList(), ), if (session.aiInterpretation != null && session.aiInterpretation!.isNotEmpty) ...[ const SizedBox(height: 10), Container( width: double.infinity, padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: const Color(0xFF261D3A), borderRadius: BorderRadius.circular(12), border: Border.all( color: const Color(0xFFBA68C8).withValues(alpha: 0.4), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row( children: [ Icon(Icons.psychology_rounded, size: 14, color: Color(0xFFE1BEE7)), SizedBox(width: 6), Text( 'AI Трактовка', style: TextStyle( fontSize: 11, fontWeight: FontWeight.bold, color: Color(0xFFE1BEE7), ), ), ], ), const SizedBox(height: 4), Text( session.aiInterpretation!, maxLines: 3, overflow: TextOverflow.ellipsis, style: const TextStyle( fontSize: 12, height: 1.4, color: Color(0xFFF3E5F5), ), ), ], ), ), ], ], ), ), ), ); }, ); } Widget _buildWordOfDayChronicleTab(List dayRecords) { final streak = _wordOfDayService.calculateStreak(); final completedCount = _wordOfDayService.totalCompletedPractices; final revealedCount = _wordOfDayService.totalRevealedDays; return SingleChildScrollView( padding: const EdgeInsets.fromLTRB(14, 12, 14, 24), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Stats Ribbon Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( gradient: const LinearGradient( colors: [Color(0xFF2E2045), Color(0xFF14172B)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(20), border: Border.all(color: const Color(0xFFFFD54F).withValues(alpha: 0.5)), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _buildStatPill('🔥 СТРИК', '$streak дн.', const Color(0xFFFFB74D)), _buildStatPill('☀️ ОТКРЫТО', '$revealedCount дн.', const Color(0xFFFFD54F)), _buildStatPill('✓ ПРАКТИКИ', '$completedCount', const Color(0xFF81C784)), ], ), ), const SizedBox(height: 16), // Action Button: Open Today ElevatedButton.icon( style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFFFFD54F), foregroundColor: const Color(0xFF0C0E1A), padding: const EdgeInsets.symmetric(vertical: 14), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), elevation: 4, ), icon: const Icon(Icons.wb_sunny_rounded, size: 20), label: const Text( 'ОТКРЫТЬ СЛОВО СЕГОДНЯШНЕГО ДНЯ', style: TextStyle(fontWeight: FontWeight.w900, letterSpacing: 0.8, fontSize: 13), ), onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (_) => const WordOfTheDayScreen()), ).then((_) => setState(() {})); }, ), const SizedBox(height: 16), const Row( children: [ Icon(Icons.history_toggle_off_rounded, size: 16, color: Color(0xFFFFD54F)), SizedBox(width: 8), Text( 'ХРОНИКА СЛОВ И ПРАКТИК', style: TextStyle( color: Color(0xFFFFD54F), fontSize: 11.5, fontWeight: FontWeight.w900, letterSpacing: 1.0, ), ), ], ), const SizedBox(height: 10), if (dayRecords.isEmpty) Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: const Color(0xFF151829), borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFF2A2D4A)), ), child: const Column( children: [ GradientStar(size: 32), SizedBox(height: 12), Text( 'Пока нет открытых слов', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white, fontSize: 14), ), SizedBox(height: 6), Text( 'Откройте сегодняшнее Слово Дня, выполните практику и зафиксируйте синхронии.', textAlign: TextAlign.center, style: TextStyle(color: Color(0xFF8C93B8), fontSize: 12, height: 1.35), ), ], ), ) else ListView.separated( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: dayRecords.length, separatorBuilder: (_, _) => const SizedBox(height: 10), itemBuilder: (context, index) { final rec = dayRecords[index]; final date = WordOfTheDayService.parseDateKey(rec.dateKey); final isTriptych = rec.isTriptychMode; final singleWord = _wordOfDayService.getWordForDate(date, phase: 'main'); final triptych = _wordOfDayService.getTriptychWordsForDate(date); final isDone = isTriptych ? rec.isDayStepCompleted : rec.isStepCompleted; return Container( decoration: BoxDecoration( color: const Color(0xFF151829), borderRadius: BorderRadius.circular(16), border: Border.all( color: isDone ? const Color(0xFF81C784).withValues(alpha: 0.5) : const Color(0xFF2A2D4A), ), ), child: InkWell( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (_) => WordOfTheDayScreen(initialDate: date), ), ).then((_) => setState(() {})); }, borderRadius: BorderRadius.circular(16), child: Padding( padding: const EdgeInsets.all(14), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( isTriptych ? '⏳' : (singleWord.emoji.isNotEmpty ? singleWord.emoji : '☀️'), style: const TextStyle(fontSize: 22), ), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( isTriptych ? '🌅 ${triptych.morning.word.toUpperCase()} • ⚡ ${triptych.day.word.toUpperCase()} • 🌙 ${triptych.evening.word.toUpperCase()}' : singleWord.word.toUpperCase(), style: TextStyle( color: const Color(0xFFFFF9C4), fontWeight: FontWeight.bold, fontSize: isTriptych ? 13 : 15, letterSpacing: 0.6, ), ), Text( '${_formatDateString(rec.dateKey)}${isTriptych ? " • Триптих" : ""}', style: const TextStyle( color: Color(0xFF8C93B8), fontSize: 11, ), ), ], ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: isDone ? const Color(0xFF1B3D23) : const Color(0xFF262A44), borderRadius: BorderRadius.circular(8), border: Border.all( color: isDone ? const Color(0xFF81C784) : const Color(0xFF8C93B8).withValues(alpha: 0.3), ), ), child: Text( isDone ? '✓ Выполнено' : (isTriptych ? (rec.areAllTriptychRevealed ? 'Открыто 3/3' : 'Триптих') : 'Открыто'), style: TextStyle( color: isDone ? const Color(0xFF81C784) : const Color(0xFFFFD54F), fontSize: 11, fontWeight: FontWeight.bold, ), ), ), ], ), if (!isTriptych && singleWord.asks.isNotEmpty) ...[ const SizedBox(height: 8), Text( 'Коан: «${singleWord.asks}»', maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle( color: Color(0xFFB39DDB), fontSize: 12, fontStyle: FontStyle.italic, ), ), ] else if (isTriptych && triptych.day.step.isNotEmpty) ...[ const SizedBox(height: 8), Text( 'Вызов дня: «${triptych.day.step}»', maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle( color: Color(0xFFA5D6A7), fontSize: 12, ), ), ], if (rec.diaryNote.isNotEmpty) ...[ const SizedBox(height: 8), Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: const Color(0xFF1A2238), borderRadius: BorderRadius.circular(8), border: Border.all(color: const Color(0xFF80DEEA).withValues(alpha: 0.3)), ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('🪶', style: TextStyle(fontSize: 12)), const SizedBox(width: 6), Expanded( child: Text( rec.diaryNote, maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle( color: Color(0xFF80DEEA), fontSize: 11.5, height: 1.3, ), ), ), ], ), ), ], ], ), ), ), ); }, ), ], ), ); } Widget _buildStatPill(String title, String val, Color color) { return Column( children: [ Text( title, style: TextStyle( color: color, fontSize: 10, fontWeight: FontWeight.w900, letterSpacing: 0.8, ), ), const SizedBox(height: 4), Text( val, style: const TextStyle( color: Colors.white, fontSize: 17, fontWeight: FontWeight.w900, ), ), ], ); } }