import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/providers/auth_provider.dart'; import '../../../core/providers/locale_provider.dart'; import '../../../core/theme/app_theme.dart'; import '../../../core/utils/currency_formatter.dart'; import '../../../core/widgets/gradient_app_bar.dart'; import '../../../core/widgets/pill_tabs.dart'; import '../../../models/finance_entry.dart'; import 'lab_finance_repository.dart'; enum _FinanceSort { newestFirst, byAmountDesc, byAmountAsc } class LabFinanceScreen extends ConsumerStatefulWidget { const LabFinanceScreen({super.key}); @override ConsumerState createState() => _LabFinanceScreenState(); } class _LabFinanceScreenState extends ConsumerState with SingleTickerProviderStateMixin { late TabController _tabController; late Future<_FinanceData> _future; _FinanceSort _sort = _FinanceSort.newestFirst; @override void initState() { super.initState(); _tabController = TabController(length: 2, vsync: this); _tabController.addListener(() { if (mounted) setState(() {}); }); _load(); } @override void dispose() { _tabController.dispose(); super.dispose(); } void _load() { final tenantId = ref.read(authProvider).activeTenant!.tenant.id; setState(() { _future = Future.wait([ LabFinanceRepository.instance.listEntries(tenantId, status: 'pending'), LabFinanceRepository.instance.listEntries(tenantId, status: 'paid'), LabFinanceRepository.instance.summary(tenantId), LabFinanceRepository.instance.byCounterparty(tenantId), ]).then((results) => _FinanceData( pending: results[0] as List, paid: results[1] as List, summary: results[2] as Map, counterparties: results[3] as List, )); }); } Future _showSortOptions() async { final s = ref.read(stringsProvider); final result = await showSortSheet( context, title: s.sort, options: [s.sortNewest, s.sortAmountDesc, s.sortAmountAsc], current: _sort.index, ); if (result != null) { setState(() => _sort = _FinanceSort.values[result]); } } List _sorted(List entries) { final list = List.from(entries); switch (_sort) { case _FinanceSort.newestFirst: list.sort((a, b) { final da = a.dateCreated != null ? DateTime.tryParse(a.dateCreated!) : null; final db = b.dateCreated != null ? DateTime.tryParse(b.dateCreated!) : null; if (da == null && db == null) return 0; if (da == null) return 1; if (db == null) return -1; return db.compareTo(da); }); case _FinanceSort.byAmountDesc: list.sort((a, b) => b.amount.compareTo(a.amount)); case _FinanceSort.byAmountAsc: list.sort((a, b) => a.amount.compareTo(b.amount)); } return list; } String _formatDate(String? raw) { if (raw == null) return ''; try { final dt = DateTime.parse(raw); return '${dt.day.toString().padLeft(2, '0')}.${dt.month.toString().padLeft(2, '0')}.${dt.year}'; } catch (_) { return ''; } } Future _confirmPayment( FinanceEntry entry, String Function(double) formatAmount, ) async { final confirmed = await showDialog( context: context, builder: (ctx) => AlertDialog( title: const Text('Ödeme Onayla'), content: Text( '${entry.counterpartyName ?? "Bu kayıt"} için ' '${formatAmount(entry.amount)} tutarındaki ödemenin ' 'hesabınıza ulaştığını onaylıyor musunuz?', ), actions: [ TextButton( onPressed: () => Navigator.pop(ctx, false), child: const Text('İptal'), ), FilledButton( onPressed: () => Navigator.pop(ctx, true), child: const Text('Onayla'), ), ], ), ); if (confirmed != true || !mounted) return; try { await LabFinanceRepository.instance.confirmPayment(entry.id); _load(); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Ödeme onaylandı.')), ); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Hata: $e')), ); } } } @override Widget build(BuildContext context) { final isSortActive = _sort != _FinanceSort.newestFirst; final s = ref.watch(stringsProvider); final currencyCode = ref.watch(authProvider).activeTenant?.tenant.defaultCurrency ?? 'TRY'; String formatAmount(double amount) => CurrencyFormatter.format(amount, currencyCode); return Scaffold( backgroundColor: AppColors.background, appBar: GradientAppBar( title: s.finance, category: s.laboratoryCategory, actions: [ IconButton( onPressed: _showSortOptions, tooltip: 'Sırala', icon: Badge( isLabelVisible: isSortActive, smallSize: 8, backgroundColor: AppColors.accent, child: const Icon(Icons.sort_rounded), ), ), ], ), body: RefreshIndicator( color: AppColors.accent, onRefresh: () async => _load(), child: FutureBuilder<_FinanceData>( future: _future, builder: (ctx, snap) { if (snap.connectionState == ConnectionState.waiting) { return const Center( child: CircularProgressIndicator(color: AppColors.accent)); } if (snap.hasError) { return Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 64, height: 64, decoration: BoxDecoration( color: AppColors.cancelledBg, borderRadius: BorderRadius.circular(16)), child: const Icon(Icons.wifi_off_rounded, color: AppColors.cancelled, size: 30), ), const SizedBox(height: 16), Text('Hata: ${snap.error}', style: const TextStyle(color: AppColors.textSecondary)), const SizedBox(height: 12), FilledButton.icon( onPressed: _load, icon: const Icon(Icons.refresh_rounded, size: 18), label: const Text('Tekrar Dene'), ), ], ), ); } final data = snap.data!; final pendingTotal = data.summary['pending'] ?? 0.0; final paidTotal = data.summary['paid'] ?? 0.0; final pending = _sorted(data.pending); final paid = _sorted(data.paid); return Column( children: [ Padding( padding: const EdgeInsets.all(16), child: Row( children: [ Expanded( child: _SummaryCard( label: 'Açık Alacak', amount: formatAmount(pendingTotal), color: AppColors.pending, bgColor: AppColors.pendingBg, icon: Icons.hourglass_empty_rounded, ), ), const SizedBox(width: 12), Expanded( child: _SummaryCard( label: 'Onaylanan Tahsilat', amount: formatAmount(paidTotal), color: AppColors.success, bgColor: AppColors.successBg, icon: Icons.check_circle_outline, ), ), ], ), ), if (data.counterparties.isNotEmpty) Padding( padding: const EdgeInsets.fromLTRB(16, 0, 16, 12), child: _CounterpartySummaryList( title: 'Klinik Bazlı Alacak', items: data.counterparties, formatAmount: formatAmount, ), ), PillTabs( tabs: [s.pending, s.collected], selected: _tabController.index, onSelect: (i) => _tabController.animateTo(i), counts: [pending.length, paid.length], ), Expanded( child: TabBarView( controller: _tabController, children: [ _EntriesList( entries: pending, emptyMessage: s.noPendingEntries, emptyIcon: Icons.hourglass_empty_rounded, formatDate: _formatDate, formatAmount: formatAmount, onConfirmPayment: (entry) => _confirmPayment(entry, formatAmount), ), _EntriesList( entries: paid, emptyMessage: s.noPaidEntries, emptyIcon: Icons.check_circle_outline, formatDate: _formatDate, formatAmount: formatAmount, ), ], ), ), ], ); }, ), ), ); } } class _FinanceData { const _FinanceData({ required this.pending, required this.paid, required this.summary, required this.counterparties, }); final List pending; final List paid; final Map summary; final List counterparties; } class _SummaryCard extends StatelessWidget { const _SummaryCard({ required this.label, required this.amount, required this.color, required this.bgColor, required this.icon, }); final String label; final String amount; final Color color; final Color bgColor; final IconData icon; @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(16), border: Border.all(color: AppColors.border), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.06), blurRadius: 12, offset: const Offset(0, 4)) ], ), child: Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: bgColor, borderRadius: BorderRadius.circular(12)), child: Icon(icon, color: color, size: 22), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( amount, style: TextStyle( fontSize: 18, fontWeight: FontWeight.w800, color: color, height: 1), ), const SizedBox(height: 3), Text(label, style: const TextStyle( fontSize: 12, color: AppColors.textSecondary, fontWeight: FontWeight.w500)), ], ), ), ], ), ); } } class _EntriesList extends StatelessWidget { const _EntriesList({ required this.entries, required this.emptyMessage, required this.emptyIcon, required this.formatDate, required this.formatAmount, this.onConfirmPayment, }); final List entries; final String emptyMessage; final IconData emptyIcon; final String Function(String?) formatDate; final String Function(double) formatAmount; final Future Function(FinanceEntry entry)? onConfirmPayment; @override Widget build(BuildContext context) { if (entries.isEmpty) { return Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 72, height: 72, decoration: BoxDecoration( color: AppColors.inProgressBg, borderRadius: BorderRadius.circular(20)), child: Icon(emptyIcon, size: 32, color: AppColors.inProgress), ), const SizedBox(height: 16), Text(emptyMessage, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: AppColors.textPrimary)), ], ), ); } return ListView.builder( padding: const EdgeInsets.fromLTRB(16, 8, 16, 24), itemCount: entries.length, itemBuilder: (ctx, i) { final entry = entries[i]; final isPending = entry.status == FinanceStatus.pending; final isReported = entry.status == FinanceStatus.reported; final statusColor = isPending ? AppColors.pending : isReported ? AppColors.accent : AppColors.success; final statusBg = isPending ? AppColors.pendingBg : isReported ? AppColors.inProgressBg : AppColors.successBg; return Padding( padding: const EdgeInsets.only(bottom: 10), child: Material( color: AppColors.surface, borderRadius: BorderRadius.circular(14), child: InkWell( onTap: isReported && onConfirmPayment != null ? () => onConfirmPayment!(entry) : null, borderRadius: BorderRadius.circular(14), child: Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(14), border: Border.all(color: AppColors.border), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.04), blurRadius: 8, offset: const Offset(0, 2)) ], ), child: Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: statusBg, borderRadius: BorderRadius.circular(12)), child: Icon( isPending ? Icons.hourglass_empty_rounded : isReported ? Icons.verified_outlined : Icons.check_circle_outline, color: statusColor, size: 22, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( entry.counterpartyName ?? 'Klinik', style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w600, color: AppColors.textPrimary), ), if (entry.patientCode != null) ...[ const SizedBox(height: 2), Text( 'Protokol: ${entry.patientCode}', style: const TextStyle( fontSize: 12, color: AppColors.textSecondary), ), ], if (entry.dateCreated != null) ...[ const SizedBox(height: 2), Text( formatDate(entry.dateCreated), style: const TextStyle( fontSize: 12, color: AppColors.textMuted), ), ], if (isReported) ...[ const SizedBox(height: 4), const Text( 'Dokunarak ödeme onayı verebilirsiniz.', style: TextStyle( fontSize: 12, color: AppColors.textSecondary), ), ] else if (isPending) ...[ const SizedBox(height: 4), const Text( 'Klinikten ödeme bildirimi bekleniyor.', style: TextStyle( fontSize: 12, color: AppColors.textSecondary), ), ], ], ), ), const SizedBox(width: 8), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( formatAmount(entry.amount), style: TextStyle( fontWeight: FontWeight.w700, color: statusColor, fontSize: 15, ), ), const SizedBox(height: 4), Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 2), decoration: BoxDecoration( color: statusBg, borderRadius: BorderRadius.circular(8), ), child: Text( entry.status.label, style: TextStyle( color: statusColor, fontSize: 11, fontWeight: FontWeight.w600, ), ), ), ], ), ], ), ), ), ), ); }, ); } } class _CounterpartySummaryList extends StatelessWidget { const _CounterpartySummaryList({ required this.title, required this.items, required this.formatAmount, }); final String title; final List items; final String Function(double) formatAmount; @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(16), border: Border.all(color: AppColors.border), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w700, color: AppColors.textPrimary, ), ), const SizedBox(height: 10), for (final item in items.take(5)) ...[ Row( children: [ Expanded( child: Text( item.counterpartyName, style: const TextStyle( fontSize: 13, color: AppColors.textPrimary, fontWeight: FontWeight.w600, ), ), ), Text( formatAmount(item.pendingAmount), style: const TextStyle( fontSize: 13, color: AppColors.pending, fontWeight: FontWeight.w700, ), ), ], ), const SizedBox(height: 8), ], ], ), ); } }