import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:intl/intl.dart' as intl;
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:superport_v2/core/common/models/paginated_result.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/entities/approval_template.dart';
import 'package:superport_v2/features/approvals/domain/repositories/approval_repository.dart';
import 'package:superport_v2/features/approvals/domain/repositories/approval_template_repository.dart';
import 'package:superport_v2/features/approvals/presentation/controllers/approval_controller.dart';
import 'package:superport_v2/features/approvals/presentation/dialogs/approval_detail_dialog.dart';
import 'package:superport_v2/widgets/components/superport_dialog.dart';

import '../../../../helpers/test_app.dart';

void main() {
  TestWidgetsFlutterBinding.ensureInitialized();

  final dateFormat = intl.DateFormat('yyyy-MM-dd HH:mm');

  late _FakeApprovalRepository approvalRepository;
  late _FakeApprovalTemplateRepository templateRepository;
  late ApprovalController controller;
  late Approval sampleApproval;
  late ApprovalTemplate sampleTemplate;

  setUp(() async {
    approvalRepository = _FakeApprovalRepository();
    templateRepository = _FakeApprovalTemplateRepository();

    controller = ApprovalController(
      approvalRepository: approvalRepository,
      templateRepository: templateRepository,
    );

    final statusInProgress = ApprovalStatus(id: 1, name: '진행중');
    final requester = ApprovalRequester(
      id: 99,
      employeeNo: 'E099',
      name: '요청자',
    );
    final approver = ApprovalApprover(id: 77, employeeNo: 'E077', name: '승인자');

    final step = ApprovalStep(
      id: 501,
      stepOrder: 1,
      approver: approver,
      status: statusInProgress,
      assignedAt: DateTime(2024, 1, 1, 9),
    );

    sampleApproval = Approval(
      id: 100,
      approvalNo: 'APP-2024-0100',
      transactionNo: 'TRX-100',
      status: statusInProgress,
      requester: requester,
      requestedAt: DateTime(2024, 1, 1, 9),
      steps: [step],
      histories: const [],
    );

    approvalRepository.detail = sampleApproval;
    approvalRepository.updatedAfterAssign = sampleApproval;
    approvalRepository.updatedAfterAction = sampleApproval;

    sampleTemplate = ApprovalTemplate(
      id: 200,
      code: 'TEMP-200',
      name: '샘플 템플릿',
      isActive: true,
      steps: [
        ApprovalTemplateStep(
          id: null,
          stepOrder: 1,
          approver: ApprovalTemplateApprover(
            id: 42,
            employeeNo: 'E042',
            name: '템플 승인자',
          ),
        ),
      ],
    );

    templateRepository
      ..listResult = [sampleTemplate]
      ..detailResult = sampleTemplate;

    approvalRepository.actions = [
      ApprovalAction(id: 1, name: '승인', code: 'approve'),
      ApprovalAction(id: 2, name: '반려', code: 'reject'),
      ApprovalAction(id: 3, name: '코멘트', code: 'comment'),
    ];

    expect(controller.templates, isEmpty);

    await controller.loadTemplates(force: true);
    await controller.loadActionOptions(force: true);
    await controller.selectApproval(sampleApproval.id!);
    expect(controller.templates, isNotEmpty);
    expect(controller.selected, isNotNull);
    expect(controller.canProceedSelected, isTrue);
    expect(controller.hasActionOptions, isTrue);
  });

  tearDown(() {
    controller.dispose();
  });

  Future<void> openDialog(WidgetTester tester) async {
    await tester.binding.setSurfaceSize(const Size(1280, 800));
    addTearDown(() => tester.binding.setSurfaceSize(null));
    await tester.pumpWidget(buildTestApp(const SizedBox.shrink()));
    final context = tester.element(find.byType(SizedBox));

    unawaited(
      showApprovalDetailDialog(
        context: context,
        controller: controller,
        dateFormat: dateFormat,
        canPerformStepActions: true,
        canApplyTemplate: true,
      ),
    );
    await tester.pumpAndSettle();
  }

  testWidgets('showApprovalDetailDialog 결재 요약과 템플릿 선택을 표시한다', (tester) async {
    await openDialog(tester);

    expect(
      find.textContaining('결재번호 ${sampleApproval.approvalNo}'),
      findsOneWidget,
    );
    expect(find.textContaining(sampleApproval.transactionNo), findsWidgets);

    final stepsTab = find.text('단계');
    await tester.tap(stepsTab, warnIfMissed: false);
    await tester.pumpAndSettle();
    expect(find.text('Step 1'), findsOneWidget);
    expect(find.text('템플릿 적용'), findsOneWidget);
  });

  testWidgets('템플릿 적용 버튼을 누르면 assignSteps가 호출되고 성공 토스트를 노출한다', (tester) async {
    await openDialog(tester);

    await tester.tap(find.text('단계'), warnIfMissed: false);
    await tester.pumpAndSettle();

    final applyButton = find.byKey(
      const ValueKey('approval_apply_template_button'),
    );
    expect(applyButton, findsOneWidget);
    await tester.ensureVisible(applyButton);
    await tester.tap(applyButton, warnIfMissed: false);
    await tester.pumpAndSettle();
    await tester.pump();

    expect(approvalRepository.assignmentInputs, hasLength(1));
    expect(
      approvalRepository.assignmentInputs.first.steps.first.approverId,
      sampleTemplate.steps.first.approver.id,
    );
  });

  testWidgets('승인 버튼을 누르면 performStepAction이 호출되고 성공 메시지를 노출한다', (
    tester,
  ) async {
    await openDialog(tester);

    await tester.tap(find.text('단계'), warnIfMissed: false);
    await tester.pumpAndSettle();

    final stepText = find.text('Step 1').first;
    final stepCard = find
        .ancestor(of: stepText, matching: find.byType(ShadCard))
        .first;
    expect(stepCard, findsOneWidget);

    final approveButtonFinder = find.byKey(
      const ValueKey('step_action_501_approve'),
    );
    expect(approveButtonFinder, findsOneWidget);
    final approveButton = tester.widget<ShadButton>(approveButtonFinder);
    expect(approveButton.onPressed, isNotNull);
    await tester.ensureVisible(approveButtonFinder);
    await tester.tap(approveButtonFinder, warnIfMissed: false);
    await tester.pumpAndSettle();

    final confirmDialog = find
        .ancestor(
          of: find.text('결재 승인'),
          matching: find.byType(SuperportDialog),
        )
        .first;

    final noteField = find.descendant(
      of: confirmDialog,
      matching: find.byType(EditableText),
    );
    await tester.enterText(noteField, '승인 메모');

    final confirmButton = find
        .descendant(
          of: confirmDialog,
          matching: find.widgetWithText(ShadButton, '승인'),
        )
        .first;
    await tester.tap(confirmButton, warnIfMissed: false);
    await tester.pumpAndSettle();

    expect(approvalRepository.actionInputs, hasLength(1));
    expect(approvalRepository.actionInputs.first.actionId, 1);
  });
}

