import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:kifi_app/features/inventory/domain/inventory_item.dart'; import 'package:kifi_app/features/inventory/domain/product.dart'; import 'package:kifi_app/features/inventory/providers/products_provider.dart'; import 'package:kifi_app/features/inventory/providers/product_categories_provider.dart'; import 'package:kifi_app/features/inventory/providers/commodity_rates_provider.dart'; import 'package:kifi_app/core/network/dio_client.dart'; import 'package:intl/intl.dart'; import 'package:lucide_icons/lucide_icons.dart'; import 'package:kifi_app/features/vendor/presentation/purchase_order_details_screen.dart'; import 'package:kifi_app/features/vendor/providers/purchase_orders_provider.dart'; import 'package:kifi_app/core/utils/purity_utils.dart'; class StockLedgerTab extends ConsumerStatefulWidget { final Product product; const StockLedgerTab({super.key, required this.product}); @override ConsumerState createState() => _StockLedgerTabState(); } class _StockLedgerTabState extends ConsumerState { List? _items; bool _isLoading = true; @override void initState() { super.initState(); _fetchLedger(); } Future _fetchLedger() async { try { final items = await ref .read(productsProvider.notifier) .fetchInventoryItems(widget.product.id!); if (mounted) { setState(() { _items = items; _isLoading = false; }); } } catch (e) { if (mounted) { setState(() { _isLoading = false; }); ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text(e.toString()))); } } } @override Widget build(BuildContext context) { if (_isLoading) { return const Center(child: CircularProgressIndicator()); } if (_items == null || _items!.isEmpty) { return const Center(child: Text("No stock available.")); } final categoriesState = ref.watch(productCategoriesProvider); final commodityRatesState = ref.watch(commodityRatesProvider); final category = categoriesState.value?.where( (c) => c.id == widget.product.categoryId, ).firstOrNull; final String unit = category?.baseUnit ?? 'g'; final double purityFactor = resolvePurity(categoryPurity: category?.purityFactor, productPurity: widget.product.purityFactor); double commodityRate = 0.0; if (category?.commodityCode != null && commodityRatesState.value != null) { final match = commodityRatesState.value!.where( (r) => r.commodityCode.toUpperCase() == category!.commodityCode!.toUpperCase() ).firstOrNull; if (match != null) { commodityRate = match.rate; } } final double currentRate = commodityRate > 0 ? (commodityRate * purityFactor) : (category?.dailyRate ?? 0.0); return RefreshIndicator( onRefresh: _fetchLedger, child: ListView.builder( padding: const EdgeInsets.all(16.0), itemCount: _items!.length, itemBuilder: (context, index) { final item = _items![index]; final weight = item.grossWeight ?? 0.0; final purchaseRate = item.purchaseCost ?? 0.0; final purchasePrice = weight * purchaseRate; final currentPrice = weight * currentRate; final gainLoss = currentPrice - purchasePrice; final isGain = gainLoss >= 0; return Card( elevation: 0, margin: const EdgeInsets.only(bottom: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: BorderSide(color: Colors.grey.withValues(alpha: 0.3)), ), child: Padding( padding: const EdgeInsets.all(12.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: 40, height: 40, decoration: BoxDecoration( color: Colors.grey.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), ), child: FutureBuilder( future: DioClient().storage.read(key: 'jwt_token'), builder: (context, snapshot) { if (snapshot.hasData && widget.product.imageIds.isNotEmpty) { return ClipRRect( borderRadius: BorderRadius.circular(8), child: Image.network( '${DioClient().dio.options.baseUrl}/inventory/products/${widget.product.id}/images/${widget.product.imageIds.first}/content', fit: BoxFit.cover, headers: {'Authorization': 'Bearer ${snapshot.data}'}, ), ); } return const Icon(LucideIcons.image, color: Colors.grey, size: 20); }, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.createdAt != null ? DateFormat('dd MMM yyyy').format(item.createdAt!) : '', style: TextStyle(color: Colors.grey[600], fontSize: 12), ), const SizedBox(height: 4), if (item.purchaseRef != null) InkWell( onTap: () async { try { var pos = ref.read(purchaseOrdersProvider).value; if (pos == null || pos.isEmpty) { pos = await ref.read(purchaseOrdersProvider.future); } final po = pos!.firstWhere( (p) => p.poNumber == item.purchaseRef, orElse: () => throw Exception('Purchase invoice not found'), ); if (mounted) { Navigator.push( context, MaterialPageRoute( builder: (context) => PurchaseOrderDetailsScreen(po: po), ), ); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Could not open purchase invoice: $e')), ); } } }, child: Text( 'INV: ${item.purchaseRef}', style: const TextStyle( color: Colors.blue, decoration: TextDecoration.underline, fontWeight: FontWeight.w600, ), ), ), if (item.huid != null && item.huid!.isNotEmpty) Padding( padding: const EdgeInsets.only(top: 4.0), child: Text('HUID: ${item.huid}', style: const TextStyle(fontWeight: FontWeight.w500)), ), ], ), ), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: Colors.green.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(4), ), child: const Text( 'IN STOCK', style: TextStyle( fontSize: 10, fontWeight: FontWeight.bold, color: Colors.green, ), ), ), const SizedBox(height: 4), Text( '${weight.toStringAsFixed(3)} $unit', style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 14, ), ), const SizedBox(height: 3), Container( padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 1.5), decoration: BoxDecoration( color: Colors.amber.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(4), border: Border.all(color: Colors.amber.withValues(alpha: 0.3)), ), child: Text( 'Purity: ${formatPurity(double.tryParse(item.purity ?? '') ?? purityFactor)}', style: TextStyle( fontSize: 10, fontWeight: FontWeight.bold, color: Colors.amber.shade900, ), ), ), ], ), ], ), const Divider(height: 24), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Purchase Rate', style: TextStyle(color: Colors.grey[500], fontSize: 12)), Text('₹${purchaseRate.toStringAsFixed(2)}/$unit', style: const TextStyle(fontWeight: FontWeight.w600)), ], ), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text('Purchase Amt', style: TextStyle(color: Colors.grey[500], fontSize: 12)), Text('₹${purchasePrice.toStringAsFixed(2)}', style: const TextStyle(fontWeight: FontWeight.w600)), ], ), ], ), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Current Rate', style: TextStyle(color: Colors.grey[500], fontSize: 12)), Text('₹${currentRate.toStringAsFixed(2)}/$unit', style: const TextStyle(fontWeight: FontWeight.w600)), ], ), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text('Current Amt', style: TextStyle(color: Colors.grey[500], fontSize: 12)), Text('₹${currentPrice.toStringAsFixed(2)}', style: const TextStyle(fontWeight: FontWeight.w600)), ], ), ], ), const SizedBox(height: 16), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: isGain ? Colors.green.withValues(alpha: 0.1) : Colors.red.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( isGain ? LucideIcons.trendingUp : LucideIcons.trendingDown, color: isGain ? Colors.green : Colors.red, size: 16, ), const SizedBox(width: 8), Text( '${isGain ? 'Gain' : 'Loss'}: ₹${gainLoss.abs().toStringAsFixed(2)}', style: TextStyle( color: isGain ? Colors.green : Colors.red, fontWeight: FontWeight.bold, fontSize: 14, ), ), ], ), ), ], ), ), ); }, ), ); } }