/// Tests unitaires pour CreateBudget use case
library create_budget_test;

import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:dartz/dartz.dart';
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/usecases/create_budget.dart';
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/repositories/finance_workflow_repository.dart';
import 'package:unionflow_mobile_apps/features/finance_workflow/domain/entities/budget.dart';
import 'package:unionflow_mobile_apps/core/error/failures.dart';

@GenerateMocks([FinanceWorkflowRepository])
import 'create_budget_test.mocks.dart';

void main() {
  late CreateBudget useCase;
  late MockFinanceWorkflowRepository mockRepository;

  setUp(() {
    mockRepository = MockFinanceWorkflowRepository();
    useCase = CreateBudget(mockRepository);
  });

  group('CreateBudget Use Case', () {
    const tName = 'Budget 2025';
    const tOrgId = 'org-123';
    final tBudgetLines = [
      BudgetLine(
        id: 'line-1',
        category: BudgetCategory.contributions,
        name: 'Cotisations mensuelles',
        description: 'Revenus des cotisations',
        amountPlanned: 3000000.0,
      ),
      BudgetLine(
        id: 'line-2',
        category: BudgetCategory.savings,
        name: 'Dépôts épargne',
        description: 'Collecte épargne',
        amountPlanned: 2000000.0,
      ),
      BudgetLine(
        id: 'line-3',
        category: BudgetCategory.solidarity,
        name: 'Aide mutuelle',
        description: 'Soutien membres',
        amountPlanned: 1000000.0,
      ),
    ];
    final tCreatedBudget = Budget(
      id: 'budget-new',
      name: tName,
      organizationId: tOrgId,
      period: BudgetPeriod.annual,
      year: 2025,
      status: BudgetStatus.draft,
      lines: tBudgetLines,
      totalPlanned: 6000000.0,
      totalRealized: 0.0,
      currency: 'XOF',
      createdBy: 'user-1',
      createdAt: DateTime.now(),
      startDate: DateTime(2025, 1, 1),
      endDate: DateTime(2025, 12, 31),
    );

    test('should create budget successfully', () async {
      // Arrange
      when(mockRepository.createBudget(
        name: tName,
        description: anyNamed('description'),
        organizationId: tOrgId,
        period: BudgetPeriod.annual,
        year: 2025,
        month: anyNamed('month'),
        lines: tBudgetLines,
      )).thenAnswer((_) async => Right(tCreatedBudget));

      // Act
      final result = await useCase(
        name: tName,
        organizationId: tOrgId,
        period: BudgetPeriod.annual,
        year: 2025,
        lines: tBudgetLines,
      );

      // Assert
      expect(result, Right(tCreatedBudget));
      result.fold(
        (failure) => fail('Should not return failure'),
        (budget) {
          expect(budget.id, equals('budget-new'));
          expect(budget.name, equals(tName));
          expect(budget.status, equals(BudgetStatus.draft));
        },
      );
      verify(mockRepository.createBudget(
        name: tName,
        description: null,
        organizationId: tOrgId,
        period: BudgetPeriod.annual,
        year: 2025,
        month: null,
        lines: tBudgetLines,
      ));
      verifyNoMoreInteractions(mockRepository);
    });

    test('should create monthly budget with description', () async {
      // Arrange
      const description = 'Budget opérationnel janvier 2025';
      final monthlyLines = [
        BudgetLine(
          id: 'line-monthly',
          category: BudgetCategory.contributions,
          name: 'Cotisations janvier',
          amountPlanned: 500000.0,
        ),
      ];
      final monthlyBudget = Budget(
        id: 'budget-monthly',
        name: 'Budget Janvier 2025',
        description: description,
        organizationId: tOrgId,
        period: BudgetPeriod.monthly,
        year: 2025,
        month: 1,
        status: BudgetStatus.draft,
        lines: monthlyLines,
        totalPlanned: 500000.0,
        totalRealized: 0.0,
        currency: 'XOF',
        createdBy: 'user-1',
        createdAt: DateTime.now(),
        startDate: DateTime(2025, 1, 1),
        endDate: DateTime(2025, 1, 31),
      );
      when(mockRepository.createBudget(
        name: 'Budget Janvier 2025',
        description: description,
        organizationId: tOrgId,
        period: BudgetPeriod.monthly,
        year: 2025,
        month: 1,
        lines: monthlyLines,
      )).thenAnswer((_) async => Right(monthlyBudget));

      // Act
      final result = await useCase(
        name: 'Budget Janvier 2025',
        description: description,
        organizationId: tOrgId,
        period: BudgetPeriod.monthly,
        year: 2025,
        month: 1,
        lines: monthlyLines,
      );

      // Assert
      result.fold(
        (failure) => fail('Should not return failure'),
        (budget) {
          expect(budget.period, equals(BudgetPeriod.monthly));
          expect(budget.month, equals(1));
        },
      );
    });

    test('should return ValidationFailure when name is empty', () async {
      // Act
      final result = await useCase(
        name: '',
        organizationId: tOrgId,
        period: BudgetPeriod.annual,
        year: 2025,
        lines: [BudgetLine(id: 'test-1', category: BudgetCategory.operational, name: 'Test line', amountPlanned: 100.0)],
      );

      // Assert
      result.fold(
        (failure) {
          expect(failure, isA<ValidationFailure>());
        },
        (budget) => fail('Should not return budget'),
      );
      verifyZeroInteractions(mockRepository);
    });

    test('should return ServerFailure when repository fails', () async {
      // Arrange
      final tFailure = ServerFailure('Erreur création budget');
      when(mockRepository.createBudget(
        name: anyNamed('name'),
        description: anyNamed('description'),
        organizationId: anyNamed('organizationId'),
        period: anyNamed('period'),
        year: anyNamed('year'),
        month: anyNamed('month'),
        lines: anyNamed('lines'),
      )).thenAnswer((_) async => Left(tFailure));

      // Act
      final result = await useCase(
        name: tName,
        organizationId: tOrgId,
        period: BudgetPeriod.annual,
        year: 2025,
        lines: [BudgetLine(id: 'test-1', category: BudgetCategory.operational, name: 'Test line', amountPlanned: 100.0)],
      );

      // Assert
      expect(result, Left(tFailure));
      result.fold(
        (failure) => expect(failure, isA<ServerFailure>()),
        (budget) => fail('Should not return budget'),
      );
    });
  });
}