class _FakeApprovalRepository implements ApprovalRepository {
  Approval? detail;
  Approval? updatedAfterAssign;
  Approval? updatedAfterAction;
  bool canProceedResult = true;
  List<ApprovalAction> actions = const [];

  final List<ApprovalStepAssignmentInput> assignmentInputs = [];
  final List<ApprovalStepActionInput> actionInputs = [];

  @override
  Future<Approval> assignSteps(ApprovalStepAssignmentInput input) async {
    assignmentInputs.add(input);
    return updatedAfterAssign ?? detail!;
  }

  @override
  Future<Approval> fetchDetail(
    int id, {
    bool includeSteps = true,
    bool includeHistories = true,
  }) async {
    return detail!;
  }

  @override
  Future<ApprovalProceedStatus> canProceed(int id) async {
    return ApprovalProceedStatus(approvalId: id, canProceed: canProceedResult);
  }

  @override
  Future<List<ApprovalAction>> listActions({bool activeOnly = true}) async {
    return actions;
  }

  @override
  Future<Approval> performStepAction(ApprovalStepActionInput input) async {
    actionInputs.add(input);
    return updatedAfterAction ?? detail!;
  }

  @override
  Future<PaginatedResult<Approval>> list({
    int page = 1,
    int pageSize = 20,
    int? transactionId,
    int? approvalStatusId,
    int? requestedById,
    List<String>? statusCodes,
    bool includePending = false,
    bool includeHistories = false,
    bool includeSteps = false,
  }) {
    throw UnimplementedError();
  }

  @override
  Future<Approval> submit(ApprovalSubmissionInput input) {
    throw UnimplementedError();
  }

  @override
  Future<Approval> resubmit(ApprovalResubmissionInput input) {
    throw UnimplementedError();
  }

  @override
  Future<Approval> approve(ApprovalDecisionInput input) {
    throw UnimplementedError();
  }

  @override
  Future<Approval> reject(ApprovalDecisionInput input) {
    throw UnimplementedError();
  }

  @override
  Future<Approval> recall(ApprovalRecallInput input) {
    throw UnimplementedError();
  }

  @override
  Future<PaginatedResult<ApprovalHistory>> listHistory({
    required int approvalId,
    int page = 1,
    int pageSize = 20,
    DateTime? from,
    DateTime? to,
    int? actorId,
    int? approvalActionId,
  }) {
    throw UnimplementedError();
  }

  @override
  Future<Approval> create(ApprovalCreateInput input) {
    throw UnimplementedError();
  }

  @override
  Future<Approval> update(ApprovalUpdateInput input) {
    throw UnimplementedError();
  }

  @override
  Future<void> delete(int id) {
    throw UnimplementedError();
  }

  @override
  Future<Approval> restore(int id) {
    throw UnimplementedError();
  }
}

class _FakeApprovalTemplateRepository implements ApprovalTemplateRepository {
  List<ApprovalTemplate> listResult = const [];
  ApprovalTemplate? detailResult;

  @override
  Future<PaginatedResult<ApprovalTemplate>> list({
    int page = 1,
    int pageSize = 20,
    String? query,
    bool? isActive,
  }) async {
    return PaginatedResult(
      items: listResult,
      page: page,
      pageSize: pageSize,
      total: listResult.length,
    );
  }

  @override
  Future<ApprovalTemplate> fetchDetail(
    int id, {
    bool includeSteps = true,
  }) async {
    return detailResult!;
  }

  @override
  Future<ApprovalTemplate> create(
    ApprovalTemplateInput input, {
    List<ApprovalTemplateStepInput> steps = const [],
  }) {
    throw UnimplementedError();
  }

  @override
  Future<ApprovalTemplate> update(
    int id,
    ApprovalTemplateInput input, {
    List<ApprovalTemplateStepInput>? steps,
  }) {
    throw UnimplementedError();
  }

  @override
  Future<void> delete(int id) {
    throw UnimplementedError();
  }

  @override
  Future<ApprovalTemplate> restore(int id) {
    throw UnimplementedError();
  }
}
