import 'package:pocketbase/pocketbase.dart'; import '../api/pocketbase_client.dart'; import '../../models/tenant.dart'; class AiContextBuilder { AiContextBuilder._(); static final instance = AiContextBuilder._(); PocketBase get _pb => PocketBaseClient.instance.pb; Future build(TenantMembership membership) async { final tenant = membership.tenant; final tenantId = tenant.id; final isLab = tenant.kind == TenantKind.lab; final now = DateTime.now(); final dateStr = '${now.day}.${now.month}.${now.year}'; final results = await Future.wait([ _fetchActiveJobs(tenantId, isLab), _fetchRecentDelivered(tenantId, isLab), _fetchFinance(tenantId, isLab), _fetchTeam(tenantId), ]); final actions = _actionsPrompt(isLab); return 'Sen DLS (Dental Lab System) uygulamasinin akilli asistanisin.\n' '${tenant.companyName} adli ${isLab ? 'dental laboratuvarinin' : 'dis kliniginin'} verilerine erisebilirsin.\n' 'Kullanici rolu: ${isLab ? 'LABORATUVAR' : 'KLINIK'}\n' '\n' 'Tarih: $dateStr\n' '\n' '${results[0]}\n' '\n' '${results[1]}\n' '\n' '${results[2]}\n' '\n' '${results[3]}\n' '\n' '$actions\n' '\n' 'Yanit kurallari:\n' '- Turkce, kisa ve net yaz\n' '- Sadece yukaridaki verilerden hareketle yorum yap\n' '- Listelerde madde isareti (- ) kullan\n' '- Onemli bilgileri **kalin** yaz\n' '- Aksiyon etiketlerini HERZAMAN metnin sonuna koy\n' '- ${isLab ? 'Is kodlari icin [ID:...] formatini kullan' : 'Hasta kodlari ve is durumlarini net belirt'}\n'; } static String _actionsPrompt(bool isLab) { final buf = StringBuffer(); buf.writeln('## EYLEM YETKILERIN'); buf.writeln('Kullanici bir islem yapmak istediginde asagidaki XML etiketlerini yanita ekle:'); buf.writeln(''); buf.writeln('Is dosyalarini gostermek:'); buf.writeln(''); buf.writeln(''); buf.writeln('Is iptal etmek:'); buf.writeln(''); if (!isLab) { buf.writeln(''); buf.writeln('Teslim edildi isaretlemek (sadece klinik):'); buf.writeln(''); } buf.writeln(''); buf.writeln('Ekip uyesi eklemek (TUM bilgiler alindiktan sonra):'); buf.writeln(''); buf.writeln(''); buf.writeln('KURALLAR:'); buf.writeln('- Etiketi SADECE kullanici acikca islem istediginde ekle'); buf.writeln('- Sifre sorulursa kullanicidan al, ASLA uydurma'); buf.writeln('- iptal gibi geri alinmaz islemleri acikca belirt'); buf.writeln('- Etiket icindeki job_id degerini yukaridaki is listesinden al'); buf.writeln('- etiketlerini KESINLİKLE kod blogu (```xml veya ```) icine ALMA, duz metin olarak yaz'); return buf.toString(); } Future _fetchActiveJobs(String tenantId, bool isLab) async { try { final tenantField = isLab ? 'lab_tenant_id' : 'clinic_tenant_id'; final counterpartField = isLab ? 'clinic_tenant_id' : 'lab_tenant_id'; final result = await _pb.collection('jobs').getList( filter: '$tenantField = "$tenantId" && status != "delivered" && status != "cancelled"', perPage: 60, sort: '-created', expand: counterpartField, ); if (result.items.isEmpty) return '## Aktif Isler\nSu an aktif is yok.'; final counterpartLabel = isLab ? 'Klinik' : 'Lab'; final lines = result.items.map((r) { final j = r.toJson(); final jobId = j['id'] as String? ?? ''; final expand = j['expand'] as Map?; final counterpart = (expand?[counterpartField] as Map?)?['company_name'] as String? ?? '-'; final status = _statusTr(j['status'] as String? ?? ''); final prosthetic = j['prosthetic_type'] as String? ?? '-'; final patient = j['patient_code'] as String? ?? '-'; final step = j['current_step'] as String?; final stepPart = (step != null && step.isNotEmpty) ? ' | Adim: $step' : ''; final due = j['due_date'] as String? ?? ''; final duePart = due.isNotEmpty ? ' | Termin: ${due.substring(0, 10)}' : ''; return '- [ID:$jobId] Hasta: $patient | $prosthetic | $status$stepPart | $counterpartLabel: $counterpart$duePart'; }).join('\n'); return '## Aktif Isler (${result.items.length})\n$lines'; } catch (e) { return '## Aktif Isler\n(Veri alinamadi: $e)'; } } Future _fetchRecentDelivered(String tenantId, bool isLab) async { try { final tenantField = isLab ? 'lab_tenant_id' : 'clinic_tenant_id'; final counterpartField = isLab ? 'clinic_tenant_id' : 'lab_tenant_id'; final result = await _pb.collection('jobs').getList( filter: '$tenantField = "$tenantId" && status = "delivered"', perPage: 10, sort: '-updated', expand: counterpartField, ); if (result.items.isEmpty) return '## Son Teslim Edilenler\nHenuz teslim edilen is yok.'; final counterpartLabel = isLab ? 'Klinik' : 'Lab'; final lines = result.items.map((r) { final j = r.toJson(); final jobId = j['id'] as String? ?? ''; final expand = j['expand'] as Map?; final counterpart = (expand?[counterpartField] as Map?)?['company_name'] as String? ?? '-'; final prosthetic = j['prosthetic_type'] as String? ?? '-'; final patient = j['patient_code'] as String? ?? '-'; final updated = (j['updated'] as String? ?? ''); final datePart = updated.length >= 10 ? updated.substring(0, 10) : ''; return '- [ID:$jobId] Hasta: $patient | $prosthetic | $counterpartLabel: $counterpart${datePart.isNotEmpty ? ' | Tarih: $datePart' : ''}'; }).join('\n'); return '## Son Teslim Edilenler (son 10)\n$lines'; } catch (_) { return '## Son Teslim Edilenler\n(Veri alinamadi)'; } } Future _fetchFinance(String tenantId, bool isLab) async { try { final type = isLab ? 'receivable' : 'payable'; final result = await _pb.collection('finance_entries').getList( filter: 'tenant_id = "$tenantId" && type = "$type"', perPage: 200, ); double pending = 0, paid = 0; for (final r in result.items) { final j = r.toJson(); final amount = (j['amount'] as num?)?.toDouble() ?? 0; if (j['status'] == 'pending') { pending += amount; } else { paid += amount; } } final label = isLab ? 'alacak' : 'borc'; return '## Finans\n' '- Bekleyen $label: ${pending.toStringAsFixed(0)} TL\n' '- Tahsil edilen: ${paid.toStringAsFixed(0)} TL'; } catch (_) { return '## Finans\n(Veri alinamadi)'; } } Future _fetchTeam(String tenantId) async { try { final result = await _pb.collection('tenant_members').getList( filter: 'tenant_id = "$tenantId"', expand: 'user_id', perPage: 50, ); if (result.items.isEmpty) return '## Ekip\nUye yok.'; final lines = result.items.map((r) { final j = r.toJson(); final expand = j['expand'] as Map?; final user = expand?['user_id'] as Map?; final first = (user?['first_name'] as String?) ?? ''; final last = (user?['last_name'] as String?) ?? ''; final email = (user?['email'] as String?) ?? ''; final name = '$first $last'.trim().isNotEmpty ? '$first $last'.trim() : email; final role = _roleTr(j['role'] as String? ?? ''); return '- $name ($role)'; }).join('\n'); return '## Ekip (${result.items.length} uye)\n$lines'; } catch (_) { return '## Ekip\n(Veri alinamadi)'; } } static String _statusTr(String s) => switch (s) { 'pending' => 'Bekliyor', 'in_progress' => 'Devam ediyor', 'sent' => 'Gonderildi', 'revision' => 'Revizyon', 'delivered' => 'Teslim edildi', 'cancelled' => 'Iptal', _ => s, }; static String _roleTr(String s) => switch (s) { 'owner' => 'Sahibi', 'admin' => 'Yonetici', 'technician' => 'Teknisyen', 'delivery' => 'Teslimat', 'finance' => 'Finans', 'doctor' => 'Hekim', _ => 'Uye', }; }