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/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/domain/usecases/apply_approval_template_use_case.dart';
import 'package:superport_v2/features/approvals/domain/usecases/save_approval_template_use_case.dart';
import 'package:superport_v2/features/approvals/template/presentation/pages/approval_template_page.dart';
import 'package:superport_v2/features/approvals/shared/domain/entities/approval_approver_candidate.dart';
import 'package:superport_v2/features/approvals/shared/domain/repositories/approval_approver_repository.dart';
import 'package:superport_v2/features/approvals/shared/widgets/approver_autocomplete_field.dart';
import 'package:superport_v2/features/auth/application/auth_service.dart';
import 'package:superport_v2/features/auth/domain/entities/auth_session.dart';
import 'package:superport_v2/features/auth/domain/entities/authenticated_user.dart';

class _MockApprovalTemplateRepository extends Mock
    implements ApprovalTemplateRepository {}

class _MockApprovalRepository extends Mock implements ApprovalRepository {}

class _FakeTemplateInput extends Fake implements ApprovalTemplateInput {}

class _FakeTemplateStepInput extends Fake
    implements ApprovalTemplateStepInput {}

class _MockAuthService extends Mock implements AuthService {}

Widget _buildApp(Widget child) {
  return MaterialApp(
    home: ShadTheme(
      data: ShadThemeData(
        colorScheme: const ShadSlateColorScheme.light(),
        brightness: Brightness.light,
      ),
      child: Scaffold(body: child),
    ),
  );
}

