import 'dart:convert'; import 'package:drift/drift.dart' show Value; import 'package:uto_manager/domain/entities/authorized_rank.dart'; import 'package:uto_manager/domain/entities/personnel_record.dart' as domain; import 'package:uto_manager/domain/repositories/personnel_repository.dart'; import 'package:uto_manager/domain/value_objects/duty_mos.dart'; import 'package:uto_manager/infrastructure/database/app_database.dart'; import 'package:uto_manager/infrastructure/database/daos/personnel_dao.dart'; /// Drift-backed implementation of [PersonnelRepository]. /// /// Converts between Drift [PersonnelData] rows and domain /// [domain.PersonnelRecord] entities. All persistence is handled via /// [PersonnelDao]. class DriftPersonnelRepository implements PersonnelRepository { const DriftPersonnelRepository({required this.dao}); final PersonnelDao dao; // --------------------------------------------------------------------------- // Mapping helpers // --------------------------------------------------------------------------- domain.PersonnelRecord _toDomain(PersonnelData row) { return domain.PersonnelRecord( soldierId: row.soldierId, name: row.name, primaryMos: DutyMos.unvalidated(row.primaryMos), payGrade: AuthorizedRank.values.firstWhere((r) => r.name == row.payGrade), qualifications: (jsonDecode(row.qualifications) as List).cast(), readinessState: domain.ReadinessState.values.firstWhere( (s) => s.name == row.readinessState, ), assignedSlotId: row.assignedSlotId, ); } PersonnelCompanion _toCompanion(domain.PersonnelRecord record) { return PersonnelCompanion.insert( soldierId: record.soldierId, name: record.name, primaryMos: record.primaryMos.code, payGrade: record.payGrade.name, qualifications: jsonEncode(record.qualifications), readinessState: record.readinessState.name, assignedSlotId: Value(record.assignedSlotId), ); } // --------------------------------------------------------------------------- // PersonnelRepository implementation // --------------------------------------------------------------------------- @override Future> getAll() async { final rows = await dao.getAllPersonnel(); return rows.map(_toDomain).toList(); } @override Future getById(String soldierId) async { final row = await dao.getPersonnelById(soldierId); return row == null ? null : _toDomain(row); } @override Future> getBySlot(String slotId) async { final rows = await dao.getBySlot(slotId); return rows.map(_toDomain).toList(); } @override Future> getUnassigned() async { final rows = await dao.getUnassigned(); return rows.map(_toDomain).toList(); } @override Future> getByMos(DutyMos primaryMos) async { final rows = await dao.getByMos(primaryMos.code); return rows.map(_toDomain).toList(); } @override Future> getByPayGrade(AuthorizedRank payGrade) async { final rows = await dao.getByPayGrade(payGrade.name); return rows.map(_toDomain).toList(); } @override Future insert(domain.PersonnelRecord record) async { final existing = await dao.getPersonnelById(record.soldierId); if (existing != null) { throw StateError('Personnel ${record.soldierId} already exists.'); } await dao.insertPersonnel(_toCompanion(record)); } @override Future insertAll(List records) async { await dao.insertAllPersonnel(records.map(_toCompanion).toList()); } @override Future update(domain.PersonnelRecord record) async { final existing = await dao.getPersonnelById(record.soldierId); if (existing == null) { throw StateError('Personnel ${record.soldierId} not found.'); } await dao.updatePersonnel(_toCompanion(record)); } @override Future delete(String soldierId) async { await dao.deletePersonnel(soldierId); } @override Future assignToSlot(String soldierId, String slotId) async { await dao.assignToSlot(soldierId, slotId); } @override Future unassignFromSlot(String soldierId) async { await dao.unassignFromSlot(soldierId); } @override Stream> watchAll() { return dao.watchAllPersonnel().map((rows) => rows.map(_toDomain).toList()); } @override Stream> watchUnassigned() { return dao.watchUnassigned().map((rows) => rows.map(_toDomain).toList()); } @override Future deleteAll() async { await dao.deleteAllPersonnel(); } }