// SPDX-FileCopyrightText: © 2025 Nøkken.io <nokken.io@proton.me>
// SPDX-License-Identifier: AGPL-3.0
//
//  mood_state.dart
//  State management for mood entries using Riverpod
//
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:nokken/src/features/mood_tracker/models/mood_entry.dart';
import 'package:nokken/src/core/services/database/database_service.dart';
import 'package:nokken/src/core/services/database/database_service_mood.dart';
import 'package:nokken/src/features/medication_tracker/providers/medication_state.dart';

/// State class to handle loading and error states for mood entry data
class MoodState {
  final List<MoodEntry> entries;
  final bool isLoading;
  final String? error;

  const MoodState({
    this.entries = const [],
    this.isLoading = false,
    this.error,
  });

  /// Create a new state object with updated fields
  MoodState copyWith({
    List<MoodEntry>? entries,
    bool? isLoading,
    String? error,
  }) {
    return MoodState(
      entries: entries ?? this.entries,
      isLoading: isLoading ?? this.isLoading,
      error: error, // Pass null to clear error
    );
  }
}

/// Notifier class to handle mood state changes
class MoodNotifier extends StateNotifier<MoodState> {
  final DatabaseService _databaseService;

  MoodNotifier({required DatabaseService databaseService})
      : _databaseService = databaseService,
        super(const MoodState()) {
    // Load mood entries when initialized
    loadMoodEntries();
  }

  /// Load mood entries from the database
  Future<void> loadMoodEntries() async {
    try {
      state = state.copyWith(isLoading: true, error: null);
      final entries = await _databaseService.getAllMoodEntries();
      state = state.copyWith(
        entries: entries,
        isLoading: false,
      );
    } catch (e) {
      state = state.copyWith(
        isLoading: false,
        error: 'Failed to load mood entries: $e',
      );
    }
  }

  /// Add a new mood entry to the database
  Future<void> addMoodEntry(MoodEntry entry) async {
    try {
      state = state.copyWith(isLoading: true, error: null);

      // Save to database
      await _databaseService.insertMoodEntry(entry);

      // Update state immediately with new entry
      state = state.copyWith(
        entries: [...state.entries, entry],
        isLoading: false,
      );
    } catch (e) {
      state = state.copyWith(
        isLoading: false,
        error: 'Failed to add mood entry: $e',
      );
    }
  }

  /// Update an existing mood entry in the database
  Future<void> updateMoodEntry(MoodEntry entry) async {
    try {
      state = state.copyWith(isLoading: true, error: null);

      // Update in database
      await _databaseService.updateMoodEntry(entry);

      // Update state immediately
      state = state.copyWith(
        entries:
            state.entries.map((e) => e.id == entry.id ? entry : e).toList(),
        isLoading: false,
      );
    } catch (e) {
      state = state.copyWith(
        isLoading: false,
        error: 'Failed to update mood entry: $e',
      );
    }
  }

  /// Delete a mood entry
  Future<void> deleteMoodEntry(String id) async {
    try {
      state = state.copyWith(isLoading: true, error: null);

      // Delete from database
      await _databaseService.deleteMoodEntry(id);

      // Update state immediately by filtering out the deleted entry
      state = state.copyWith(
        entries: state.entries.where((e) => e.id != id).toList(),
        isLoading: false,
      );
    } catch (e) {
      state = state.copyWith(
        isLoading: false,
        error: 'Failed to delete mood entry: $e',
      );
    }
  }

  /// Get a mood entry for a specific date
  Future<MoodEntry?> getMoodEntryForDate(DateTime date) async {
    try {
      // Check if we already have it in state
      final normalizedDate = DateTime(date.year, date.month, date.day);
      final existingEntry = state.entries.firstWhere(
        (entry) => DateTime(entry.date.year, entry.date.month, entry.date.day)
            .isAtSameMomentAs(normalizedDate),
        orElse: () => throw Exception('Not found in state'),
      );
      return existingEntry;
    } catch (_) {
      try {
        // Try to fetch from database
        return await _databaseService.getMoodEntryForDate(date);
      } catch (e) {
        // Handle error but don't update state
        return null;
      }
    }
  }
}

//----------------------------------------------------------------------------
// PROVIDER DEFINITIONS
//----------------------------------------------------------------------------

/// Main state notifier provider for mood entries
final moodStateProvider = StateNotifierProvider<MoodNotifier, MoodState>((ref) {
  final databaseService = ref.watch(databaseServiceProvider);
  return MoodNotifier(databaseService: databaseService);
});

//----------------------------------------------------------------------------
// CONVENIENCE PROVIDERS
//----------------------------------------------------------------------------

/// Provider for accessing the list of mood entries
final moodEntriesProvider = Provider<List<MoodEntry>>((ref) {
  return ref.watch(moodStateProvider).entries;
});

/// Provider for checking if mood entries are loading
final moodEntriesLoadingProvider = Provider<bool>((ref) {
  return ref.watch(moodStateProvider).isLoading;
});

/// Provider for accessing mood entry loading errors
final moodEntriesErrorProvider = Provider<String?>((ref) {
  return ref.watch(moodStateProvider).error;
});

/// Provider for getting a mood entry for a specific date
final moodEntryForDateProvider =
    FutureProvider.family<MoodEntry?, DateTime>((ref, date) async {
  final moodNotifier = ref.watch(moodStateProvider.notifier);
  return await moodNotifier.getMoodEntryForDate(date);
});

/// Provider for getting mood entry dates
final moodEntryDatesProvider = Provider<Set<DateTime>>((ref) {
  final entries = ref.watch(moodStateProvider).entries;
  return entries.map((entry) {
    final date = entry.date;
    return DateTime(date.year, date.month, date.day);
  }).toSet();
});

final filteredMoodEntriesProvider =
    Provider.family<List<MoodEntry>, String>((ref, timeframe) {
  final entries = ref.watch(moodEntriesProvider);

  if (timeframe == 'All Time') {
    return entries;
  }

  // Extract start date based on timeframe
  final now = DateTime.now();
  final DateTime startDate;

  switch (timeframe) {
    case 'Last 7 Days':
      startDate = now.subtract(const Duration(days: 7));
      break;
    case 'Last 30 Days':
      startDate = now.subtract(const Duration(days: 30));
      break;
    case 'Last 90 Days':
      startDate = now.subtract(const Duration(days: 90));
      break;
    case 'Last 6 Months':
      startDate = DateTime(now.year, now.month - 6, now.day);
      break;
    case 'Last Year':
      startDate = DateTime(now.year - 1, now.month, now.day);
      break;
    default:
      return entries;
  }

  return entries.where((entry) => entry.date.isAfter(startDate)).toList();
});

/// Provider for mood icon colors
final moodColorsProvider = Provider<Map<MoodRating, Color>>((ref) {
  return {
    MoodRating.great: Colors.green.shade400,
    MoodRating.good: Colors.lightGreen.shade400,
    MoodRating.okay: Colors.amber.shade400,
    MoodRating.meh: Colors.orange.shade400,
    MoodRating.bad: Colors.red.shade400,
  };
});
