// SPDX-FileCopyrightText: © 2025 Nøkken.io <nokken.io@proton.me>
// SPDX-License-Identifier: AGPL-3.0
//
//  database_service_mood.dart
//  Extensions to DatabaseService for mood entries
//
import 'package:nokken/src/core/services/database/database_service.dart';
import 'package:nokken/src/features/mood_tracker/models/mood_entry.dart';
import 'package:sqflite/sqflite.dart';

// At the top of the file
class MoodDatabaseException implements Exception {
  final String message;
  final dynamic error;
  MoodDatabaseException(this.message, [this.error]);

  @override
  String toString() =>
      'MoodDatabaseException: $message${error != null ? ' ($error)' : ''}';
}

/// Extension to add mood entry functionality to DatabaseService
extension MoodEntryDatabase on DatabaseService {
  /// Insert a new mood entry into the database
  Future<void> insertMoodEntry(MoodEntry entry) async {
    try {
      final db = await database;
      await db.insert(
        'mood_entries',
        _moodEntryToMap(entry),
        conflictAlgorithm: ConflictAlgorithm.replace,
      );
    } catch (e) {
      throw MoodDatabaseException('Failed to insert mood entry', e);
    }
  }

  /// Update an existing mood entry in the database
  Future<void> updateMoodEntry(MoodEntry entry) async {
    try {
      final db = await database;
      await db.update(
        'mood_entries',
        _moodEntryToMap(entry),
        where: 'id = ?',
        whereArgs: [entry.id],
      );
    } catch (e) {
      throw MoodDatabaseException('Failed to update mood entry', e);
    }
  }

  /// Delete a mood entry
  Future<void> deleteMoodEntry(String id) async {
    try {
      final db = await database;
      await db.delete(
        'mood_entries',
        where: 'id = ?',
        whereArgs: [id],
      );
    } catch (e) {
      throw MoodDatabaseException('Failed to delete mood entry: $e');
    }
  }

  /// Get all mood entries from the database
  Future<List<MoodEntry>> getAllMoodEntries() async {
    try {
      final db = await database;
      final List<Map<String, dynamic>> maps = await db.query('mood_entries');
      return maps.map(_mapToMoodEntry).toList();
    } catch (e) {
      throw MoodDatabaseException('Failed to fetch mood entries: $e');
    }
  }

  /// Get a mood entry for a specific date
  Future<MoodEntry?> getMoodEntryForDate(DateTime date) async {
    try {
      final db = await database;
      final normalizedDate =
          DateTime(date.year, date.month, date.day).toIso8601String();

      final List<Map<String, dynamic>> maps = await db.query(
        'mood_entries',
        where: 'date = ?',
        whereArgs: [normalizedDate],
      );

      if (maps.isEmpty) {
        return null;
      }

      return _mapToMoodEntry(maps.first);
    } catch (e) {
      throw MoodDatabaseException('Failed to fetch mood entry for date: $e');
    }
  }

  /// Convert a MoodEntry object to a database map
  Map<String, dynamic> _moodEntryToMap(MoodEntry entry) {
    // Normalize the date to just year-month-day
    final normalizedDate =
        DateTime(entry.date.year, entry.date.month, entry.date.day)
            .toIso8601String();

    final map = {
      'id': entry.id,
      'date': normalizedDate,
      'mood': entry.mood.toString(),
      'emotions': entry.emotions.map((e) => e.toString()).join(','),
      'notes': entry.notes,
      'sleep_quality': entry.sleepQuality?.toString(),
      'energy_level': entry.energyLevel?.toString(),
      'libido_level': entry.libidoLevel?.toString(),
      'appetite_level': entry.appetiteLevel?.toString(),
      'focus_level': entry.focusLevel?.toString(),
      'dysphoria_level': entry.dysphoriaLevel?.toString(),
      'exercise_level': entry.exerciseLevel?.toString(),
    };

    return map;
  }

  /// Convert a database map to a MoodEntry object
  MoodEntry _mapToMoodEntry(Map<String, dynamic> map) {
    // Parse the mood
    final moodStr = map['mood'] as String;
    final mood = MoodRating.values.firstWhere(
      (e) => e.toString() == moodStr,
      orElse: () => MoodRating.okay,
    );

    // Parse the emotions
    final emotionsStr = map['emotions'] as String;
    final emotions = emotionsStr
        .split(',')
        .where((e) => e.isNotEmpty)
        .map((emotionStr) {
          try {
            return Emotion.values.firstWhere(
              (e) => e.toString() == emotionStr,
            );
          } catch (_) {
            return null;
          }
        })
        .where((e) => e != null)
        .cast<Emotion>()
        .toSet();

    // Parse optional health categories without defaults
    SleepQuality? sleepQuality;
    if (map['sleep_quality'] != null) {
      try {
        sleepQuality = SleepQuality.values.firstWhere(
          (e) => e.toString() == map['sleep_quality'],
        );
      } catch (_) {
        // Keep as null if no match found
      }
    }

    EnergyLevel? energyLevel;
    if (map['energy_level'] != null) {
      try {
        energyLevel = EnergyLevel.values.firstWhere(
          (e) => e.toString() == map['energy_level'],
        );
      } catch (_) {
        // Keep as null if no match found
      }
    }

    LibidoLevel? libidoLevel;
    if (map['libido_level'] != null) {
      try {
        libidoLevel = LibidoLevel.values.firstWhere(
          (e) => e.toString() == map['libido_level'],
        );
      } catch (_) {
        // Keep as null if no match found
      }
    }

    AppetiteLevel? appetiteLevel;
    if (map['appetite_level'] != null) {
      try {
        appetiteLevel = AppetiteLevel.values.firstWhere(
          (e) => e.toString() == map['appetite_level'],
        );
      } catch (_) {
        // Keep as null if no match found
      }
    }

    FocusLevel? focusLevel;
    if (map['focus_level'] != null) {
      try {
        focusLevel = FocusLevel.values.firstWhere(
          (e) => e.toString() == map['focus_level'],
        );
      } catch (_) {
        // Keep as null if no match found
      }
    }

    DysphoriaLevel? dysphoriaLevel;
    if (map['dysphoria_level'] != null) {
      try {
        dysphoriaLevel = DysphoriaLevel.values.firstWhere(
          (e) => e.toString() == map['dysphoria_level'],
        );
      } catch (_) {
        // Keep as null if no match found
      }
    }

    ExerciseLevel? exerciseLevel;
    if (map['exercise_level'] != null) {
      try {
        exerciseLevel = ExerciseLevel.values.firstWhere(
          (e) => e.toString() == map['exercise_level'],
        );
      } catch (_) {
        // Keep as null if no match found
      }
    }

    return MoodEntry(
      id: map['id'] as String,
      date: DateTime.parse(map['date'] as String),
      mood: mood,
      emotions: emotions,
      notes: map['notes'] as String?,
      sleepQuality: sleepQuality,
      energyLevel: energyLevel,
      libidoLevel: libidoLevel,
      appetiteLevel: appetiteLevel,
      focusLevel: focusLevel,
      dysphoriaLevel: dysphoriaLevel,
      exerciseLevel: exerciseLevel,
    );
  }
}
