// Test file for database service functionality
import 'package:flutter_test/flutter_test.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:health_tracker/services/database_service.dart';
import 'package:health_tracker/models/weight_entry.dart';
import 'package:health_tracker/models/blood_pressure_entry.dart';
import 'package:health_tracker/models/mood_entry.dart';
import 'package:health_tracker/models/mood_type.dart';

void main() {
  // Initialize FFI for testing on desktop
  setUpAll(() {
    sqfliteFfiInit();
    databaseFactory = databaseFactoryFfi;
  });

  late DatabaseService dbService;

  setUp(() async {
    dbService = DatabaseService.instance;
    // Clear any existing data for clean tests
    await dbService.clearAllData();
  });

  tearDown(() async {
    await dbService.clearAllData();
  });

  group('Database Initialization', () {
    test('should create database tables successfully', () async {
      // The database is automatically created when we access it
      final db = await dbService.database;
      expect(db.isOpen, isTrue);

      // Verify tables exist by querying them
      final weightResult = await db.rawQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='weight_entries'");
      expect(weightResult.length, equals(1));

      final bpResult = await db.rawQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='blood_pressure_entries'");
      expect(bpResult.length, equals(1));

      final moodResult = await db.rawQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='mood_entries'");
      expect(moodResult.length, equals(1));
    });

    test('should create database indices', () async {
      final db = await dbService.database;
      
      // Check that indices exist
      final indices = await db.rawQuery("SELECT name FROM sqlite_master WHERE type='index'");
      final indexNames = indices.map((row) => row['name'] as String).toList();
      
      expect(indexNames, contains('idx_weight_timestamp'));
      expect(indexNames, contains('idx_bp_timestamp'));
      expect(indexNames, contains('idx_mood_timestamp'));
    });
  });

  group('Weight Entry CRUD Operations', () {
    test('should insert and retrieve weight entry', () async {
      final entry = WeightEntry.fromUserInput(
        weightValue: 70.5,
        unit: 'kg',
        timestamp: DateTime.now(),
        notes: 'Test weight entry',
      );

      // Insert entry
      final id = await dbService.insertWeightEntry(entry);
      expect(id, greaterThan(0));

      // Retrieve entry
      final retrieved = await dbService.getWeightEntry(id);
      expect(retrieved, isNotNull);
      expect(retrieved!.weightKg, equals(70.5));
      expect(retrieved.originalUnit, equals('kg'));
      expect(retrieved.notes, equals('Test weight entry'));
    });

    test('should update weight entry', () async {
      final entry = WeightEntry.fromUserInput(
        weightValue: 70.0,
        unit: 'kg',
        timestamp: DateTime.now(),
      );

      final id = await dbService.insertWeightEntry(entry);
      final inserted = await dbService.getWeightEntry(id);

      // Update the entry
      final updated = inserted!.copyWith(
        weightKg: 71.5,
        notes: 'Updated notes',
      );

      await dbService.updateWeightEntry(updated);
      final retrieved = await dbService.getWeightEntry(id);

      expect(retrieved!.weightKg, equals(71.5));
      expect(retrieved.notes, equals('Updated notes'));
    });

    test('should delete weight entry', () async {
      final entry = WeightEntry.fromUserInput(
        weightValue: 70.0,
        unit: 'kg',
        timestamp: DateTime.now(),
      );

      final id = await dbService.insertWeightEntry(entry);
      expect(await dbService.getWeightEntry(id), isNotNull);

      await dbService.deleteWeightEntry(id);
      expect(await dbService.getWeightEntry(id), isNull);
    });

    test('should get weight entries with date filtering', () async {
      final now = DateTime.now();
      final yesterday = now.subtract(const Duration(days: 1));

      // Insert entries with different dates
      await dbService.insertWeightEntry(WeightEntry.fromUserInput(
        weightValue: 70.0,
        unit: 'kg',
        timestamp: yesterday,
      ));

      await dbService.insertWeightEntry(WeightEntry.fromUserInput(
        weightValue: 71.0,
        unit: 'kg',
        timestamp: now,
      ));

      // Get entries from today onwards
      final entries = await dbService.getWeightEntries(startDate: now);
      expect(entries.length, equals(1));
      expect(entries.first.weightKg, equals(71.0));
    });
  });

  group('Blood Pressure Entry CRUD Operations', () {
    test('should insert and retrieve blood pressure entry', () async {
      final entry = BloodPressureEntry(
        timestamp: DateTime.now(),
        systolic: 120,
        diastolic: 80,
        notes: 'Normal reading',
      );

      final id = await dbService.insertBloodPressureEntry(entry);
      expect(id, greaterThan(0));

      final retrieved = await dbService.getBloodPressureEntry(id);
      expect(retrieved, isNotNull);
      expect(retrieved!.systolic, equals(120));
      expect(retrieved.diastolic, equals(80));
      expect(retrieved.notes, equals('Normal reading'));
    });

    test('should validate blood pressure categories', () async {
      final entry = BloodPressureEntry(
        timestamp: DateTime.now(),
        systolic: 140,
        diastolic: 90,
      );

      final id = await dbService.insertBloodPressureEntry(entry);
      final retrieved = await dbService.getBloodPressureEntry(id);

      expect(retrieved!.category, equals('high_stage_2'));
      expect(retrieved.isHigh, isTrue);
      expect(retrieved.isEmergency, isFalse);
    });
  });

  group('Mood Entry CRUD Operations', () {
    test('should insert and retrieve mood entry', () async {
      final entry = MoodEntry(
        timestamp: DateTime.now(),
        mood: MoodType.energized,
        notes: 'Feeling great!',
      );

      final id = await dbService.insertMoodEntry(entry);
      expect(id, greaterThan(0));

      final retrieved = await dbService.getMoodEntry(id);
      expect(retrieved, isNotNull);
      expect(retrieved!.mood, equals(MoodType.energized));
      expect(retrieved.notes, equals('Feeling great!'));
      expect(retrieved.emoji, equals('⚡'));
    });

    test('should handle mood analytics', () async {
      final entry = MoodEntry(
        timestamp: DateTime.now(),
        mood: MoodType.calm,
      );

      final id = await dbService.insertMoodEntry(entry);
      final retrieved = await dbService.getMoodEntry(id);

      expect(retrieved!.isPositiveMood, isTrue);
      expect(retrieved.isNegativeMood, isFalse);
      expect(retrieved.energyLevel, equals(3));
      expect(retrieved.valence, equals(4));
    });
  });

  group('Aggregation Operations', () {
    test('should get recent entries across all types', () async {
      // Insert sample data
      await dbService.insertWeightEntry(WeightEntry.fromUserInput(
        weightValue: 70.0,
        unit: 'kg',
        timestamp: DateTime.now(),
      ));

      await dbService.insertBloodPressureEntry(BloodPressureEntry(
        timestamp: DateTime.now(),
        systolic: 120,
        diastolic: 80,
      ));

      await dbService.insertMoodEntry(MoodEntry(
        timestamp: DateTime.now(),
        mood: MoodType.energized,
      ));

      final recent = await dbService.getRecentEntries(limit: 10);
      
      expect(recent['weight']!.length, equals(1));
      expect(recent['bloodPressure']!.length, equals(1));
      expect(recent['mood']!.length, equals(1));
    });

    test('should get entry counts', () async {
      // Insert sample data
      await dbService.insertWeightEntry(WeightEntry.fromUserInput(
        weightValue: 70.0,
        unit: 'kg',
        timestamp: DateTime.now(),
      ));

      await dbService.insertBloodPressureEntry(BloodPressureEntry(
        timestamp: DateTime.now(),
        systolic: 120,
        diastolic: 80,
      ));

      final counts = await dbService.getEntryCounts();
      
      expect(counts['weight'], equals(1));
      expect(counts['bloodPressure'], equals(1));
      expect(counts['mood'], equals(0));
    });
  });

  group('Batch Operations', () {
    test('should insert multiple entries in batch', () async {
      final entries = [
        WeightEntry.fromUserInput(
          weightValue: 70.0,
          unit: 'kg',
          timestamp: DateTime.now(),
        ),
        BloodPressureEntry(
          timestamp: DateTime.now(),
          systolic: 120,
          diastolic: 80,
        ),
        MoodEntry(
          timestamp: DateTime.now(),
          mood: MoodType.energized,
        ),
      ];

      await dbService.batchInsert(entries);

      final counts = await dbService.getEntryCounts();
      expect(counts['weight'], equals(1));
      expect(counts['bloodPressure'], equals(1));
      expect(counts['mood'], equals(1));
    });

    test('should clear all data', () async {
      // Insert some data
      await dbService.insertWeightEntry(WeightEntry.fromUserInput(
        weightValue: 70.0,
        unit: 'kg',
        timestamp: DateTime.now(),
      ));

      // Verify data exists
      final beforeCounts = await dbService.getEntryCounts();
      expect(beforeCounts['weight'], equals(1));

      // Clear all data
      await dbService.clearAllData();

      // Verify data is gone
      final afterCounts = await dbService.getEntryCounts();
      expect(afterCounts['weight'], equals(0));
      expect(afterCounts['bloodPressure'], equals(0));
      expect(afterCounts['mood'], equals(0));
    });
  });
}
