import 'dart:collection';

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:habo/generated/l10n.dart';
import 'package:habo/habits/habit.dart';
import 'package:habo/habits/habits_manager.dart';
import 'package:habo/model/habit_data.dart';
import 'package:habo/repositories/category_repository.dart';
import 'package:habo/repositories/event_repository.dart';
import 'package:habo/repositories/habit_repository.dart';
import 'package:habo/services/backup_service.dart';
import 'package:habo/services/notification_service.dart';
import 'package:habo/services/ui_feedback_service.dart';
import 'package:mocktail/mocktail.dart';

class MockHabitRepository extends Mock implements HabitRepository {}

class MockEventRepository extends Mock implements EventRepository {}

class MockCategoryRepository extends Mock implements CategoryRepository {}

class MockBackupService extends Mock implements BackupService {}

class MockNotificationService extends Mock implements NotificationService {}

class MockUIFeedbackService extends Mock implements UIFeedbackService {}

void main() {
  late HabitsManager habitsManager;
  late MockHabitRepository mockHabitRepository;
  late MockEventRepository mockEventRepository;
  late MockCategoryRepository mockCategoryRepository;
  late MockBackupService mockBackupService;
  late MockNotificationService mockNotificationService;
  late MockUIFeedbackService mockUIFeedbackService;

  setUpAll(() async {
    // Initialize localization for tests
    TestWidgetsFlutterBinding.ensureInitialized();
    await S.load(const Locale('en'));
    registerFallbackValue(Habit(
      habitData: HabitData(
        position: 0,
        title: 'Fallback',
        twoDayRule: false,
        cue: '',
        routine: '',
        reward: '',
        showReward: false,
        advanced: false,
        notification: false,
        notTime: const TimeOfDay(hour: 9, minute: 0),
        events: SplayTreeMap<DateTime, List>(),
        sanction: '',
        showSanction: false,
        accountant: '',
      ),
    ));
    registerFallbackValue(TimeOfDay.now());
    registerFallbackValue(Colors.grey);
  });

  setUp(() {
    mockHabitRepository = MockHabitRepository();
    mockEventRepository = MockEventRepository();
    mockCategoryRepository = MockCategoryRepository();
    mockBackupService = MockBackupService();
    mockNotificationService = MockNotificationService();
    mockUIFeedbackService = MockUIFeedbackService();

    habitsManager = HabitsManager(
      habitRepository: mockHabitRepository,
      eventRepository: mockEventRepository,
      categoryRepository: mockCategoryRepository,
      backupService: mockBackupService,
      notificationService: mockNotificationService,
      uiFeedbackService: mockUIFeedbackService,
    );
  });

  group('HabitsManager Tests', () {
    test('should initialize with provided repositories', () {
      expect(habitsManager, isNotNull);
    });

    test('should populate allHabits from repository', () async {
      // Arrange
      final mockHabits = [
        Habit(
          habitData: HabitData(
            id: 1,
            position: 0,
            title: 'Test Habit',
            twoDayRule: false,
            cue: '',
            routine: '',
            reward: '',
            showReward: false,
            advanced: false,
            notification: false,
            notTime: const TimeOfDay(hour: 9, minute: 0),
            events: SplayTreeMap<DateTime, List>(),
            sanction: '',
            showSanction: false,
            accountant: '',
          ),
        ),
      ];

      when(() => mockHabitRepository.getAllHabits())
          .thenAnswer((_) async => mockHabits);

      // Act
      await habitsManager.initModel();

      // Assert
      verify(() => mockHabitRepository.getAllHabits()).called(1);
    });

    group('CRUD Operations', () {
      test('should add habit', () async {
        // Arrange

        when(() => mockHabitRepository.createHabit(any()))
            .thenAnswer((_) async => 1);

        // Act
        habitsManager.addHabit(
          'Test Habit',
          false,
          'Test cue',
          'Test routine',
          'Test reward',
          false,
          false,
          false,
          const TimeOfDay(hour: 9, minute: 0),
          'Test sanction',
          false,
          'Test accountant',
        );

        // Assert
        verify(() => mockHabitRepository.createHabit(any())).called(1);
      });

      test('should edit habit', () async {
        // Setup
        final testHabit = Habit(
          habitData: HabitData(
            id: 1,
            position: 0,
            title: 'Test Habit',
            twoDayRule: false,
            cue: '',
            routine: '',
            reward: '',
            showReward: false,
            advanced: false,
            notification: false,
            notTime: const TimeOfDay(hour: 9, minute: 0),
            events: SplayTreeMap<DateTime, List>(),
            sanction: '',
            showSanction: false,
            accountant: '',
          ),
        );

        // Add habit to internal state
        habitsManager.allHabits.add(testHabit);
        when(() => mockHabitRepository.updateHabit(any()))
            .thenAnswer((_) async {});

        // Act
        habitsManager.editHabit(testHabit.habitData);

        // Assert
        verify(() => mockHabitRepository.updateHabit(any())).called(1);
      });

      test('should delete habit', () async {
        // Setup
        final testHabit = Habit(
          habitData: HabitData(
            id: 1,
            position: 0,
            title: 'Test Habit',
            twoDayRule: false,
            cue: '',
            routine: '',
            reward: '',
            showReward: false,
            advanced: false,
            notification: false,
            notTime: const TimeOfDay(hour: 9, minute: 0),
            events: SplayTreeMap<DateTime, List>(),
            sanction: '',
            showSanction: false,
            accountant: '',
          ),
        );

        // Add habit to internal state
        habitsManager.allHabits.add(testHabit);
        when(() => mockHabitRepository.deleteHabit(any()))
            .thenAnswer((_) async {});
        when(() => mockUIFeedbackService.showMessageWithAction(
              message: any(named: 'message'),
              actionLabel: any(named: 'actionLabel'),
              onActionPressed: any(named: 'onActionPressed'),
              backgroundColor: any(named: 'backgroundColor'),
            )).thenReturn(null);

        // Act
        habitsManager.deleteHabit(1);

        // Assert - verify internal state changes immediately
        expect(habitsManager.allHabits.length, 0);
        expect(habitsManager.toDelete.length, 1);
      });
    });

    group('Archive Operations', () {
      test('should archive habit', () async {
        // Setup
        final testHabit = Habit(
          habitData: HabitData(
            id: 1,
            position: 0,
            title: 'Test Habit',
            twoDayRule: false,
            cue: '',
            routine: '',
            reward: '',
            showReward: false,
            advanced: false,
            notification: false,
            notTime: const TimeOfDay(hour: 9, minute: 0),
            events: SplayTreeMap<DateTime, List>(),
            sanction: '',
            showSanction: false,
            accountant: '',
            archived: false,
          ),
        );

        // Add habit to internal state
        habitsManager.allHabits.add(testHabit);
        when(() => mockHabitRepository.updateHabit(any()))
            .thenAnswer((_) async {});
        when(() => mockNotificationService.disableHabitNotification(any()))
            .thenReturn(null);
        when(() => mockUIFeedbackService.showMessageWithAction(
              message: any(named: 'message'),
              actionLabel: any(named: 'actionLabel'),
              onActionPressed: any(named: 'onActionPressed'),
              backgroundColor: any(named: 'backgroundColor'),
            )).thenReturn(null);

        // Act
        habitsManager.archiveHabit(1);

        // Assert
        expect(testHabit.habitData.archived, true);
        verify(() => mockHabitRepository.updateHabit(any())).called(1);
        verify(() => mockNotificationService.disableHabitNotification(1))
            .called(1);
        verify(() => mockUIFeedbackService.showMessageWithAction(
              message: any(named: 'message'),
              actionLabel: any(named: 'actionLabel'),
              onActionPressed: any(named: 'onActionPressed'),
              backgroundColor: any(named: 'backgroundColor'),
            )).called(1);
      });

      test('should unarchive habit', () async {
        // Setup
        final testHabit = Habit(
          habitData: HabitData(
            id: 1,
            position: 0,
            title: 'Test Habit',
            twoDayRule: false,
            cue: '',
            routine: '',
            reward: '',
            showReward: false,
            advanced: false,
            notification: true,
            notTime: const TimeOfDay(hour: 9, minute: 0),
            events: SplayTreeMap<DateTime, List>(),
            sanction: '',
            showSanction: false,
            accountant: '',
            archived: true,
          ),
        );

        // Add habit to internal state
        habitsManager.allHabits.add(testHabit);
        when(() => mockHabitRepository.updateHabit(any()))
            .thenAnswer((_) async {});
        when(() => mockNotificationService.setHabitNotification(
            any(), any(), any(), any())).thenReturn(null);
        when(() => mockUIFeedbackService.showSuccess(any())).thenReturn(null);

        // Act
        habitsManager.unarchiveHabit(1);

        // Assert
        expect(testHabit.habitData.archived, false);
        verify(() => mockHabitRepository.updateHabit(any())).called(1);
        verify(() => mockNotificationService.setHabitNotification(
            1, any(), 'Habo', 'Test Habit')).called(1);
        verify(() => mockUIFeedbackService.showSuccess(any())).called(1);
      });

      test('should filter active habits correctly', () {
        // Setup
        final activeHabit = Habit(
          habitData: HabitData(
            id: 1,
            position: 0,
            title: 'Active Habit',
            twoDayRule: false,
            cue: '',
            routine: '',
            reward: '',
            showReward: false,
            advanced: false,
            notification: false,
            notTime: const TimeOfDay(hour: 9, minute: 0),
            events: SplayTreeMap<DateTime, List>(),
            sanction: '',
            showSanction: false,
            accountant: '',
            archived: false,
          ),
        );

        final archivedHabit = Habit(
          habitData: HabitData(
            id: 2,
            position: 1,
            title: 'Archived Habit',
            twoDayRule: false,
            cue: '',
            routine: '',
            reward: '',
            showReward: false,
            advanced: false,
            notification: false,
            notTime: const TimeOfDay(hour: 9, minute: 0),
            events: SplayTreeMap<DateTime, List>(),
            sanction: '',
            showSanction: false,
            accountant: '',
            archived: true,
          ),
        );

        // Add habits to internal state
        habitsManager.allHabits.addAll([activeHabit, archivedHabit]);

        // Act & Assert
        final activeHabits = habitsManager.activeHabits;
        final archivedHabits = habitsManager.archivedHabits;

        expect(activeHabits.length, 1);
        expect(activeHabits.first.habitData.title, 'Active Habit');
        expect(archivedHabits.length, 1);
        expect(archivedHabits.first.habitData.title, 'Archived Habit');
      });
    });
  });
}
