import 'package:sqflite/sqflite.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:path/path.dart'; import 'dart:io'; import '../models/supplement.dart'; import '../models/supplement_intake.dart'; class DatabaseHelper { static const _databaseName = 'supplements.db'; static const _databaseVersion = 2; // Increment version for schema changes static const supplementsTable = 'supplements'; static const intakesTable = 'supplement_intakes'; DatabaseHelper._privateConstructor(); static final DatabaseHelper instance = DatabaseHelper._privateConstructor(); static Database? _database; static bool _initialized = false; static void _initializeDatabaseFactory() { if (!_initialized) { // Initialize for desktop platforms if (Platform.isLinux || Platform.isWindows || Platform.isMacOS) { sqfliteFfiInit(); databaseFactory = databaseFactoryFfi; } _initialized = true; } } Future get database async { _initializeDatabaseFactory(); _database ??= await _initDatabase(); return _database!; } Future _initDatabase() async { String path = join(await getDatabasesPath(), _databaseName); return await openDatabase( path, version: _databaseVersion, onCreate: _onCreate, onUpgrade: _onUpgrade, ); } Future _onCreate(Database db, int version) async { await db.execute(''' CREATE TABLE $supplementsTable ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, dosageAmount REAL NOT NULL, numberOfUnits INTEGER NOT NULL DEFAULT 1, unit TEXT NOT NULL, unitType TEXT NOT NULL DEFAULT 'units', frequencyPerDay INTEGER NOT NULL, reminderTimes TEXT NOT NULL, notes TEXT, createdAt TEXT NOT NULL, isActive INTEGER NOT NULL DEFAULT 1 ) '''); await db.execute(''' CREATE TABLE $intakesTable ( id INTEGER PRIMARY KEY AUTOINCREMENT, supplementId INTEGER NOT NULL, takenAt TEXT NOT NULL, dosageTaken REAL NOT NULL, unitsTaken REAL NOT NULL DEFAULT 1, notes TEXT, FOREIGN KEY (supplementId) REFERENCES $supplementsTable (id) ) '''); } Future _onUpgrade(Database db, int oldVersion, int newVersion) async { if (oldVersion < 2) { // First, add new columns await db.execute('ALTER TABLE $supplementsTable ADD COLUMN dosageAmount REAL DEFAULT 0'); await db.execute('ALTER TABLE $supplementsTable ADD COLUMN numberOfUnits INTEGER DEFAULT 1'); await db.execute('ALTER TABLE $supplementsTable ADD COLUMN unitType TEXT DEFAULT "units"'); await db.execute('ALTER TABLE $intakesTable ADD COLUMN unitsTaken REAL DEFAULT 1'); // Migrate existing data from old dosage column to new dosageAmount column await db.execute(''' UPDATE $supplementsTable SET dosageAmount = COALESCE(dosage, 0), numberOfUnits = 1, unitType = 'units' WHERE dosageAmount = 0 '''); // Create new table with correct schema await db.execute(''' CREATE TABLE ${supplementsTable}_new ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, dosageAmount REAL NOT NULL, numberOfUnits INTEGER NOT NULL DEFAULT 1, unit TEXT NOT NULL, unitType TEXT NOT NULL DEFAULT 'units', frequencyPerDay INTEGER NOT NULL, reminderTimes TEXT NOT NULL, notes TEXT, createdAt TEXT NOT NULL, isActive INTEGER NOT NULL DEFAULT 1 ) '''); // Copy data to new table await db.execute(''' INSERT INTO ${supplementsTable}_new (id, name, dosageAmount, numberOfUnits, unit, unitType, frequencyPerDay, reminderTimes, notes, createdAt, isActive) SELECT id, name, dosageAmount, numberOfUnits, unit, unitType, frequencyPerDay, reminderTimes, notes, createdAt, isActive FROM $supplementsTable '''); // Drop old table and rename new table await db.execute('DROP TABLE $supplementsTable'); await db.execute('ALTER TABLE ${supplementsTable}_new RENAME TO $supplementsTable'); } } // Supplement CRUD operations Future insertSupplement(Supplement supplement) async { Database db = await database; return await db.insert(supplementsTable, supplement.toMap()); } Future> getAllSupplements() async { Database db = await database; List> maps = await db.query( supplementsTable, where: 'isActive = ?', whereArgs: [1], orderBy: 'name ASC', ); return List.generate(maps.length, (i) => Supplement.fromMap(maps[i])); } Future getSupplement(int id) async { Database db = await database; List> maps = await db.query( supplementsTable, where: 'id = ?', whereArgs: [id], ); if (maps.isNotEmpty) { return Supplement.fromMap(maps.first); } return null; } Future updateSupplement(Supplement supplement) async { Database db = await database; return await db.update( supplementsTable, supplement.toMap(), where: 'id = ?', whereArgs: [supplement.id], ); } Future deleteSupplement(int id) async { Database db = await database; return await db.update( supplementsTable, {'isActive': 0}, where: 'id = ?', whereArgs: [id], ); } // Supplement Intake CRUD operations Future insertIntake(SupplementIntake intake) async { Database db = await database; return await db.insert(intakesTable, intake.toMap()); } Future> getIntakesForDate(DateTime date) async { Database db = await database; String startDate = DateTime(date.year, date.month, date.day).toIso8601String(); String endDate = DateTime(date.year, date.month, date.day, 23, 59, 59).toIso8601String(); List> maps = await db.query( intakesTable, where: 'takenAt >= ? AND takenAt <= ?', whereArgs: [startDate, endDate], orderBy: 'takenAt DESC', ); return List.generate(maps.length, (i) => SupplementIntake.fromMap(maps[i])); } Future> getIntakesForMonth(int year, int month) async { Database db = await database; String startDate = DateTime(year, month, 1).toIso8601String(); String endDate = DateTime(year, month + 1, 0, 23, 59, 59).toIso8601String(); List> maps = await db.query( intakesTable, where: 'takenAt >= ? AND takenAt <= ?', whereArgs: [startDate, endDate], orderBy: 'takenAt DESC', ); return List.generate(maps.length, (i) => SupplementIntake.fromMap(maps[i])); } Future>> getIntakesWithSupplementsForDate(DateTime date) async { Database db = await database; String startDate = DateTime(date.year, date.month, date.day).toIso8601String(); String endDate = DateTime(date.year, date.month, date.day, 23, 59, 59).toIso8601String(); List> result = await db.rawQuery(''' SELECT i.*, s.name as supplementName, s.unit as supplementUnit, s.unitType as supplementUnitType FROM $intakesTable i JOIN $supplementsTable s ON i.supplementId = s.id WHERE i.takenAt >= ? AND i.takenAt <= ? ORDER BY i.takenAt DESC ''', [startDate, endDate]); return result; } Future>> getIntakesWithSupplementsForMonth(int year, int month) async { Database db = await database; String startDate = DateTime(year, month, 1).toIso8601String(); String endDate = DateTime(year, month + 1, 0, 23, 59, 59).toIso8601String(); List> result = await db.rawQuery(''' SELECT i.*, s.name as supplementName, s.unit as supplementUnit, s.unitType as supplementUnitType FROM $intakesTable i JOIN $supplementsTable s ON i.supplementId = s.id WHERE i.takenAt >= ? AND i.takenAt <= ? ORDER BY i.takenAt DESC ''', [startDate, endDate]); return result; } Future deleteIntake(int id) async { Database db = await database; return await db.delete( intakesTable, where: 'id = ?', whereArgs: [id], ); } }