Files
999/lib/screens/house_chains_screen.dart

600 lines
24 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../models/history_entry_model.dart';
import '../models/houses_session_model.dart';
import '../models/word_model.dart';
import '../services/ai_oracle_service.dart';
import '../widgets/gradient_star_badge.dart';
import 'symbol_detail_screen.dart';
import 'matrix_geometry_screen.dart';
import '../models/syncretic_session_model.dart';
import '../services/words_repository.dart';
class HouseChainsScreen extends StatefulWidget {
final HousesSession session;
const HouseChainsScreen({super.key, required this.session});
@override
State<HouseChainsScreen> createState() => _HouseChainsScreenState();
}
class _HouseChainsScreenState extends State<HouseChainsScreen> {
final _aiService = AiOracleService();
final Map<int, String> _chainAiInterpretations = {};
final Set<int> _loadingChains = {};
@override
void initState() {
super.initState();
_recordChainsToSyncreticSession(showToast: false);
}
Future<void> _recordChainsToSyncreticSession({bool showToast = false}) async {
final session = widget.session;
if (session.chains.isEmpty) return;
final chainItems = session.chains.map((chain) {
final pathStr = chain.steps.map((s) => '«${s.guest.word.toUpperCase()}»').join(' ➔ ');
final startWord = chain.startingHouse.houseWord;
final aiText = _chainAiInterpretations[chain.chainIndex];
var loreText = 'Траектория: ' + pathStr + '\n' + chain.description;
if (aiText != null && aiText.isNotEmpty) {
loreText += '\n\nТолкование Оракула:\n' + aiText;
}
return ReadingSignItem(
title: 'Цепочка #${chain.chainIndex}: ${chain.endTypeTitle}',
subtitle: 'Старт: Дом #${chain.startingHouse.houseIndex + 1} «${startWord.word}» • Шагов: ${chain.steps.length}',
symbol: chain.hasLoop ? '🔄' : (chain.endType == ChainEndType.archonThrone ? '👑' : '🔗'),
lore: loreText,
linkedWord: chain.steps.isNotEmpty ? chain.steps.last.guest : startWord,
meta: {
'chainIndex': chain.chainIndex,
'endType': chain.endType.name,
'stepsCount': chain.steps.length,
'path': pathStr,
},
);
}).toList();
await WordsRepository().addReadingToSyncreticSession(
SyncreticReading(
id: 'chains_${session.id}',
systemId: 'matrix_chains',
systemName: 'Матрица 33×30: Цепочки Домов',
systemIcon: '🔗',
timestamp: session.createdAt,
layoutTitle: '${session.chains.length} ${session.chains.length == 1 ? "цепочка" : (session.chains.length < 5 ? "цепочки" : "цепочек")}',
inputMethod: 'dice',
items: chainItems,
question: session.question,
),
question: session.question,
);
if (showToast && mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Цепочки домов (${session.chains.length}) сохранены в Синхроническую сессию дня',
style: const TextStyle(fontWeight: FontWeight.w600),
),
backgroundColor: const Color(0xFF1E2238),
behavior: SnackBarBehavior.floating,
),
);
}
}
Future<void> _interpretChain(HouseChain chain) async {
setState(() => _loadingChains.add(chain.chainIndex));
HapticFeedback.lightImpact();
final buffer = StringBuffer();
buffer.writeln('Ты — верховный оракул, читающий цепочки домов Ленорман в системе Оракула 999.');
buffer.writeln('Проанализируй путь перетекания силы в цепочке домов:');
buffer.writeln('ЦЕПОЧКА #${chain.chainIndex} (${chain.steps.length} шагов).');
buffer.writeln('ИТОГ ЦЕПОЧКИ: ${chain.description}.\n');
for (int i = 0; i < chain.steps.length; i++) {
final step = chain.steps[i];
final sector = widget.session.sectors[step.house.sectorIndex];
buffer.writeln(
'Шаг ${i + 1}: В Доме #${step.house.houseIndex + 1} «${step.house.houseWord.word.toUpperCase()}» (Архонт «${sector.archonWord.word.toUpperCase()}») выпал Гость «${step.guest.word.toUpperCase()}».',
);
if (step.isLoopClosing) {
buffer.writeln(' ➔ [ПЕТЛЯ ЗАМКНУЛАСЬ: возврат энергии в исходный дом]');
} else if (step.leadsToArchon) {
buffer.writeln(' ➔ [ВОСХОЖДЕНИЕ К АРХОНТУ: увенчание Престолом «${step.destinationArchon?.archonWord.word.toUpperCase()}»]');
} else if (step.computed) {
buffer.writeln(' ➔ [ВЫЧИСЛЕНО ИЗ ПОЛЯ, НЕ ЖРЕБИЙ: дом не был открыт]');
}
}
buffer.writeln('\nДай емкое и глубокое толкование: что запускает этот поток, где ключевой перелом и к какому исходу приводит замыкание цепи.');
try {
final res = await _aiService.interpretSession(
words: chain.steps
.map((s) => SessionWord.fromWordItem(
s.guest,
positionTitle: 'Дом «${s.house.houseWord.word}»',
))
.toList(),
question: widget.session.question ?? 'Цепочка домов судьбы',
);
if (mounted) {
setState(() {
_chainAiInterpretations[chain.chainIndex] = res;
_loadingChains.remove(chain.chainIndex);
});
_recordChainsToSyncreticSession(showToast: false);
}
} catch (e) {
if (mounted) {
setState(() {
_chainAiInterpretations[chain.chainIndex] = 'Связь с оракулом прервалась: $e';
_loadingChains.remove(chain.chainIndex);
});
}
}
}
void _openWordDossier(WordItem word) {
HapticFeedback.selectionClick();
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SymbolDetailScreen(word: word)),
);
}
void _copyChain(HouseChain chain) {
final buffer = StringBuffer();
buffer.writeln('🔗 ОРАКУЛ — ЦЕПОЧКА ДОМОВ #${chain.chainIndex}');
buffer.writeln('Итог: ${chain.description}\n');
for (int i = 0; i < chain.steps.length; i++) {
final s = chain.steps[i];
final archon = widget.session.sectors[s.house.sectorIndex].archonWord.word.toUpperCase();
buffer.writeln(
'${i + 1}. [Архонт: $archon] ➔ Дом «${s.house.houseWord.word.toUpperCase()}» ➔ Гость «${s.guest.word.toUpperCase()}»',
);
if (s.isLoopClosing) {
buffer.writeln(' 🔄 Цикл замкнулся!');
} else if (s.leadsToArchon) {
buffer.writeln(' 👑 Восхождение к Архонту «${s.destinationArchon?.archonWord.word.toUpperCase()}»');
} else if (s.computed) {
buffer.writeln(' ◎ Знак вычислен из поля, это не жребий.');
}
}
if (_chainAiInterpretations[chain.chainIndex] != null) {
buffer.writeln('\n🤖 Толкование Оракула:\n${_chainAiInterpretations[chain.chainIndex]}');
}
Clipboard.setData(ClipboardData(text: buffer.toString()));
HapticFeedback.selectionClick();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Цепочка домов скопирована в буфер'),
behavior: SnackBarBehavior.floating,
),
);
}
@override
Widget build(BuildContext context) {
final chains = widget.session.chains;
return Scaffold(
backgroundColor: const Color(0xFF0A0C16),
appBar: AppBar(
title: const Row(
children: [
GradientStar(size: 20),
SizedBox(width: 8),
Text('Цепочки Домов', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
],
),
actions: [
IconButton(
tooltip: 'Записать цепочки в Синхроническую сессию дня',
icon: const Icon(Icons.bookmark_add_rounded, color: Color(0xFFFFD54F)),
onPressed: () => _recordChainsToSyncreticSession(showToast: true),
),
],
),
body: chains.isEmpty
? const Center(
child: Text(
'В данном раскладе нет открытых цепочек',
style: TextStyle(color: Color(0xFFB0BEC5)),
),
)
: ListView.builder(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 40),
itemCount: chains.length + 1,
itemBuilder: (context, index) {
if (index == 0) {
return _buildHeaderInfo();
}
final chain = chains[index - 1];
return _buildChainCard(chain);
},
),
);
}
Widget _buildHeaderInfo() {
return Container(
margin: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFF151829),
borderRadius: BorderRadius.circular(18),
border: Border.all(color: const Color(0xFFFFD54F).withValues(alpha: 0.3)),
),
child: const Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(Icons.hub_outlined, color: Color(0xFFFFD54F), size: 24),
SizedBox(width: 12),
Expanded(
child: Text(
'Цепочки домов показывают траекторию судьбы: гость из первого дома ведет в собственную обитель на поле 33×30, гость следующего дома ведет дальше — пока энергия не замкнется в петлю судьбы или не взойдет к Престолу Архонта.',
style: TextStyle(color: Color(0xFFCFD8DC), fontSize: 13, height: 1.45),
),
),
],
),
);
}
Widget _buildChainCard(HouseChain chain) {
final isInterpreting = _loadingChains.contains(chain.chainIndex);
final aiText = _chainAiInterpretations[chain.chainIndex];
Color statusColor;
IconData statusIcon;
String statusTitle;
switch (chain.endType) {
case ChainEndType.loop:
statusColor = const Color(0xFFE57373);
statusIcon = Icons.sync_rounded;
statusTitle = 'Петля Судьбы (Замкнутый узел)';
break;
case ChainEndType.archonThrone:
statusColor = const Color(0xFFFFD54F);
statusIcon = Icons.shield_rounded;
statusTitle = 'Восхождение к Престолу Архонта';
break;
case ChainEndType.fieldVector:
statusColor = const Color(0xFF64B5F6);
statusIcon = Icons.trending_flat_rounded;
statusTitle = 'Вектор в поле (Открытый луч)';
break;
}
return Container(
margin: const EdgeInsets.only(bottom: 20),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFF131627),
borderRadius: BorderRadius.circular(22),
border: Border.all(
color: chain.hasLoop
? const Color(0xFFFFD54F).withValues(alpha: 0.5)
: const Color(0xFF2A2D4A),
width: chain.hasLoop ? 1.5 : 1.0,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header: Chain index + Status Badge
Row(
children: [
Text(
'ЦЕПОЧКА #${chain.chainIndex}',
style: const TextStyle(
color: Color(0xFFFFD54F),
fontWeight: FontWeight.w900,
fontSize: 16,
letterSpacing: 0.8,
),
),
const Spacer(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: statusColor.withValues(alpha: 0.18),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: statusColor, width: 1.0),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(statusIcon, size: 14, color: statusColor),
const SizedBox(width: 4),
Text(
statusTitle,
style: TextStyle(
color: statusColor,
fontSize: 11,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
const SizedBox(height: 6),
Text(
chain.description,
style: const TextStyle(color: Color(0xFFB0BEC5), fontSize: 12),
),
const SizedBox(height: 16),
// Steps list
for (int i = 0; i < chain.steps.length; i++) ...[
_buildStepRow(chain.steps[i], i + 1),
if (i < chain.steps.length - 1)
Padding(
padding: const EdgeInsets.only(left: 20, top: 4, bottom: 4),
child: Row(
children: [
const Icon(Icons.arrow_downward_rounded, size: 16, color: Color(0xFFFFD54F)),
const SizedBox(width: 8),
Text(
'переход в обитель знака «${chain.steps[i].guest.word.toUpperCase()}» на поле',
style: const TextStyle(color: Color(0xFF78909C), fontSize: 10, fontStyle: FontStyle.italic),
),
],
),
),
],
const SizedBox(height: 16),
// AI Interpretation if exists
if (aiText != null) ...[
Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: const Color(0xFF1E2238),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: const Color(0xFFFFD54F).withValues(alpha: 0.4)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.auto_awesome_rounded, color: Color(0xFFFFD54F), size: 16),
SizedBox(width: 6),
Text(
'Толкование цепочки Оракулом:',
style: TextStyle(
color: Color(0xFFFFD54F),
fontWeight: FontWeight.bold,
fontSize: 13,
),
),
],
),
const SizedBox(height: 8),
Text(
aiText,
style: const TextStyle(color: Color(0xFFECEFF1), fontSize: 13, height: 1.45),
),
],
),
),
const SizedBox(height: 12),
],
// Action Buttons
Row(
children: [
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF2A2D4A),
foregroundColor: const Color(0xFFFFD54F),
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
side: const BorderSide(color: Color(0xFFFFD54F), width: 0.8),
),
),
onPressed: isInterpreting ? null : () => _interpretChain(chain),
icon: isInterpreting
? const SizedBox(
width: 14,
height: 14,
child: CircularProgressIndicator(strokeWidth: 2, color: Color(0xFFFFD54F)),
)
: const Icon(Icons.auto_awesome, size: 16),
label: Text(
aiText == null ? 'Трактовать (AI)' : 'Обновить (AI)',
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
),
),
const SizedBox(width: 8),
OutlinedButton.icon(
style: OutlinedButton.styleFrom(
foregroundColor: const Color(0xFF00E5FF),
side: const BorderSide(color: Color(0xFF00E5FF), width: 0.8),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MatrixGeometryScreen(
session: widget.session,
initialChainIndex: chain.chainIndex,
),
),
);
},
icon: const Icon(Icons.polyline_rounded, size: 15),
label: const Text('Узор на поле', style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold)),
),
const Spacer(),
IconButton(
tooltip: 'Скопировать цепочку',
icon: const Icon(Icons.copy_rounded, size: 18, color: Color(0xFF9FA8DA)),
onPressed: () => _copyChain(chain),
),
],
),
],
),
);
}
Widget _buildStepRow(HouseChainStep step, int order) {
final sector = widget.session.sectors[step.house.sectorIndex];
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
decoration: BoxDecoration(
color: const Color(0xFF181C33),
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: step.isLoopClosing
? const Color(0xFFE57373)
: (step.leadsToArchon ? const Color(0xFFFFD54F) : sector.sectorColor.withValues(alpha: 0.3)),
width: (step.isLoopClosing || step.leadsToArchon) ? 1.5 : 1.0,
),
),
child: Row(
children: [
// Order circle
Container(
width: 24,
height: 24,
decoration: BoxDecoration(
color: sector.sectorColor.withValues(alpha: 0.2),
shape: BoxShape.circle,
border: Border.all(color: sector.sectorColor),
),
child: Center(
child: Text(
'$order',
style: TextStyle(
color: sector.sectorColor,
fontWeight: FontWeight.w900,
fontSize: 11,
),
),
),
),
const SizedBox(width: 12),
// Content
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// House title
InkWell(
onTap: () => _openWordDossier(step.house.houseWord),
child: Row(
children: [
Text(
'Дом #${(step.house.houseIndex + 1).toString().padLeft(3, '0')}: «${step.house.houseWord.word.toUpperCase()}»',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w900,
fontSize: 13,
),
),
const SizedBox(width: 6),
Text(
'(Архонт: ${sector.archonWord.word})',
style: TextStyle(
color: sector.sectorColor,
fontSize: 11,
fontWeight: FontWeight.w600,
),
),
],
),
),
const SizedBox(height: 3),
// Guest title
InkWell(
onTap: () => _openWordDossier(step.guest),
child: Row(
children: [
const Icon(Icons.auto_awesome_rounded, size: 13, color: Color(0xFFFFD54F)),
const SizedBox(width: 4),
Text(
step.computed
? 'Вычислен из поля, не жребий: «${step.guest.word.toUpperCase()}»'
: 'В него пришел Гость: «${step.guest.word.toUpperCase()}»',
style: const TextStyle(
color: Color(0xFFFFD54F),
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
],
),
),
// Special termination notices
if (step.isLoopClosing) ...[
const SizedBox(height: 4),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: const Color(0xFFE57373).withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(6),
),
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.sync_rounded, size: 12, color: Color(0xFFE57373)),
SizedBox(width: 4),
Text(
'🔄 Узел замкнулся: энергия возвращается в исходный дом!',
style: TextStyle(color: Color(0xFFEF9A9A), fontSize: 11, fontWeight: FontWeight.bold),
),
],
),
),
] else if (step.leadsToArchon) ...[
const SizedBox(height: 4),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: const Color(0xFFFFD54F).withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(6),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.shield_rounded, size: 12, color: Color(0xFFFFD54F)),
const SizedBox(width: 4),
Text(
'👑 Восхождение к Престолу Архонта «${step.destinationArchon?.archonWord.word.toUpperCase()}»!',
style: const TextStyle(color: Color(0xFFFFF9C4), fontSize: 11, fontWeight: FontWeight.bold),
),
],
),
),
],
],
),
),
],
),
);
}
}