import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:path/path.dart' as p; import '../cekirdek/kimlik.dart'; import '../cekirdek/log.dart'; /// Bir kullanıcı şablonu — ACE-Step formundaki tüm ayarları paketler. class AceStepSablon { final String id; final String isim; final String? aciklama; final DateTime olusturma; final DateTime guncelleme; /// Ayarlar — `formSnapshot` ile aynı yapıda. final Map ayarlar; /// Sihirbazda kullanılan ham seçimler (kulturler, turler vb.) — şablonu /// sihirbaz üzerinden tekrar açıp düzenleyebilmek için. final Map? sihirbazSecimler; /// Önceden tanımlı şablonlar düzenlenemez/silinemez. final bool yerlesik; const AceStepSablon({ required this.id, required this.isim, this.aciklama, required this.olusturma, required this.guncelleme, required this.ayarlar, this.sihirbazSecimler, this.yerlesik = false, }); Map toJson() => { 'id': id, 'isim': isim, 'aciklama': aciklama, 'olusturma': olusturma.toIso8601String(), 'guncelleme': guncelleme.toIso8601String(), 'ayarlar': ayarlar, 'sihirbazSecimler': sihirbazSecimler, 'yerlesik': yerlesik, }; static AceStepSablon fromJson(Map j) => AceStepSablon( id: j['id'] as String, isim: j['isim'] as String, aciklama: j['aciklama'] as String?, olusturma: DateTime.parse(j['olusturma'] as String), guncelleme: DateTime.parse(j['guncelleme'] as String), ayarlar: Map.from(j['ayarlar'] as Map), sihirbazSecimler: j['sihirbazSecimler'] is Map ? Map.from(j['sihirbazSecimler'] as Map) : null, yerlesik: j['yerlesik'] as bool? ?? false, ); AceStepSablon kopyala({ String? isim, String? aciklama, Map? ayarlar, Map? sihirbazSecimler, }) => AceStepSablon( id: id, isim: isim ?? this.isim, aciklama: aciklama ?? this.aciklama, olusturma: olusturma, guncelleme: DateTime.now(), ayarlar: ayarlar ?? this.ayarlar, sihirbazSecimler: sihirbazSecimler ?? this.sihirbazSecimler, yerlesik: yerlesik, ); } class AceStepSablonDurumu extends StateNotifier> { AceStepSablonDurumu() : super([]) { _yukle(); } Box? _kutu; Future _box() async => _kutu ??= await Hive.openBox('ace_step_sablonlar'); Future _yukle() async { final b = await _box(); final liste = []; for (final v in b.values) { try { liste.add(AceStepSablon.fromJson(Map.from(v as Map))); } catch (e) { Log.uyari('Sablon', 'Şablon yükleme hatası: $e'); } } liste.sort((a, b) => b.guncelleme.compareTo(a.guncelleme)); state = liste; } Future kaydet({ String? id, required String isim, String? aciklama, required Map ayarlar, Map? sihirbazSecimler, }) async { final mevcut = id != null ? state.where((s) => s.id == id).firstOrNull : null; final sablon = AceStepSablon( id: mevcut?.id ?? FikoYTId.yeni(), isim: isim, aciklama: aciklama, olusturma: mevcut?.olusturma ?? DateTime.now(), guncelleme: DateTime.now(), ayarlar: ayarlar, sihirbazSecimler: sihirbazSecimler, yerlesik: mevcut?.yerlesik ?? false, ); final b = await _box(); await b.put(sablon.id, sablon.toJson()); state = [ sablon, ...state.where((s) => s.id != sablon.id), ]..sort((a, b) => b.guncelleme.compareTo(a.guncelleme)); return sablon; } Future sil(String id) async { final hedef = state.where((s) => s.id == id).firstOrNull; if (hedef == null || hedef.yerlesik) return; final b = await _box(); await b.delete(id); state = state.where((s) => s.id != id).toList(); } AceStepSablon? bul(String id) { final liste = state.where((s) => s.id == id); return liste.isEmpty ? null : liste.first; } } final aceStepSablonProvider = StateNotifierProvider>( (ref) => AceStepSablonDurumu()); // ============================================================ // Son ayar otomatik kaydı — global + per-project // ============================================================ /// Form değiştikçe debounced olarak son ayarları kaydeder. /// - Global: Hive box `ace_step_son_ayarlar` → key='global' /// - Proje: /ace_step_son_ayarlar.json class AceStepSonAyar { static Box? _kutu; static Future _box() async => _kutu ??= await Hive.openBox('ace_step_son_ayarlar'); static Timer? _debounce; /// Debounced kayıt — 800ms. static void kaydetDebounced({ required Map ayarlar, String? projeKlasoru, }) { _debounce?.cancel(); _debounce = Timer(const Duration(milliseconds: 800), () { _kaydetHemen(ayarlar: ayarlar, projeKlasoru: projeKlasoru); }); } /// Anında kaydet (debounce'u atla) — sihirbaz uygula / şablon yükle gibi /// kritik anlar için. static Future kaydetHemen({ required Map ayarlar, String? projeKlasoru, }) async { _debounce?.cancel(); return _kaydetHemen(ayarlar: ayarlar, projeKlasoru: projeKlasoru); } static Future _kaydetHemen({ required Map ayarlar, String? projeKlasoru, }) async { try { final b = await _box(); // Global her zaman güncellenir await b.put('global', ayarlar); // Proje varsa ayrıca dosyaya yaz if (projeKlasoru != null && projeKlasoru.isNotEmpty) { final dosya = File(p.join(projeKlasoru, 'ace_step_son_ayarlar.json')); await dosya.parent.create(recursive: true); await dosya.writeAsString(jsonEncode(ayarlar)); } } catch (e) { Log.uyari('AceStepSonAyar', 'Kaydet hatası: $e'); } } /// Yükler — önce proje, yoksa global. Hiçbiri yoksa null. static Future?> yukle({String? projeKlasoru}) async { try { // Proje öncelikli if (projeKlasoru != null && projeKlasoru.isNotEmpty) { final dosya = File(p.join(projeKlasoru, 'ace_step_son_ayarlar.json')); if (await dosya.exists()) { final j = jsonDecode(await dosya.readAsString()); if (j is Map) return Map.from(j); } } // Global final b = await _box(); final v = b.get('global'); if (v is Map) return Map.from(v); } catch (e) { Log.uyari('AceStepSonAyar', 'Yükle hatası: $e'); } return null; } } extension _IterableFirstOrNull on Iterable { E? get firstOrNull { final it = iterator; return it.moveNext() ? it.current : null; } }