import 'package:flutter_test/flutter_test.dart';
import 'package:superport_v2/features/approvals/request/presentation/controllers/approval_request_controller.dart';

void main() {
  ApprovalRequestParticipant buildParticipant(int id) {
    return ApprovalRequestParticipant(
      id: id,
      name: '사용자$id',
      employeeNo: 'EMP$id',
    );
  }

  group('ApprovalRequestController', () {
    late ApprovalRequestController controller;

    setUp(() {
      controller = ApprovalRequestController();
    });

    test('addStep 성공 시 최종 승인자로 바인딩된다', () {
      final approver = buildParticipant(10);

      final added = controller.addStep(approver: approver, note: '검토 필요');

      expect(added, isTrue);
      expect(controller.steps, hasLength(1));
      expect(controller.steps.first.stepOrder, 1);
      expect(controller.finalApprover?.id, approver.id);
      expect(controller.errorMessage, isNull);
    });

    test('최대 단계 수를 초과하면 추가를 거부한다', () {
      for (var i = 0; i < controller.maxSteps; i++) {
        final success = controller.addStep(approver: buildParticipant(i + 1));
        expect(success, isTrue);
      }

      final result = controller.addStep(approver: buildParticipant(999));

      expect(result, isFalse);
      expect(controller.errorMessage, isNotNull);
      expect(controller.steps, hasLength(controller.maxSteps));
    });

    test('동일한 승인자를 중복 추가할 수 없다', () {
      final approver = buildParticipant(3);
      controller.addStep(approver: approver);

      final duplicated = controller.addStep(approver: approver);

      expect(duplicated, isFalse);
      expect(controller.errorMessage, isNotNull);
      expect(controller.steps, hasLength(1));
    });

    test('상신자는 승인자로 추가할 수 없다', () {
      final requester = buildParticipant(7);
      controller.setRequester(requester);

      final added = controller.addStep(approver: requester);

      expect(added, isFalse);
      expect(controller.steps, isEmpty);
      expect(controller.errorMessage, isNotNull);
    });

    test('updateStep에서 승인자 중복을 감지한다', () {
      controller
        ..addStep(approver: buildParticipant(1))
        ..addStep(approver: buildParticipant(2));

      final updated = controller.updateStep(1, approver: buildParticipant(1));

      expect(updated, isFalse);
      expect(controller.steps[1].approver.id, 2);
      expect(controller.errorMessage, isNotNull);
    });

    test('removeStepAt 호출 시 순번을 재정렬한다', () {
      controller
        ..addStep(approver: buildParticipant(1))
        ..addStep(approver: buildParticipant(2))
        ..addStep(approver: buildParticipant(3));

      controller.removeStepAt(1);

      expect(controller.steps, hasLength(2));
      expect(controller.steps.first.stepOrder, 1);
      expect(controller.steps.last.stepOrder, 2);
    });

    test('moveStep 호출 시 순서를 변경한다', () {
      controller
        ..addStep(approver: buildParticipant(1))
        ..addStep(approver: buildParticipant(2))
        ..addStep(approver: buildParticipant(3));

      controller.moveStep(2, 0);

      expect(controller.steps.first.approver.id, 3);
      expect(controller.steps.first.stepOrder, 1);
      expect(controller.steps.last.approver.id, 2);
      expect(controller.steps.last.stepOrder, 3);
    });

    test('setFinalApprover는 단계가 없을 때 새 단계를 추가한다', () {
      final approver = buildParticipant(55);

      final result = controller.setFinalApprover(approver);

      expect(result, isTrue);
      expect(controller.steps, hasLength(1));
      expect(controller.finalApprover?.id, approver.id);
    });

    test('buildTransactionApprovalInput은 필수 정보를 누락하면 예외를 던진다', () {
      controller.addStep(approver: buildParticipant(1));

      expect(
        () => controller.buildTransactionApprovalInput(),
        throwsStateError,
      );
    });

    test('buildTransactionApprovalInput은 요청 데이터를 변환한다', () {
      final requester = buildParticipant(77);
      controller.setRequester(requester);
      controller
        ..addStep(approver: buildParticipant(101), note: '검토')
        ..addStep(approver: buildParticipant(102));

      final approvalInput = controller.buildTransactionApprovalInput(
        approvalStatusId: 9,
        title: '입고 결재',
        summary: '재고 입고 승인 요청',
        note: '긴급 승인 필요',
        metadata: const {'priority': 'high'},
      );

      final payload = approvalInput.toJson();
      expect(payload['requested_by_id'], requester.id);
      expect(payload['approval_status_id'], 9);
      expect(payload['final_approver_id'], 102);
      expect(payload['title'], '입고 결재');
      expect(payload['summary'], '재고 입고 승인 요청');
      expect(payload['note'], '긴급 승인 필요');
      final steps = payload['steps'] as List<dynamic>;
      expect(steps, hasLength(2));
      expect(steps.first['step_order'], 1);
      expect(steps.first['approver_id'], 101);
      expect(steps.first['note'], '검토');
      expect(steps.last['step_order'], 2);
      expect(steps.last['approver_id'], 102);
    });

    test('buildSubmissionInput 변환 시 finalApproverId를 채운다', () {
      controller
        ..setRequester(buildParticipant(2))
        ..addStep(approver: buildParticipant(10))
        ..addStep(approver: buildParticipant(11), note: '최종 검토');

      final submission = controller.buildSubmissionInput(statusId: 4);

      expect(submission.requesterId, 2);
      expect(submission.finalApproverId, 11);
      expect(submission.steps, hasLength(2));
    });

    test('applyTemplateSteps는 전달된 순서를 유지한다', () {
      final steps = [
        ApprovalRequestStep(stepOrder: 3, approver: buildParticipant(90)),
        ApprovalRequestStep(stepOrder: 9, approver: buildParticipant(91)),
      ];

      controller.applyTemplateSteps(steps);

      expect(controller.steps.first.stepOrder, 1);
      expect(controller.steps.last.stepOrder, 2);
      expect(controller.finalApproverId, 91);
    });

    test('중복 승인자가 있는 상태에서 빌드 시 예외가 발생한다', () {
      controller.setRequester(buildParticipant(5));
      controller.applyTemplateSteps([
        ApprovalRequestStep(stepOrder: 1, approver: buildParticipant(8)),
        ApprovalRequestStep(stepOrder: 2, approver: buildParticipant(8)),
      ]);

      expect(
        () => controller.buildSubmissionInput(statusId: 1),
        throwsStateError,
      );
    });

    test('상신자와 동일한 승인자가 포함된 템플릿은 적용되지 않는다', () {
      final requester = buildParticipant(20);
      controller.setRequester(requester);

      controller.applyTemplateSteps([
        ApprovalRequestStep(stepOrder: 1, approver: requester),
        ApprovalRequestStep(stepOrder: 2, approver: buildParticipant(21)),
      ]);

      expect(controller.steps, isEmpty);
      expect(controller.errorMessage, isNotNull);
    });

    test('상신자 변경 시 승인자와 겹치면 오류가 발생한다', () {
      controller
        ..addStep(approver: buildParticipant(40))
        ..addStep(approver: buildParticipant(41));

      controller.setRequester(buildParticipant(41));

      expect(controller.requester?.id, 41);
      expect(controller.errorMessage, isNotNull);
    });
  });
}
