import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';

import 'package:agenda_tasks/core/errors/failures.dart';
import 'package:agenda_tasks/core/errors/result.dart';
import 'package:agenda_tasks/core/logging/app_logger.dart';
import 'package:agenda_tasks/features/tasks/domain/entities/task_entity.dart';
import 'package:agenda_tasks/features/tasks/domain/enums/priority.dart';
import 'package:agenda_tasks/features/tasks/domain/repositories/task_repository.dart';
import 'package:agenda_tasks/features/tasks/presentation/viewmodels/daily_tasks_viewmodel.dart';

// Mock classes
class MockTaskRepository extends Mock implements TaskRepository {}

class MockAppLogger extends Mock implements AppLogger {}

void main() {
  late DailyTasksViewModel viewModel;
  late MockTaskRepository mockRepository;
  late MockAppLogger mockLogger;

  setUp(() {
    mockRepository = MockTaskRepository();
    mockLogger = MockAppLogger();
    viewModel = DailyTasksViewModel(
      repository: mockRepository,
      logger: mockLogger,
    );
  });

  group('DailyTasksViewModel', () {
    final testDate = DateTime(2024, 1, 15);

    // Helper function to create fresh test data for each test
    List<TaskEntity> createTestTasks() => [
          TaskEntity(
            id: '1',
            title: 'Task 1',
            date: testDate,
            priority: Priority.high,
            isDone: false,
          ),
          TaskEntity(
            id: '2',
            title: 'Task 2',
            date: testDate,
            priority: Priority.medium,
            isDone: true,
          ),
          TaskEntity(
            id: '3',
            title: 'Task 3',
            date: testDate,
            priority: Priority.low,
            isDone: false,
          ),
        ];

    group('loadTasks', () {
      test('should set status to loading then success when tasks loaded', () async {
        // Arrange
        when(() => mockRepository.getTasksByDate(any()))
            .thenAnswer((_) async => Success(createTestTasks()));

        // Act
        final future = viewModel.loadTasks();

        // Assert - initial state should be loading
        expect(viewModel.status, equals(TasksStatus.loading));

        await future;

        // After completion
        expect(viewModel.status, equals(TasksStatus.success));
        expect(viewModel.tasks.length, equals(3));
      });

      test('should set status to error when loading fails', () async {
        // Arrange
        when(() => mockRepository.getTasksByDate(any()))
            .thenAnswer((_) async => Error(const NetworkFailure()));

        // Act
        await viewModel.loadTasks();

        // Assert
        expect(viewModel.status, equals(TasksStatus.error));
        expect(viewModel.failure, isA<NetworkFailure>());
      });

      test('should sort tasks by priority (high > medium > low)', () async {
        // Arrange
        final unsortedTasks = [
          TaskEntity(id: '1', title: 'Low', date: testDate, priority: Priority.low, isDone: false),
          TaskEntity(id: '2', title: 'High', date: testDate, priority: Priority.high, isDone: false),
          TaskEntity(id: '3', title: 'Medium', date: testDate, priority: Priority.medium, isDone: false),
        ];
        when(() => mockRepository.getTasksByDate(any()))
            .thenAnswer((_) async => Success(unsortedTasks));

        // Act
        await viewModel.loadTasks();

        // Assert
        expect(viewModel.tasks[0].priority, equals(Priority.high));
        expect(viewModel.tasks[1].priority, equals(Priority.medium));
        expect(viewModel.tasks[2].priority, equals(Priority.low));
      });
    });

    group('setFilter', () {
      setUp(() async {
        when(() => mockRepository.getTasksByDate(any()))
            .thenAnswer((_) async => Success(createTestTasks()));
        await viewModel.loadTasks();
      });

      test('should show all tasks when filter is all', () {
        // Act
        viewModel.setFilter(TaskFilter.all);

        // Assert
        expect(viewModel.filter, equals(TaskFilter.all));
        expect(viewModel.tasks.length, equals(3));
      });

      test('should show only active tasks when filter is active', () {
        // Act
        viewModel.setFilter(TaskFilter.active);

        // Assert
        expect(viewModel.filter, equals(TaskFilter.active));
        expect(viewModel.tasks.length, equals(2));
        expect(viewModel.tasks.every((t) => !t.isDone), isTrue);
      });

      test('should show only completed tasks when filter is completed', () {
        // Act
        viewModel.setFilter(TaskFilter.completed);

        // Assert
        expect(viewModel.filter, equals(TaskFilter.completed));
        expect(viewModel.tasks.length, equals(1));
        expect(viewModel.tasks.every((t) => t.isDone), isTrue);
      });
    });

    group('toggleTask', () {
      test('should update task in list when toggle succeeds', () async {
        // Arrange
        when(() => mockRepository.getTasksByDate(any()))
            .thenAnswer((_) async => Success(createTestTasks()));
        await viewModel.loadTasks();

        final toggledTask = createTestTasks()[0].copyWith(isDone: true);
        when(() => mockRepository.toggleTaskStatus('1'))
            .thenAnswer((_) async => Success(toggledTask));

        // Act
        await viewModel.toggleTask('1');

        // Assert
        final updatedTask = viewModel.tasks.firstWhere((t) => t.id == '1');
        expect(updatedTask.isDone, isTrue);
      });

      test('should set failure when toggle fails', () async {
        // Arrange
        when(() => mockRepository.getTasksByDate(any()))
            .thenAnswer((_) async => Success(createTestTasks()));
        await viewModel.loadTasks();

        when(() => mockRepository.toggleTaskStatus('1'))
            .thenAnswer((_) async => Error(const ServerFailure(message: 'Error')));

        // Act
        await viewModel.toggleTask('1');

        // Assert
        expect(viewModel.failure, isA<ServerFailure>());
      });
    });

    group('deleteTask', () {
      test('should remove task from list when delete succeeds', () async {
        // Arrange
        when(() => mockRepository.getTasksByDate(any()))
            .thenAnswer((_) async => Success(createTestTasks()));
        await viewModel.loadTasks();

        when(() => mockRepository.deleteTask('1'))
            .thenAnswer((_) async => const Success(null));

        // Act
        await viewModel.deleteTask('1');

        // Assert
        expect(viewModel.tasks.any((t) => t.id == '1'), isFalse);
        expect(viewModel.totalCount, equals(2));
      });
    });

    group('date navigation', () {
      test('should update selectedDate when setSelectedDate is called', () {
        // Arrange
        when(() => mockRepository.getTasksByDate(any()))
            .thenAnswer((_) async => Success([]));

        // Act
        viewModel.setSelectedDate(testDate);

        // Assert
        expect(viewModel.selectedDate.year, equals(2024));
        expect(viewModel.selectedDate.month, equals(1));
        expect(viewModel.selectedDate.day, equals(15));
      });

      test('previousDay should subtract one day', () {
        // Arrange
        when(() => mockRepository.getTasksByDate(any()))
            .thenAnswer((_) async => Success([]));
        viewModel.setSelectedDate(testDate);

        // Act
        viewModel.previousDay();

        // Assert
        expect(viewModel.selectedDate.day, equals(14));
      });

      test('nextDay should add one day', () {
        // Arrange
        when(() => mockRepository.getTasksByDate(any()))
            .thenAnswer((_) async => Success([]));
        viewModel.setSelectedDate(testDate);

        // Act
        viewModel.nextDay();

        // Assert
        expect(viewModel.selectedDate.day, equals(16));
      });
    });

    group('counts', () {
      test('should return correct total and completed counts', () async {
        // Arrange
        when(() => mockRepository.getTasksByDate(any()))
            .thenAnswer((_) async => Success(createTestTasks()));

        // Act
        await viewModel.loadTasks();

        // Assert
        expect(viewModel.totalCount, equals(3));
        expect(viewModel.completedCount, equals(1));
      });
    });
  });
}
