import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get_it/get_it.dart';
import 'package:mocktail/mocktail.dart';
import 'package:shadcn_ui/shadcn_ui.dart';

import 'package:superport_v2/core/common/models/paginated_result.dart';
import 'package:superport_v2/core/permissions/permission_manager.dart';
import 'package:superport_v2/core/permissions/permission_resources.dart';
import 'package:superport_v2/features/approvals/domain/entities/approval.dart';
import 'package:superport_v2/features/approvals/step/domain/entities/approval_step_input.dart';
import 'package:superport_v2/features/approvals/step/domain/entities/approval_step_record.dart';
import 'package:superport_v2/features/approvals/step/domain/repositories/approval_step_repository.dart';
import 'package:superport_v2/features/approvals/step/presentation/pages/approval_step_page.dart';

class _MockApprovalStepRepository extends Mock
    implements ApprovalStepRepository {}

Widget _buildApp(Widget child) {
  final manager = PermissionManager(
    overrides: {
      PermissionResources.approvalSteps: PermissionAction.values.toSet(),
    },
  );
  return MaterialApp(
    home: PermissionScope(
      manager: manager,
      child: ShadTheme(
        data: ShadThemeData(
          colorScheme: const ShadSlateColorScheme.light(),
          brightness: Brightness.light,
        ),
        child: Scaffold(body: child),
      ),
    ),
  );
}

