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

import 'package:superport_v2/features/approvals/domain/entities/approval.dart';
import 'package:superport_v2/features/approvals/domain/entities/approval_proceed_status.dart';
import 'package:superport_v2/features/approvals/domain/repositories/approval_repository.dart';
import 'package:superport_v2/features/approvals/domain/usecases/approve_approval_use_case.dart';
import 'package:superport_v2/features/approvals/domain/usecases/reject_approval_use_case.dart';

class _MockApprovalRepository extends Mock implements ApprovalRepository {}

void main() {
  late ApprovalRepository repository;
  late ApproveApprovalUseCase approveUseCase;
  late RejectApprovalUseCase rejectUseCase;

  setUpAll(() {
    registerFallbackValue(ApprovalDecisionInput(approvalId: 0, actorId: 0));
  });

  setUp(() {
    repository = _MockApprovalRepository();
    approveUseCase = ApproveApprovalUseCase(repository: repository);
    rejectUseCase = RejectApprovalUseCase(repository: repository);
  });

  group('ApproveApprovalUseCase', () {
    test('권한이 있으면 승인 요청을 위임한다', () async {
      const approvalId = 10;
      const actorId = 7;
      final input = ApprovalDecisionInput(
        approvalId: approvalId,
        actorId: actorId,
      );
      final approval = _buildApproval(id: approvalId);

      when(() => repository.canProceed(approvalId)).thenAnswer(
        (_) async =>
            ApprovalProceedStatus(approvalId: approvalId, canProceed: true),
      );
      when(() => repository.approve(any())).thenAnswer((_) async => approval);

      final flow = await approveUseCase(input);

      expect(flow.id, approvalId);
      verify(() => repository.canProceed(approvalId)).called(1);
      final captured =
          verify(() => repository.approve(captureAny())).captured.single
              as ApprovalDecisionInput;
      expect(captured.approvalId, approvalId);
      expect(captured.actorId, actorId);
    });

    test('권한이 없으면 StateError를 던지고 승인 요청을 하지 않는다', () async {
      const approvalId = 99;
      final input = ApprovalDecisionInput(approvalId: approvalId, actorId: 3);
      when(() => repository.canProceed(approvalId)).thenAnswer(
        (_) async => ApprovalProceedStatus(
          approvalId: approvalId,
          canProceed: false,
          reason: '승인 권한이 없습니다.',
        ),
      );

      expect(
        () => approveUseCase(input),
        throwsA(
          isA<StateError>().having(
            (error) => error.message,
            'message',
            contains('승인 권한이 없습니다.'),
          ),
        ),
      );
      verify(() => repository.canProceed(approvalId)).called(1);
      verifyNever(() => repository.approve(any()));
    });
  });

  group('RejectApprovalUseCase', () {
    test('권한이 있으면 반려 요청을 위임한다', () async {
      const approvalId = 70;
      const actorId = 11;
      final input = ApprovalDecisionInput(
        approvalId: approvalId,
        actorId: actorId,
      );
      final approval = _buildApproval(id: approvalId);

      when(() => repository.canProceed(approvalId)).thenAnswer(
        (_) async =>
            ApprovalProceedStatus(approvalId: approvalId, canProceed: true),
      );
      when(() => repository.reject(any())).thenAnswer((_) async => approval);

      final flow = await rejectUseCase(input);

      expect(flow.id, approvalId);
      verify(() => repository.canProceed(approvalId)).called(1);
      final captured =
          verify(() => repository.reject(captureAny())).captured.single
              as ApprovalDecisionInput;
      expect(captured.approvalId, approvalId);
      expect(captured.actorId, actorId);
    });

    test('권한이 없으면 StateError를 던지고 반려 요청을 하지 않는다', () async {
      const approvalId = 45;
      final input = ApprovalDecisionInput(approvalId: approvalId, actorId: 9);

      when(() => repository.canProceed(approvalId)).thenAnswer(
        (_) async =>
            ApprovalProceedStatus(approvalId: approvalId, canProceed: false),
      );

      expect(
        () => rejectUseCase(input),
        throwsA(
          isA<StateError>().having(
            (error) => error.message,
            'message',
            contains('결재를 진행할 권한이 없습니다.'),
          ),
        ),
      );
      verify(() => repository.canProceed(approvalId)).called(1);
      verifyNever(() => repository.reject(any()));
    });
  });
}

Approval _buildApproval({required int id}) {
  final status = ApprovalStatus(id: 1, name: '진행중');
  final approver = ApprovalApprover(id: 5, employeeNo: 'EMP-5', name: '김승인');
  return Approval(
    id: id,
    approvalNo: 'APP-$id',
    transactionNo: 'TRX-$id',
    status: status,
    currentStep: ApprovalStep(
      id: 10,
      stepOrder: 1,
      approver: approver,
      status: status,
      assignedAt: DateTime.utc(2025, 1, 1, 9),
    ),
    requester: ApprovalRequester(id: 2, employeeNo: 'EMP-2', name: '상신자'),
    requestedAt: DateTime.utc(2025, 1, 1, 9),
    steps: [
      ApprovalStep(
        id: 10,
        stepOrder: 1,
        approver: approver,
        status: status,
        assignedAt: DateTime.utc(2025, 1, 1, 9),
      ),
    ],
    histories: const [],
  );
}
