import 'package:sqflite/sqflite.dart'; import 'package:path/path.dart'; import '../models/servicio.dart'; import '../models/factura.dart'; import '../models/configuracion.dart'; class DatabaseHelper { static final DatabaseHelper instance = DatabaseHelper._init(); static Database? _database; DatabaseHelper._init(); Future get database async { if (_database != null) return _database!; _database = await _initDB('hogarcontrol.db'); return _database!; } Future _initDB(String filePath) async { final dbPath = await getDatabasesPath(); final path = join(dbPath, filePath); return await openDatabase(path, version: 3, onCreate: _createDB, onUpgrade: _upgradeDB, onConfigure: _onConfigure); } Future _onConfigure(Database db) async { await db.execute('PRAGMA foreign_keys = ON'); } Future _upgradeDB(Database db, int oldVersion, int newVersion) async { if (oldVersion < 3) { try { await db.execute( "ALTER TABLE servicios ADD COLUMN es_recurrente INTEGER NOT NULL DEFAULT 0"); } catch (_) {} } } Future _createDB(Database db, int version) async { await db.execute(''' CREATE TABLE servicios ( id INTEGER PRIMARY KEY AUTOINCREMENT, nombre TEXT NOT NULL, icono TEXT NOT NULL, color INTEGER NOT NULL, dia_vencimiento INTEGER NOT NULL, fecha_creacion TEXT NOT NULL, es_recurrente INTEGER NOT NULL DEFAULT 0 ) '''); await db.execute(''' CREATE TABLE facturas ( id INTEGER PRIMARY KEY AUTOINCREMENT, servicio_id INTEGER NOT NULL, monto REAL NOT NULL, fecha_emision TEXT NOT NULL, fecha_vencimiento TEXT NOT NULL, estado TEXT NOT NULL DEFAULT 'Pendiente', nota TEXT, fecha_pago TEXT, FOREIGN KEY (servicio_id) REFERENCES servicios(id) ON DELETE CASCADE ) '''); await db.execute(''' CREATE TABLE configuracion ( id INTEGER PRIMARY KEY AUTOINCREMENT, dias_recordatorio TEXT NOT NULL DEFAULT '7,3,1,0', tema TEXT NOT NULL DEFAULT 'claro', moneda TEXT NOT NULL DEFAULT 'USD' ) '''); } Future insertServicio(Servicio servicio) async { final db = await database; return await db.insert('servicios', servicio.toMap()..remove('id')); } Future> getServicios() async { final db = await database; final maps = await db.query('servicios', orderBy: 'nombre ASC'); return maps.map((map) => Servicio.fromMap(map)).toList(); } Future updateServicio(Servicio servicio) async { final db = await database; return await db.update('servicios', servicio.toMap(), where: 'id = ?', whereArgs: [servicio.id]); } Future deleteServicio(int id) async { final db = await database; return await db.delete('servicios', where: 'id = ?', whereArgs: [id]); } Future getServicio(int id) async { final db = await database; final maps = await db.query('servicios', where: 'id = ?', whereArgs: [id], limit: 1); if (maps.isEmpty) return null; return Servicio.fromMap(maps.first); } Future insertFactura(Factura factura) async { final db = await database; return await db.insert('facturas', factura.toMap()..remove('id')); } Future> getFacturas({String? estado, int? servicioId}) async { final db = await database; String? where; List? whereArgs; if (estado != null && servicioId != null) { where = 'estado = ? AND servicio_id = ?'; whereArgs = [estado, servicioId]; } else if (estado != null) { where = 'estado = ?'; whereArgs = [estado]; } else if (servicioId != null) { where = 'servicio_id = ?'; whereArgs = [servicioId]; } final maps = await db.query('facturas', where: where, whereArgs: whereArgs, orderBy: 'fecha_vencimiento DESC'); return maps.map((map) => Factura.fromMap(map)).toList(); } Future> getFacturasVencidas() async { final db = await database; final hoy = DateTime.now().toIso8601String().split('T')[0]; final maps = await db.query('facturas', where: 'estado = ? AND fecha_vencimiento < ?', whereArgs: ['Pendiente', hoy], orderBy: 'fecha_vencimiento ASC'); return maps.map((map) => Factura.fromMap(map)).toList(); } Future> getFacturasPorMes(int year, int month) async { final db = await database; final mes = month.toString().padLeft(2, '0'); final maps = await db.query('facturas', where: "fecha_vencimiento LIKE ?", whereArgs: ['$year-$mes%'], orderBy: 'fecha_vencimiento ASC'); return maps.map((map) => Factura.fromMap(map)).toList(); } Future> getFacturasPorRango(String inicio, String fin) async { final db = await database; final maps = await db.query('facturas', where: 'fecha_vencimiento BETWEEN ? AND ?', whereArgs: [inicio, fin], orderBy: 'fecha_vencimiento ASC'); return maps.map((map) => Factura.fromMap(map)).toList(); } Future getTotalGastadoMes(int year, int month) async { final db = await database; final mes = month.toString().padLeft(2, '0'); final result = await db.rawQuery( "SELECT COALESCE(SUM(monto), 0) as total FROM facturas WHERE estado = 'Pagada' AND fecha_pago LIKE ?", ['$year-$mes%']); return (result.first['total'] as num).toDouble(); } Future> getGastosPorServicio(int year, int month) async { final db = await database; final mes = month.toString().padLeft(2, '0'); final result = await db.rawQuery(''' SELECT s.nombre, COALESCE(SUM(f.monto), 0) as total FROM facturas f JOIN servicios s ON f.servicio_id = s.id WHERE f.estado = 'Pagada' AND f.fecha_pago LIKE ? GROUP BY s.nombre ''', ['$year-$mes%']); final map = {}; for (final row in result) { map[row['nombre'] as String] = (row['total'] as num).toDouble(); } return map; } Future>> getGastoMensual(int year) async { final db = await database; return await db.rawQuery(''' SELECT strftime('%m', fecha_pago) as mes, COALESCE(SUM(monto), 0) as total FROM facturas WHERE estado = 'Pagada' AND fecha_pago LIKE ? GROUP BY mes ORDER BY mes ''', ['$year-%']); } Future updateFactura(Factura factura) async { final db = await database; return await db.update('facturas', factura.toMap(), where: 'id = ?', whereArgs: [factura.id]); } Future deleteFactura(int id) async { final db = await database; return await db.delete('facturas', where: 'id = ?', whereArgs: [id]); } Future getConfiguracion() async { final db = await database; final maps = await db.query('configuracion', limit: 1); if (maps.isEmpty) return null; return Configuracion.fromMap(maps.first); } Future saveConfiguracion(Configuracion config) async { final db = await database; final existing = await getConfiguracion(); if (existing != null) { return await db.update('configuracion', config.toMap()..remove('id'), where: 'id = ?', whereArgs: [existing.id]); } return await db.insert('configuracion', config.toMap()..remove('id')); } }