import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:drift/drift.dart'; import 'package:path_provider/path_provider.dart'; import 'package:kooltab2/database/app_database.dart'; import 'package:kooltab2/services/bar_tab_service.dart'; import 'package:kooltab2/models/payment_method.dart'; import 'package:kooltab2/services/default_product_seeder.dart'; import 'package:kooltab2/services/pin_lock_service.dart'; import 'package:kooltab2/services/product_service.dart'; import 'package:kooltab2/services/settings_service.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:uuid/uuid.dart'; class DevMenuViewModel extends ChangeNotifier { final AppDatabase database; final ProductService productService; final BarTabService barTabService; final SettingsService settingsService; final PinLockService pinLockService; bool _isLoading = false; String? _lastAction; bool get isLoading => _isLoading; String? get lastAction => _lastAction; DevMenuViewModel({ required this.database, required this.productService, required this.barTabService, required this.settingsService, required this.pinLockService, }); Future clearOpenTabs() async { _isLoading = true; notifyListeners(); try { await database.transaction(() async { await database.delete(database.tabItems).go(); await database.delete(database.barTabs).go(); }); _lastAction = 'Cleared all open tabs'; } finally { _isLoading = false; notifyListeners(); } } Future clearClosedTabHistory() async { _isLoading = true; notifyListeners(); try { await database.transaction(() async { await database.delete(database.closedTabItems).go(); await database.delete(database.closedTabs).go(); }); _lastAction = 'Cleared closed tab history'; } finally { _isLoading = false; notifyListeners(); } } Future clearAllProducts() async { _isLoading = true; notifyListeners(); try { await database.transaction(() async { await database.delete(database.tabItems).go(); await database.delete(database.products).go(); }); _lastAction = 'Cleared all products'; } finally { _isLoading = false; notifyListeners(); } } Future seedDefaultProducts() async { _isLoading = true; notifyListeners(); try { await DefaultProductSeeder(database: database).seedIfEmpty(); final count = await _getProductCount(); _lastAction = 'Seeded default products ($count total)'; } finally { _isLoading = false; notifyListeners(); } } Future clearAndReseedProducts() async { _isLoading = true; notifyListeners(); try { await database.transaction(() async { await database.delete(database.tabItems).go(); await database.delete(database.products).go(); }); await DefaultProductSeeder(database: database).seedIfEmpty(); final count = await _getProductCount(); _lastAction = 'Cleared and reseeded ($count products)'; } finally { _isLoading = false; notifyListeners(); } } Future _getProductCount() async { final countExpr = database.products.id.count(); final query = database.selectOnly(database.products) ..addColumns([countExpr]); final row = await query.getSingle(); return row.read(countExpr) ?? 0; } Future clearProductImages() async { _isLoading = true; notifyListeners(); try { final appDir = await getApplicationSupportDirectory(); final imagesDir = Directory('${appDir.path}/product_images'); if (await imagesDir.exists()) { await imagesDir.delete(recursive: true); } _lastAction = 'Product images cleared'; } catch (e, stack) { debugPrint('DevMenuViewModel: clearProductImages error: $e'); Sentry.captureException(e, stackTrace: stack); _lastAction = 'Failed to clear images: $e'; } finally { _isLoading = false; notifyListeners(); } } Future resetAllStock() async { _isLoading = true; notifyListeners(); try { final products = await productService.getProducts(); for (final product in products) { await productService.updateProduct( product.copyWith(stockQuantity: 100), ); } _lastAction = 'Reset stock for ${products.length} products'; } finally { _isLoading = false; notifyListeners(); } } Future seedDemoData() async { _isLoading = true; notifyListeners(); try { final uuid = const Uuid(); final categories = ['Beer', 'Wine', 'Soft Drinks', 'Snacks']; final demoProducts = [ ('Heineken', 350, 50), ('Stella Artois', 380, 40), ('Leffe Blonde', 420, 30), ('Duvel', 450, 25), ('Hoegaarden', 380, 35), ('Chimay Blue', 550, 20), ('White Wine', 450, 60), ('Red Wine', 450, 55), ('Rosé', 400, 45), ('Champagne', 850, 15), ('Cola', 250, 100), ('Lemonade', 250, 90), ('Sparkling Water', 200, 80), ('Orange Juice', 300, 70), ('Chips', 250, 40), ('Nuts', 300, 35), ('Chocolate Bar', 200, 50), ('Ice Cream', 350, 25), ]; for (var i = 0; i < demoProducts.length; i++) { final (name, priceInCents, stock) = demoProducts[i]; final category = categories[i ~/ 5]; await database.into(database.products).insert( ProductsCompanion.insert( id: uuid.v4(), name: name, category: category, stockQuantity: stock, lowStockThreshold: 10, priceInCents: priceInCents, ), ); } _lastAction = 'Seeded ${demoProducts.length} demo products'; } finally { _isLoading = false; notifyListeners(); } } Future resetPin() async { _isLoading = true; notifyListeners(); try { await pinLockService.clearPin(); _lastAction = 'PIN reset (no PIN required)'; } finally { _isLoading = false; notifyListeners(); } } Future createTestTab() async { _isLoading = true; notifyListeners(); try { final products = await productService.getProducts(); if (products.isEmpty) { _lastAction = 'No products to add to test tab'; return; } final tab = await barTabService.createTab(customerName: 'Test Customer'); final randomProducts = products.take(3).toList(); for (final product in randomProducts) { await barTabService.addProductToTab( tabId: tab.id, product: product, ); } _lastAction = 'Created test tab with ${randomProducts.length} items'; } finally { _isLoading = false; notifyListeners(); } } Future addTestProducts({int count = 100}) async { _isLoading = true; notifyListeners(); try { final uuid = const Uuid(); for (var i = 0; i < count; i++) { await database.into(database.products).insert( ProductsCompanion.insert( id: uuid.v4(), name: 'Test Product $i', category: 'Test Category ${i % 5}', stockQuantity: 50 + (i % 50), lowStockThreshold: 5, priceInCents: 100 + (i * 10), ), ); } _lastAction = 'Added $count test products'; } finally { _isLoading = false; notifyListeners(); } } Future simulateLowStock() async { _isLoading = true; notifyListeners(); try { final products = await productService.getProducts(); for (final product in products) { await productService.updateProduct( product.copyWith(stockQuantity: 3, lowStockThreshold: 10), ); } _lastAction = 'Simulated low stock for ${products.length} products'; } finally { _isLoading = false; notifyListeners(); } } Future clearImageCache() async { _isLoading = true; notifyListeners(); try { _lastAction = 'Image cache cleared (no-op in debug mode)'; } finally { _isLoading = false; notifyListeners(); } } Future reloadProductImages() async { _isLoading = true; notifyListeners(); try { _lastAction = 'Product images reloaded'; } finally { _isLoading = false; notifyListeners(); } try { throw Exception('Test GlitchTip error!'); } catch (exception, stackTrace) { Sentry.captureException(exception, stackTrace: stackTrace); } } Future generateMockOrders({int count = 100}) async { _isLoading = true; notifyListeners(); try { final products = await productService.getProducts(); if (products.isEmpty) { _lastAction = 'No products available — seed demo data first'; return; } final uuid = const Uuid(); final customerNames = [ 'Alice', 'Bob', 'Charlie', 'Diana', 'Eve', 'Frank', 'Grace', 'Hank', 'Ivy', 'Jack', 'Kate', 'Leo', 'Mia', 'Noah', 'Olivia', 'Pete', 'Quinn', 'Rose', 'Sam', 'Tina', 'Umar', 'Vera', 'Wes', 'Xena', 'Yves', 'Zara', ]; const paymentMethods = [PaymentMethod.cash, PaymentMethod.payconiq]; await database.transaction(() async { for (var i = 0; i < count; i++) { final customer = customerNames[i % customerNames.length]; final closedTabId = uuid.v4(); final itemCount = 1 + (i % 5); final closedAt = DateTime.now().subtract( Duration( days: i % 90, hours: i % 24, minutes: i % 60, ), ); await database.into(database.closedTabs).insert( ClosedTabsCompanion.insert( id: closedTabId, originalTabId: uuid.v4(), customerName: customer, closedAt: closedAt, paymentMethod: Value(paymentMethods[(i * 3) % paymentMethods.length].value), ), ); for (var j = 0; j < itemCount; j++) { final product = products[(i + j) % products.length]; final quantity = 1 + ((i * 7 + j * 13) % 6); await database.into(database.closedTabItems).insert( ClosedTabItemsCompanion.insert( id: uuid.v4(), closedTabId: closedTabId, productId: product.id, productName: product.name, quantity: quantity, unitPriceInCents: product.priceInCents, ), ); } } }); _lastAction = 'Generated $count mock orders across ' '${customerNames.length} customers'; } finally { _isLoading = false; notifyListeners(); } } }