import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../app/tema.dart'; import '../comfy/uretim_kuyrugu.dart'; import '../comfy/workflow/muzik_workflow.dart'; import '../comfy/workflow/resim_workflow.dart'; import '../comfy/workflow/video_workflow.dart'; import '../muzik/muzik_form_state.dart'; import '../resim/resim_form_state.dart'; import '../veri/isar_servisi.dart'; import '../veri/modeller/uretim_kaydi.dart'; import '../video/video_form_state.dart'; import 'profesyonel_calar.dart'; import 'ses_kes_dialog.dart'; import 'video_oynatici_dialog.dart'; /// Her stüdyonun kendi içinde gösterdiği üretim geçmişi paneli. /// Başlık yeniden adlandırma + parametreleri forma geri yükleme + oynatma/önizleme. class StudyoGecmisPaneli extends ConsumerWidget { const StudyoGecmisPaneli({super.key, required this.tip}); final UretimTipi tip; @override Widget build(BuildContext context, WidgetRef ref) { final hepsi = ref.watch(uretimKayitlariProvider).valueOrNull ?? []; final kayitlar = hepsi.where((k) => k.tip == tip).toList(); return Container( width: 340, decoration: BoxDecoration( color: Theme.of(context).canvasColor, borderRadius: BorderRadius.circular(11), border: Border.all(color: Theme.of(context).dividerColor), ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Padding( padding: const EdgeInsets.fromLTRB(12, 10, 8, 6), child: Row( children: [ const Icon(Icons.history, size: 17, color: FikoRenkler.neonMor), const SizedBox(width: 6), const Text('Üretim Geçmişi', style: TextStyle(fontSize: 13, fontWeight: FontWeight.w700)), const Spacer(), Text('${kayitlar.length}', style: TextStyle( fontSize: 11, color: Theme.of(context) .colorScheme .onSurface .withValues(alpha: 0.5))), ], ), ), const Divider(height: 1), Expanded( child: kayitlar.isEmpty ? Center( child: Text('Henüz üretim yok', style: TextStyle( fontSize: 12, color: Theme.of(context) .colorScheme .onSurface .withValues(alpha: 0.45)))) : ListView.separated( padding: const EdgeInsets.all(8), itemCount: kayitlar.length, separatorBuilder: (_, _) => const SizedBox(height: 8), itemBuilder: (_, i) => _GecmisKart(kayit: kayitlar[i], tip: tip), ), ), ], ), ); } } class _GecmisKart extends ConsumerWidget { const _GecmisKart({required this.kayit, required this.tip}); final UretimKaydi kayit; final UretimTipi tip; @override Widget build(BuildContext context, WidgetRef ref) { final soluk = Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5); final tamam = kayit.durum == UretimDurumu.tamamlandi; final (durumRenk, durumYazi) = switch (kayit.durum) { UretimDurumu.tamamlandi => (FikoRenkler.neonYesil, 'Tamam'), UretimDurumu.hata => (Theme.of(context).colorScheme.error, 'Hata'), UretimDurumu.iptal => (Colors.grey, 'İptal'), _ => (FikoRenkler.neonAmber, 'Üretiliyor'), }; return Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(9), border: Border.all(color: Theme.of(context).dividerColor), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text(kayit.baslik, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( fontSize: 12, fontWeight: FontWeight.w700)), ), InkWell( borderRadius: BorderRadius.circular(5), onTap: () => _adlandir(context), child: const Padding( padding: EdgeInsets.all(3), child: Icon(Icons.edit, size: 14), ), ), InkWell( borderRadius: BorderRadius.circular(5), onTap: () => kayitVeDosyalariSil(kayit.id), child: const Padding( padding: EdgeInsets.all(3), child: Icon(Icons.delete_outline, size: 14), ), ), ], ), Row(children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 1), decoration: BoxDecoration( color: durumRenk.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(4), ), child: Text(durumYazi, style: TextStyle(fontSize: 9, color: durumRenk)), ), const SizedBox(width: 6), Text(_tarih(kayit.olusturmaZamani), style: TextStyle(fontSize: 9.5, color: soluk)), ]), if (tamam && kayit.ciktiDosyalari.isNotEmpty) ...[ const SizedBox(height: 6), _onizleme(), ], const SizedBox(height: 6), Row( children: [ Expanded( child: OutlinedButton.icon( style: OutlinedButton.styleFrom( visualDensity: VisualDensity.compact, padding: const EdgeInsets.symmetric(horizontal: 6), textStyle: const TextStyle(fontSize: 10.5)), icon: const Icon(Icons.edit_note, size: 14), label: const Text('Forma yükle'), onPressed: () => _formaYukle(ref, context), ), ), const SizedBox(width: 5), IconButton( tooltip: 'Aynısını tekrar üret', visualDensity: VisualDensity.compact, icon: const Icon(Icons.refresh, size: 16), onPressed: () => ref.read(uretimKuyruguProvider.notifier).yenidenDene(kayit), ), IconButton( tooltip: 'Varyasyon (yeni seed)', visualDensity: VisualDensity.compact, icon: const Icon(Icons.shuffle, size: 16), onPressed: () => _varyasyonUret(ref), ), if (tamam && tip == UretimTipi.muzik && kayit.ciktiDosyalari.isNotEmpty) IconButton( tooltip: 'Kes / Böl', visualDensity: VisualDensity.compact, icon: const Icon(Icons.content_cut, size: 15, color: FikoRenkler.neonCyan), onPressed: () => showSesKesDialog(context, ref, kayit), ), IconButton( tooltip: kayit.favori ? 'Favoriden çıkar' : 'Favorile', visualDensity: VisualDensity.compact, icon: Icon(kayit.favori ? Icons.star : Icons.star_border, size: 16, color: kayit.favori ? FikoRenkler.neonAmber : null), onPressed: () => isar.writeTxn(() { kayit.favori = !kayit.favori; return isar.uretimKaydis.put(kayit); }), ), ], ), ], ), ); } Widget _onizleme() { final dosya = kayit.ciktiDosyalari.first; return switch (tip) { UretimTipi.muzik => ProfesyonelCalar(dosyaYolu: dosya, kompakt: true), UretimTipi.resim => _ResimOnizleme(dosya: dosya), UretimTipi.video => Builder( builder: (context) => Row(children: [ const Icon(Icons.movie, size: 18, color: FikoRenkler.neonMor), const SizedBox(width: 6), Expanded( child: FilledButton.icon( style: FilledButton.styleFrom( visualDensity: VisualDensity.compact, backgroundColor: FikoRenkler.neonMor.withValues(alpha: 0.8), textStyle: const TextStyle(fontSize: 10.5)), icon: const Icon(Icons.play_arrow, size: 16), label: const Text('Oynat'), onPressed: File(dosya).existsSync() ? () => showVideoOynatici(context, dosya, baslik: kayit.baslik) : null, ), ), ])), }; } /// Yeni seed ile varyasyon — aynı varyant grubuna eklenir. Future _varyasyonUret(WidgetRef ref) async { try { final j = Map.from( jsonDecode(kayit.parametrelerJson) as Map); final kuyruk = ref.read(uretimKuyruguProvider.notifier); switch (tip) { case UretimTipi.muzik: final y = MuzikParametreleri.fromJson(j) .kopyala(seed: MuzikParametreleri.rastgeleSeed()); await kuyruk.uretimEkle( tip: tip, baslik: '${kayit.baslik} (varyasyon)', parametrelerJson: y.jsonString(), sihirbazJson: kayit.sihirbazJson, workflow: muzikWorkflowOlustur(y), varyantGrubu: kayit.etkinGrup); case UretimTipi.resim: final y = ResimParametreleri.fromJson(j) .kopyala(seed: ResimParametreleri.rastgeleSeed()); await kuyruk.uretimEkle( tip: tip, baslik: '${kayit.baslik} (varyasyon)', parametrelerJson: y.jsonString(), sihirbazJson: kayit.sihirbazJson, workflow: resimWorkflowOlustur(y), varyantGrubu: kayit.etkinGrup); case UretimTipi.video: final y = VideoParametreleri.fromJson(j) .kopyala(seed: VideoParametreleri.rastgeleSeed()); await kuyruk.uretimEkle( tip: tip, baslik: '${kayit.baslik} (varyasyon)', parametrelerJson: y.jsonString(), workflow: videoWorkflowOlustur(y), varyantGrubu: kayit.etkinGrup); } } catch (_) {} } void _formaYukle(WidgetRef ref, BuildContext context) { switch (tip) { case UretimTipi.muzik: ref .read(muzikFormProvider.notifier) .parametrelerdenYukle(kayit.parametrelerJson, kayit.sihirbazJson); case UretimTipi.resim: ref .read(resimFormProvider.notifier) .parametrelerdenYukle(kayit.parametrelerJson, kayit.sihirbazJson); case UretimTipi.video: ref .read(videoFormProvider.notifier) .parametrelerdenYukle(kayit.parametrelerJson); } ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Parametreler forma yüklendi'), duration: Duration(seconds: 2))); } void _adlandir(BuildContext context) { final ctrl = TextEditingController(text: kayit.baslik); showDialog( context: context, builder: (ctx) => AlertDialog( title: const Text('Başlığı Değiştir'), content: TextField( controller: ctrl, autofocus: true, decoration: const InputDecoration(labelText: 'Başlık'), onSubmitted: (_) => _kaydet(ctx, ctrl.text), ), actions: [ TextButton( onPressed: () => Navigator.pop(ctx), child: const Text('Vazgeç')), FilledButton( onPressed: () => _kaydet(ctx, ctrl.text), child: const Text('Kaydet')), ], ), ); } void _kaydet(BuildContext ctx, String yeni) { if (yeni.trim().isNotEmpty) { kayit.baslik = yeni.trim(); isar.writeTxn(() => isar.uretimKaydis.put(kayit)); } Navigator.pop(ctx); } static String _tarih(DateTime t) => '${t.day.toString().padLeft(2, '0')}.${t.month.toString().padLeft(2, '0')} ' '${t.hour.toString().padLeft(2, '0')}:${t.minute.toString().padLeft(2, '0')}'; } class _ResimOnizleme extends StatelessWidget { const _ResimOnizleme({required this.dosya}); final String dosya; @override Widget build(BuildContext context) { if (!File(dosya).existsSync()) return const SizedBox.shrink(); return GestureDetector( onTap: () => showDialog( context: context, builder: (ctx) => Dialog( backgroundColor: Colors.transparent, child: InteractiveViewer( child: Image.file(File(dosya), fit: BoxFit.contain)), ), ), child: ClipRRect( borderRadius: BorderRadius.circular(7), child: Image.file(File(dosya), height: 90, width: double.infinity, fit: BoxFit.cover, cacheWidth: 360), ), ); } }