void main() {
  TestWidgetsFlutterBinding.ensureInitialized();

  setUpAll(() {
    registerFallbackValue(
      ApprovalStepInput(approvalId: 1, stepOrder: 1, approverId: 1),
    );
  });

  late _MockApprovalStepRepository repository;

  final record = ApprovalStepRecord(
    approvalId: 10,
    approvalNo: 'APP-2024-0001',
    transactionNo: 'TRX-2024-001',
    templateName: '입고 기본',
    step: ApprovalStep(
      id: 501,
      stepOrder: 1,
      approver: ApprovalApprover(id: 21, employeeNo: 'E001', name: '최승인'),
      status: ApprovalStatus(id: 1, name: '승인대기', color: null),
      assignedAt: DateTime(2024, 4, 1, 9),
      decidedAt: null,
      note: '검토 필요',
    ),
  );

  tearDown(() async {
    await GetIt.I.reset();
    dotenv.clean();
  });

  testWidgets('플래그 Off 시 스펙 페이지를 노출한다', (tester) async {
    dotenv.testLoad(fileInput: 'FEATURE_APPROVALS_ENABLED=false\n');

    await tester.pumpWidget(_buildApp(const ApprovalStepPage()));
    await tester.pump();

    expect(find.text('결재 단계 관리'), findsWidgets);
    expect(find.text('결재 단계 기능 준비 중'), findsOneWidget);
  });

  testWidgets('목록을 렌더링하고 상세 다이얼로그를 연다', (tester) async {
    dotenv.testLoad(fileInput: 'FEATURE_APPROVALS_ENABLED=true\n');
    repository = _MockApprovalStepRepository();
    GetIt.I.registerLazySingleton<ApprovalStepRepository>(() => repository);

    when(
      () => repository.list(
        page: any(named: 'page'),
        pageSize: any(named: 'pageSize'),
        query: any(named: 'query'),
        statusId: any(named: 'statusId'),
        approverId: any(named: 'approverId'),
        approvalId: any(named: 'approvalId'),
      ),
    ).thenAnswer(
      (_) async => PaginatedResult<ApprovalStepRecord>(
        items: [record],
        page: 1,
        pageSize: 20,
        total: 1,
      ),
    );

    when(() => repository.fetchDetail(any())).thenAnswer((_) async => record);

    await tester.pumpWidget(_buildApp(const ApprovalStepPage()));
    await tester.pump();
    await tester.pumpAndSettle();

    expect(find.text('APP-2024-0001'), findsWidgets);
    expect(find.text('최승인'), findsWidgets);

    await tester.tap(find.text('APP-2024-0001'));
    await tester.pump();
    await tester.pumpAndSettle();

    expect(find.text('결재 단계 상세'), findsWidgets);
    expect(find.text('검토 필요'), findsWidgets);
    verify(() => repository.fetchDetail(501)).called(1);

    await tester.tap(find.byTooltip('닫기'));
    await tester.pumpAndSettle();
  });

  testWidgets('단계 추가 다이얼로그에서 저장을 호출한다', (tester) async {
    dotenv.testLoad(fileInput: 'FEATURE_APPROVALS_ENABLED=true\n');
    repository = _MockApprovalStepRepository();
    GetIt.I.registerLazySingleton<ApprovalStepRepository>(() => repository);

    final createdRecord = ApprovalStepRecord(
      approvalId: 12,
      approvalNo: 'APP-2024-0012',
      transactionNo: 'TRX-2024-012',
      templateName: '입고 기본',
      step: ApprovalStep(
        id: 777,
        stepOrder: 2,
        approver: ApprovalApprover(id: 33, employeeNo: 'E033', name: '김승인2'),
        status: ApprovalStatus(id: 1, name: '승인대기', color: null),
        assignedAt: DateTime(2024, 4, 2, 9),
        decidedAt: null,
        note: '신규 단계',
      ),
    );

    when(
      () => repository.list(
        page: any(named: 'page'),
        pageSize: any(named: 'pageSize'),
        query: any(named: 'query'),
        statusId: any(named: 'statusId'),
        approverId: any(named: 'approverId'),
        approvalId: any(named: 'approvalId'),
      ),
    ).thenAnswer(
      (_) async => PaginatedResult<ApprovalStepRecord>(
        items: [record],
        page: 1,
        pageSize: 20,
        total: 1,
      ),
    );

    when(() => repository.create(any())).thenAnswer((_) async => createdRecord);

    await tester.pumpWidget(_buildApp(const ApprovalStepPage()));
    await tester.pump();
    await tester.pumpAndSettle();

    await tester.tap(find.byKey(const ValueKey('approval_step_create')));
    await tester.pumpAndSettle();

    await tester.enterText(
      find.byKey(const ValueKey('step_form_approval_id')),
      '12',
    );
    await tester.enterText(
      find.byKey(const ValueKey('step_form_step_order')),
      '2',
    );
    await tester.enterText(
      find.byKey(const ValueKey('step_form_approver_id')),
      '33',
    );
    await tester.enterText(
      find.byKey(const ValueKey('step_form_note')),
      '신규 단계',
    );

    await tester.tap(find.byKey(const ValueKey('step_form_submit')));
    await tester.pump();
    await tester.pumpAndSettle();

    verify(() => repository.create(any())).called(1);
    expect(find.text('결재번호 APP-2024-0012 단계가 추가되었습니다.'), findsOneWidget);
  });

  testWidgets('상세 다이얼로그에서 단계를 수정한다', (tester) async {
    dotenv.testLoad(fileInput: 'FEATURE_APPROVALS_ENABLED=true\n');
    repository = _MockApprovalStepRepository();
    GetIt.I.registerLazySingleton<ApprovalStepRepository>(() => repository);

    final updatedRecord = ApprovalStepRecord(
      approvalId: record.approvalId,
      approvalNo: record.approvalNo,
      transactionNo: record.transactionNo,
      templateName: record.templateName,
      step: ApprovalStep(
        id: record.step.id,
        stepOrder: 2,
        approver: ApprovalApprover(id: 30, employeeNo: 'E030', name: '박수정'),
        status: record.step.status,
        assignedAt: record.step.assignedAt,
        decidedAt: record.step.decidedAt,
        note: '수정됨',
      ),
    );

    when(
      () => repository.list(
        page: any(named: 'page'),
        pageSize: any(named: 'pageSize'),
        query: any(named: 'query'),
        statusId: any(named: 'statusId'),
        approverId: any(named: 'approverId'),
        approvalId: any(named: 'approvalId'),
      ),
    ).thenAnswer(
      (_) async => PaginatedResult<ApprovalStepRecord>(
        items: [record],
        page: 1,
        pageSize: 20,
        total: 1,
      ),
    );

    when(() => repository.fetchDetail(any())).thenAnswer((_) async => record);
    when(
      () => repository.update(any(), any()),
    ).thenAnswer((_) async => updatedRecord);

    await tester.pumpWidget(_buildApp(const ApprovalStepPage()));
    await tester.pump();
    await tester.pumpAndSettle();

    await tester.tap(find.text('APP-2024-0001'));
    await tester.pump();
    await tester.pumpAndSettle();

    final editTabsState =
        tester.state(find.byWidgetPredicate((widget) => widget is ShadTabs))
            as dynamic;
    editTabsState.controller.select('edit');
    await tester.pump();
    await tester.pumpAndSettle();

    await tester.enterText(
      find.byKey(const ValueKey('approval_step_detail_step_order')),
      '2',
    );
    await tester.enterText(
      find.byKey(const ValueKey('approval_step_detail_approver_id')),
      '30',
    );
    await tester.enterText(
      find.byKey(const ValueKey('approval_step_detail_note')),
      '수정됨',
    );

    final submitButtonFinder = find.byKey(
      const ValueKey('approval_step_detail_submit'),
    );
    final submitButton = tester.widget<ShadButton>(submitButtonFinder);
    submitButton.onPressed?.call();
    await tester.pump();
    await tester.pumpAndSettle();

    verify(() => repository.update(record.step.id!, any())).called(1);
    expect(find.text('결재 단계 정보를 수정했습니다.'), findsOneWidget);
  });

  testWidgets('상세 다이얼로그에서 단계를 삭제한다', (tester) async {
    dotenv.testLoad(fileInput: 'FEATURE_APPROVALS_ENABLED=true\n');
    repository = _MockApprovalStepRepository();
    GetIt.I.registerLazySingleton<ApprovalStepRepository>(() => repository);

    when(
      () => repository.list(
        page: any(named: 'page'),
        pageSize: any(named: 'pageSize'),
        query: any(named: 'query'),
        statusId: any(named: 'statusId'),
        approverId: any(named: 'approverId'),
        approvalId: any(named: 'approvalId'),
      ),
    ).thenAnswer(
      (_) async => PaginatedResult<ApprovalStepRecord>(
        items: [record],
        page: 1,
        pageSize: 20,
        total: 1,
      ),
    );

    when(() => repository.fetchDetail(any())).thenAnswer((_) async => record);
    when(() => repository.delete(any())).thenAnswer((_) async {});

    await tester.pumpWidget(_buildApp(const ApprovalStepPage()));
    await tester.pump();
    await tester.pumpAndSettle();

    await tester.tap(find.text('APP-2024-0001'));
    await tester.pump();
    await tester.pumpAndSettle();

    final deleteTabsState =
        tester.state(find.byWidgetPredicate((widget) => widget is ShadTabs))
            as dynamic;
    deleteTabsState.controller.select('delete');
    await tester.pump();
    await tester.pumpAndSettle();

    final deleteButtonFinder = find.byKey(
      const ValueKey('approval_step_detail_delete'),
    );
    final deleteButton = tester.widget<ShadButton>(deleteButtonFinder);
    deleteButton.onPressed?.call();
    await tester.pump();
    await tester.pumpAndSettle();

    verify(() => repository.delete(501)).called(1);
    expect(find.text('결재 단계를 삭제했습니다.'), findsOneWidget);
  });

  testWidgets('상세 다이얼로그에서 삭제된 단계를 복구한다', (tester) async {
    dotenv.testLoad(fileInput: 'FEATURE_APPROVALS_ENABLED=true\n');
    repository = _MockApprovalStepRepository();
    GetIt.I.registerLazySingleton<ApprovalStepRepository>(() => repository);

    final deletedRecord = record.copyWith(
      step: record.step.copyWith(isDeleted: true),
    );

    when(
      () => repository.list(
        page: any(named: 'page'),
        pageSize: any(named: 'pageSize'),
        query: any(named: 'query'),
        statusId: any(named: 'statusId'),
        approverId: any(named: 'approverId'),
        approvalId: any(named: 'approvalId'),
      ),
    ).thenAnswer(
      (_) async => PaginatedResult<ApprovalStepRecord>(
        items: [deletedRecord],
        page: 1,
        pageSize: 20,
        total: 1,
      ),
    );

    when(
      () => repository.fetchDetail(any()),
    ).thenAnswer((_) async => deletedRecord);
    when(() => repository.restore(any())).thenAnswer((_) async => record);

    await tester.pumpWidget(_buildApp(const ApprovalStepPage()));
    await tester.pump();
    await tester.pumpAndSettle();

    await tester.tap(find.text('APP-2024-0001'));
    await tester.pump();
    await tester.pumpAndSettle();

    final tabsState =
        tester.state(find.byWidgetPredicate((widget) => widget is ShadTabs))
            as dynamic;
    tabsState.controller.select('restore');
    await tester.pump();
    await tester.pumpAndSettle();

    final restoreButtonFinder = find.byKey(
      const ValueKey('approval_step_detail_restore'),
    );
    final restoreButton = tester.widget<ShadButton>(restoreButtonFinder);
    restoreButton.onPressed?.call();
    await tester.pump();
    await tester.pumpAndSettle();

    verify(() => repository.restore(501)).called(1);
    expect(find.text('결재 단계를 복구했습니다.'), findsOneWidget);
  });
}