void main() {
  TestWidgetsFlutterBinding.ensureInitialized();

  setUpAll(() {
    registerFallbackValue(_FakeTemplateInput());
    registerFallbackValue(_FakeTemplateStepInput());
    registerFallbackValue(<ApprovalTemplateStepInput>[]);
  });

  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 ApprovalTemplatePage()));
    await tester.pump();

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

  group('플래그 On', () {
    late _MockApprovalTemplateRepository repository;
    late _MockApprovalRepository approvalRepository;

    setUp(() {
      dotenv.testLoad(fileInput: 'FEATURE_APPROVALS_ENABLED=true\n');
      repository = _MockApprovalTemplateRepository();
      approvalRepository = _MockApprovalRepository();
      GetIt.I.registerLazySingleton<ApprovalTemplateRepository>(
        () => repository,
      );
      GetIt.I.registerLazySingleton<ApprovalRepository>(
        () => approvalRepository,
      );
      GetIt.I.registerLazySingleton<SaveApprovalTemplateUseCase>(
        () => SaveApprovalTemplateUseCase(repository: repository),
      );
      GetIt.I.registerLazySingleton<ApplyApprovalTemplateUseCase>(
        () => ApplyApprovalTemplateUseCase(
          templateRepository: repository,
          approvalRepository: approvalRepository,
        ),
      );
      GetIt.I.registerLazySingleton<ApprovalApproverRepository>(
        () => _FakeApproverRepository({
          33: ApprovalApproverCandidate(
            id: 33,
            employeeNo: 'EMP033',
            name: '테스트 승인자',
            team: 'QA팀',
          ),
          21: ApprovalApproverCandidate(
            id: 21,
            employeeNo: 'E001',
            name: '최승인',
            team: '결재팀',
          ),
        }),
      );
      final authService = _MockAuthService();
      when(() => authService.session).thenReturn(
        AuthSession(
          accessToken: 'test-access',
          refreshToken: 'test-refresh',
          expiresAt: DateTime(2099, 1, 1),
          user: const AuthenticatedUser(
            id: 99,
            name: '테스트 사용자',
            employeeNo: 'E999',
            email: 'test@example.com',
          ),
        ),
      );
      GetIt.I.registerSingleton<AuthService>(authService);
    });

    ApprovalTemplate buildTemplate({bool isActive = true}) {
      return ApprovalTemplate(
        id: 10,
        code: 'AP_INBOUND',
        name: '입고 템플릿',
        description: '입고 2단계',
        note: '기본 템플릿',
        isActive: isActive,
        createdBy: null,
        createdAt: DateTime(2024, 4, 1, 9),
        updatedAt: DateTime(2024, 4, 2, 9),
        steps: [
          ApprovalTemplateStep(
            id: 1,
            stepOrder: 1,
            approver: ApprovalTemplateApprover(
              id: 21,
              employeeNo: 'E001',
              name: '최승인',
            ),
            note: '확인',
          ),
        ],
      );
    }

    testWidgets('목록을 조회해 테이블로 렌더링한다', (tester) async {
      when(
        () => repository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          query: any(named: 'query'),
          isActive: any(named: 'isActive'),
        ),
      ).thenAnswer(
        (_) async => PaginatedResult<ApprovalTemplate>(
          items: [buildTemplate()],
          page: 1,
          pageSize: 20,
          total: 1,
        ),
      );

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

      expect(find.text('AP_INBOUND'), findsWidgets);
      expect(find.text('입고 템플릿'), findsWidgets);
      expect(find.textContaining('1. 최승인'), findsOneWidget);

      verify(
        () =>
            repository.list(page: 1, pageSize: 20, query: null, isActive: null),
      ).called(1);
    });

    testWidgets('생성 플로우에서 create 호출 후 목록을 새로고침한다', (tester) async {
      when(
        () => repository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          query: any(named: 'query'),
          isActive: any(named: 'isActive'),
        ),
      ).thenAnswer(
        (_) async => PaginatedResult<ApprovalTemplate>(
          items: const [],
          page: 1,
          pageSize: 20,
          total: 0,
        ),
      );

      when(
        () => repository.create(any(), steps: any(named: 'steps')),
      ).thenAnswer((_) async => buildTemplate().copyWith(name: '신규 템플릿'));

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

      await tester.tap(find.text('템플릿 생성'));
      await tester.pumpAndSettle();

      await tester.enterText(
        find.byKey(const ValueKey('template_form_name')),
        '신규 템플릿',
      );
      final approverField = tester.widget<ApprovalApproverAutocompleteField>(
        find.byType(ApprovalApproverAutocompleteField).first,
      );
      approverField.idController.text = '33';
      await tester.pump();

      final createButtonFinder = find.widgetWithText(ShadButton, '등록').last;
      final createButton = tester.widget<ShadButton>(createButtonFinder);
      createButton.onPressed?.call();
      await tester.pump();
      await tester.pumpAndSettle();

      verify(
        () => repository.create(any(), steps: any(named: 'steps')),
      ).called(1);
      expect(find.textContaining('템플릿 "신규 템플릿"'), findsOneWidget);
    });

    testWidgets('보기 버튼을 눌러 템플릿 단계를 미리본다', (tester) async {
      final template = buildTemplate();

      when(
        () => repository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          query: any(named: 'query'),
          isActive: any(named: 'isActive'),
        ),
      ).thenAnswer(
        (_) async => PaginatedResult<ApprovalTemplate>(
          items: [template],
          page: 1,
          pageSize: 20,
          total: 1,
        ),
      );

      when(
        () => repository.fetchDetail(template.id, includeSteps: true),
      ).thenAnswer((_) async => template);

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

      await tester.tap(find.text(template.name));
      await tester.pump();
      await tester.pumpAndSettle();

      expect(find.text(template.name), findsWidgets);
      final detailTabsState =
          tester.state(find.byWidgetPredicate((widget) => widget is ShadTabs))
              as dynamic;
      detailTabsState.controller.select('steps');
      await tester.pump();
      await tester.pumpAndSettle();
      expect(find.textContaining('사번 E001'), findsOneWidget);

      verify(
        () => repository.fetchDetail(template.id, includeSteps: true),
      ).called(1);

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

    testWidgets('수정 플로우에서 fetchDetail 후 update를 호출한다', (tester) async {
      final activeTemplate = buildTemplate();

      when(
        () => repository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          query: any(named: 'query'),
          isActive: any(named: 'isActive'),
        ),
      ).thenAnswer(
        (_) async => PaginatedResult<ApprovalTemplate>(
          items: [activeTemplate],
          page: 1,
          pageSize: 20,
          total: 1,
        ),
      );

      when(
        () => repository.fetchDetail(
          any(),
          includeSteps: any(named: 'includeSteps'),
        ),
      ).thenAnswer((_) async => activeTemplate);

      when(
        () => repository.update(any(), any(), steps: any(named: 'steps')),
      ).thenAnswer((_) async => activeTemplate.copyWith(name: '수정된 템플릿'));

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

      expect(find.text('입고 템플릿'), findsOneWidget);
      verify(
        () =>
            repository.list(page: 1, pageSize: 20, query: null, isActive: null),
      ).called(1);

      await tester.tap(find.text('입고 템플릿'));
      await tester.pump();
      await tester.pumpAndSettle();

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

      await tester.enterText(
        find.byKey(const ValueKey('template_form_name')),
        '수정된 템플릿',
      );

      final saveButtonFinder = find.widgetWithText(ShadButton, '저장').last;
      final saveButton = tester.widget<ShadButton>(saveButtonFinder);
      saveButton.onPressed?.call();
      await tester.pump();
      await tester.pumpAndSettle();

      verify(() => repository.fetchDetail(10, includeSteps: true)).called(1);
      verify(
        () => repository.update(10, any(), steps: any(named: 'steps')),
      ).called(1);

      expect(find.text('템플릿 "수정된 템플릿"을(를) 수정했습니다.'), findsOneWidget);
    });
  });
}

class _FakeApproverRepository implements ApprovalApproverRepository {
  _FakeApproverRepository(this._candidates);

  final Map<int, ApprovalApproverCandidate> _candidates;

  @override
  Future<ApprovalApproverCandidate?> fetchById(int id) async {
    return _candidates[id];
  }

  @override
  Future<List<ApprovalApproverCandidate>> search({
    required String keyword,
    int limit = 20,
  }) async {
    final lower = keyword.trim().toLowerCase();
    if (lower.isEmpty) {
      return _candidates.values.toList(growable: false);
    }
    return _candidates.values
        .where(
          (candidate) =>
              candidate.name.toLowerCase().contains(lower) ||
              candidate.employeeNo.toLowerCase().contains(lower) ||
              candidate.id.toString().contains(lower),
        )
        .toList(growable: false);
  }

  @override
  Future<List<ApprovalApproverCandidate>> listInitial({int limit = 20}) async {
    return _candidates.values.take(limit).toList(growable: false);
  }
}
