import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../ai/ollama_servisi.dart'; import '../app/tema.dart'; import '../karakter/karakter_servisi.dart'; import '../veri/modeller/sahne.dart'; import 'film_servisi.dart'; /// AI Senaryo diyaloğunu açar — sinopsis → otomatik sahne üretimi. void aiSenaryoAc(BuildContext context, WidgetRef ref, int bolumId) { showDialog( context: context, builder: (_) => _AiSenaryoDialog(bolumId: bolumId), ); } class _AiSenaryoDialog extends ConsumerStatefulWidget { const _AiSenaryoDialog({required this.bolumId}); final int bolumId; @override ConsumerState<_AiSenaryoDialog> createState() => _AiSenaryoDialogState(); } class _AiSenaryoDialogState extends ConsumerState<_AiSenaryoDialog> { final _konu = TextEditingController(); double _sahneSayisi = 5; bool _calisiyor = false; String? _hata; @override void dispose() { _konu.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final modeller = ref.watch(ollamaModelleriProvider); return AlertDialog( title: const Row(children: [ Icon(Icons.auto_fix_high_rounded, size: 18, color: FikoRenkler.neonAmber), SizedBox(width: 8), Text('AI Senaryo → Sahneler'), ]), content: SizedBox( width: 540, child: SingleChildScrollView( child: Column(mainAxisSize: MainAxisSize.min, children: [ modeller.when( loading: () => const _Bilgi('Ollama modelleri kontrol ediliyor…'), error: (_, _) => const _Bilgi( 'Ollama\'ya ulaşılamadı. Servisin açık olduğundan emin olun.', hata: true), data: (liste) => liste.isEmpty ? const _Bilgi( 'Ollama açık değil veya kurulu model yok. ' '"ollama pull llama3.1" deneyin.', hata: true) : _Bilgi('Model: ${liste.first}'), ), const SizedBox(height: 10), TextField( controller: _konu, minLines: 3, maxLines: 6, autofocus: true, decoration: const InputDecoration( labelText: 'Konu / sinopsis', hintText: 'Örn: Issız bir kasabaya gelen yabancının ' 'kasabalıların sırrını çözmesi…', ), ), const SizedBox(height: 10), Row(children: [ const Text('Sahne sayısı', style: TextStyle(fontSize: 11.5)), Expanded( child: Slider( value: _sahneSayisi, min: 2, max: 20, divisions: 18, label: '${_sahneSayisi.round()}', onChanged: _calisiyor ? null : (v) => setState(() => _sahneSayisi = v), ), ), Text('${_sahneSayisi.round()}', style: const TextStyle( fontSize: 12, fontWeight: FontWeight.w700)), ]), if (_hata != null) ...[ const SizedBox(height: 6), _Bilgi(_hata!, hata: true), ], ]), ), ), actions: [ TextButton( onPressed: _calisiyor ? null : () => Navigator.pop(context), child: const Text('Vazgeç')), FilledButton.icon( icon: _calisiyor ? const SizedBox( width: 14, height: 14, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white)) : const Icon(Icons.auto_awesome, size: 16), label: Text(_calisiyor ? 'Üretiliyor…' : 'Sahneleri Üret'), onPressed: _calisiyor ? null : _uret, ), ], ); } Future _uret() async { final konu = _konu.text.trim(); if (konu.isEmpty) { setState(() => _hata = 'Lütfen bir konu/sinopsis girin.'); return; } setState(() { _calisiyor = true; _hata = null; }); final messenger = ScaffoldMessenger.of(context); final karakterler = ref.read(karakterlerProvider).valueOrNull ?? []; try { final oneriler = await ref.read(ollamaServisiProvider).senaryoCozumle( konu: konu, sahneSayisi: _sahneSayisi.round(), karakterler: [for (final k in karakterler) k.ad], ); // Karakter adı → id eşleme (büyük/küçük harf duyarsız). final adHaritasi = { for (final k in karakterler) if (k.ad.trim().isNotEmpty) k.ad.toLowerCase().trim(): k.id, }; final sahneler = [ for (final o in oneriler) Sahne() ..aciklama = o.aksiyon ..mekan = o.mekan ..zaman = o.zaman ..diyalog = o.diyalog ..prompt = o.prompt ..karakterIdler = [ for (final ad in o.karakterAdlari) if (adHaritasi[ad.toLowerCase().trim()] != null) adHaritasi[ad.toLowerCase().trim()]!, ], ]; await FilmServisi.senaryodanEkle(widget.bolumId, sahneler); if (!mounted) return; Navigator.pop(context); messenger.showSnackBar(SnackBar( content: Text('${sahneler.length} sahne panoya eklendi.'), duration: const Duration(seconds: 2), )); } on OllamaHatasi catch (e) { if (mounted) setState(() => _hata = e.mesaj); } catch (e) { if (mounted) { setState(() => _hata = 'Beklenmeyen hata: ${e.toString()}'); } } finally { if (mounted) setState(() => _calisiyor = false); } } } class _Bilgi extends StatelessWidget { const _Bilgi(this.metin, {this.hata = false}); final String metin; final bool hata; @override Widget build(BuildContext context) { final renk = hata ? const Color(0xFFFF5370) : FikoRenkler.neonCyan; return Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7), decoration: BoxDecoration( color: renk.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(8), border: Border.all(color: renk.withValues(alpha: 0.4)), ), child: Row(children: [ Icon(hata ? Icons.error_outline : Icons.info_outline, size: 14, color: renk), const SizedBox(width: 7), Expanded( child: Text(metin, style: TextStyle(fontSize: 11, color: renk))), ]), ); } }