import 'dart:ui';

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 '../../../../../helpers/tester_extensions.dart';
import 'package:superport_v2/core/common/models/paginated_result.dart';
import 'package:superport_v2/core/permissions/permission_manager.dart';
import 'package:superport_v2/features/masters/group/domain/entities/group.dart';
import 'package:superport_v2/features/masters/group/domain/repositories/group_repository.dart';
import 'package:superport_v2/features/masters/group_permission/domain/entities/group_permission.dart';
import 'package:superport_v2/features/masters/group_permission/domain/repositories/group_permission_repository.dart';
import 'package:superport_v2/features/masters/user/domain/entities/user.dart';
import 'package:superport_v2/features/masters/user/domain/repositories/user_repository.dart';
import 'package:superport_v2/features/masters/user/presentation/pages/user_page.dart';
import 'package:superport_v2/widgets/components/superport_dialog.dart';

class _MockUserRepository extends Mock implements UserRepository {}

class _MockGroupRepository extends Mock implements GroupRepository {}

class _MockGroupPermissionRepository extends Mock
    implements GroupPermissionRepository {}

class _FakeUserInput extends Fake implements UserInput {}

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

void main() {
  TestWidgetsFlutterBinding.ensureInitialized();

  setUpAll(() {
    registerFallbackValue(_FakeUserInput());
  });

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

  testWidgets('플래그 Off 시 스펙 화면 유지', (tester) async {
    dotenv.testLoad(fileInput: 'FEATURE_USERS_ENABLED=false\n');

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

    expect(find.text('사용자(사원) 관리'), findsOneWidget);
    expect(find.text('테이블 리스트'), findsOneWidget);
  });

  group('플래그 On', () {
    late _MockUserRepository userRepository;
    late _MockGroupRepository groupRepository;
    late _MockGroupPermissionRepository permissionRepository;
    setUp(() {
      dotenv.testLoad(fileInput: 'FEATURE_USERS_ENABLED=true\n');
      userRepository = _MockUserRepository();
      groupRepository = _MockGroupRepository();
      permissionRepository = _MockGroupPermissionRepository();
      GetIt.I.registerLazySingleton<UserRepository>(() => userRepository);
      GetIt.I.registerLazySingleton<GroupRepository>(() => groupRepository);
      GetIt.I.registerLazySingleton<GroupPermissionRepository>(
        () => permissionRepository,
      );

      when(
        () => groupRepository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          query: any(named: 'query'),
          isDefault: any(named: 'isDefault'),
          isActive: any(named: 'isActive'),
          includePermissions: any(named: 'includePermissions'),
          includeEmployees: any(named: 'includeEmployees'),
        ),
      ).thenAnswer(
        (_) async => PaginatedResult<Group>(
          items: [Group(id: 1, groupName: '관리자')],
          page: 1,
          pageSize: 20,
          total: 1,
        ),
      );

      when(
        () => permissionRepository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          groupId: any(named: 'groupId'),
          menuId: any(named: 'menuId'),
          isActive: any(named: 'isActive'),
          includeDeleted: any(named: 'includeDeleted'),
        ),
      ).thenAnswer(
        (_) async => PaginatedResult<GroupPermission>(
          items: [
            GroupPermission(
              id: 1,
              group: GroupPermissionGroup(id: 1, groupName: '관리자'),
              menu: GroupPermissionMenu(
                id: 10,
                menuCode: 'DASHBOARD',
                menuName: '대시보드',
                path: '/dashboard',
              ),
              canCreate: true,
              canRead: true,
            ),
          ],
          page: 1,
          pageSize: 200,
          total: 1,
        ),
      );
    });

    testWidgets('목록 조회 후 테이블 렌더', (tester) async {
      when(
        () => userRepository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          query: any(named: 'query'),
          groupId: any(named: 'groupId'),
          isActive: any(named: 'isActive'),
        ),
      ).thenAnswer(
        (_) async => PaginatedResult<UserAccount>(
          items: [
            UserAccount(
              id: 1,
              employeeNo: 'A001',
              employeeName: '홍길동',
              email: 'hong@superport.com',
              group: UserGroup(id: 1, groupName: '관리자'),
            ),
          ],
          page: 1,
          pageSize: 20,
          total: 1,
        ),
      );

      await tester.pumpWidget(_buildApp(const UserPage()));
      await tester.pumpAndSettle();

      expect(find.text('A001'), findsOneWidget);
      verify(
        () => userRepository.list(
          page: 1,
          pageSize: 20,
          query: null,
          groupId: null,
          isActive: null,
        ),
      ).called(1);
    });

    testWidgets('폼 검증: 필수값 누락', (tester) async {
      when(
        () => userRepository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          query: any(named: 'query'),
          groupId: any(named: 'groupId'),
          isActive: any(named: 'isActive'),
        ),
      ).thenAnswer(
        (_) async => PaginatedResult<UserAccount>(
          items: const [],
          page: 1,
          pageSize: 20,
          total: 0,
        ),
      );

      await tester.pumpWidget(_buildApp(const UserPage()));
      await tester.pumpAndSettle();

      await tester.tap(find.text('신규 등록'));
      await tester.pumpAndSettle();
      await tester.tapShadButton('등록', settle: false);
      await tester.pump();

      expect(find.text('사번을 입력하세요.'), findsOneWidget);
      expect(find.text('성명을 입력하세요.'), findsOneWidget);
    });

    testWidgets('신규 등록 성공', (tester) async {
      final view = tester.view;
      view.physicalSize = const Size(1600, 900);
      view.devicePixelRatio = 1.0;
      addTearDown(() {
        view.resetPhysicalSize();
        view.resetDevicePixelRatio();
      });

      var listCallCount = 0;
      when(
        () => userRepository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          query: any(named: 'query'),
          groupId: any(named: 'groupId'),
          isActive: any(named: 'isActive'),
        ),
      ).thenAnswer((_) async {
        listCallCount += 1;
        if (listCallCount == 1) {
          return PaginatedResult<UserAccount>(
            items: const [],
            page: 1,
            pageSize: 20,
            total: 0,
          );
        }
        return PaginatedResult<UserAccount>(
          items: [
            UserAccount(
              id: 2,
              employeeNo: 'A010',
              employeeName: '신규 사용자',
              email: 'new@superport.com',
              group: UserGroup(id: 1, groupName: '관리자'),
            ),
          ],
          page: 1,
          pageSize: 20,
          total: 1,
        );
      });

      UserInput? capturedInput;
      when(() => userRepository.create(any())).thenAnswer((invocation) async {
        capturedInput = invocation.positionalArguments.first as UserInput;
        return UserAccount(
          id: 2,
          employeeNo: capturedInput!.employeeNo,
          employeeName: capturedInput!.employeeName,
          group: UserGroup(id: capturedInput!.groupId, groupName: '관리자'),
        );
      });

      await tester.pumpWidget(_buildApp(const UserPage()));
      await tester.pumpAndSettle();

      await tester.tap(find.text('신규 등록'));
      await tester.pumpAndSettle();

      final dialog = find.byType(SuperportDialog);
      await tester.enterText(
        find.byKey(const ValueKey('user_form_employee')),
        'A010',
      );
      await tester.enterText(
        find.byKey(const ValueKey('user_form_name')),
        '신규 사용자',
      );
      await tester.enterText(
        find.byKey(const ValueKey('user_form_password')),
        'Aa1!abcd',
      );
      await tester.enterText(
        find.byKey(const ValueKey('user_form_email')),
        'new@superport.com',
      );
      await tester.enterText(
        find.byKey(const ValueKey('user_form_phone')),
        '010-1111-2222',
      );

      final selectFinder = find.descendant(
        of: dialog,
        matching: find.byType(ShadSelect<int?>),
      );
      final selectElement = tester.element(selectFinder);
      final renderBox = selectElement.renderObject as RenderBox;
      final globalCenter = renderBox.localToGlobal(
        renderBox.size.center(Offset.zero),
      );
      await tester.tapAt(globalCenter);
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 200));
      final adminOption = find.text('관리자', skipOffstage: false);
      expect(adminOption, findsWidgets);
      await tester.tap(adminOption.first, warnIfMissed: false);
      await tester.pumpAndSettle();

      await tester.tapShadButton('등록');

      expect(capturedInput, isNotNull);
      expect(capturedInput?.employeeNo, 'A010');
      expect(capturedInput?.password, 'Aa1!abcd');
      expect(capturedInput?.forcePasswordChange, isTrue);
      expect(capturedInput?.email, 'new@superport.com');
      expect(capturedInput?.mobileNo, '010-1111-2222');
      expect(find.byType(SuperportDialog), findsNothing);
      expect(find.text('A010'), findsOneWidget);
      verify(() => userRepository.create(any())).called(1);
    });

    testWidgets('비밀번호 정책을 위반하면 에러가 노출되고 생성이 중단된다', (tester) async {
      final view = tester.view;
      view.physicalSize = const Size(1280, 800);
      view.devicePixelRatio = 1.0;
      addTearDown(() {
        view.resetPhysicalSize();
        view.resetDevicePixelRatio();
      });

      when(
        () => userRepository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          query: any(named: 'query'),
          groupId: any(named: 'groupId'),
          isActive: any(named: 'isActive'),
        ),
      ).thenAnswer(
        (_) async => PaginatedResult<UserAccount>(
          items: const [],
          page: 1,
          pageSize: 20,
          total: 0,
        ),
      );

      await tester.pumpWidget(_buildApp(const UserPage()));
      await tester.pumpAndSettle();

      await tester.tap(find.text('신규 등록'));
      await tester.pumpAndSettle();

      final dialog = find.byType(SuperportDialog);
      await tester.enterText(
        find.byKey(const ValueKey('user_form_employee')),
        'A011',
      );
      await tester.enterText(
        find.byKey(const ValueKey('user_form_name')),
        '정책 위반',
      );
      await tester.enterText(
        find.byKey(const ValueKey('user_form_password')),
        'abc',
      );
      await tester.enterText(
        find.byKey(const ValueKey('user_form_email')),
        'invalid@superport.com',
      );
      await tester.enterText(
        find.byKey(const ValueKey('user_form_phone')),
        '010-3333-4444',
      );

      final selectFinder = find.descendant(
        of: dialog,
        matching: find.byType(ShadSelect<int?>),
      );
      final selectElement = tester.element(selectFinder);
      final renderBox = selectElement.renderObject as RenderBox;
      final globalCenter = renderBox.localToGlobal(
        renderBox.size.center(Offset.zero),
      );
      await tester.tapAt(globalCenter);
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 200));
      await tester.tap(find.text('관리자', skipOffstage: false).first);
      await tester.pumpAndSettle();

      await tester.tapShadButton('등록', settle: false);
      await tester.pump();

      expect(find.textContaining('최소 8자 이상 입력해야 합니다.'), findsOneWidget);
      verifyNever(() => userRepository.create(any()));
    });

    testWidgets('상세 팝업에서 비밀번호 재설정 액션 수행', (tester) async {
      final view = tester.view;
      view.physicalSize = const Size(1280, 800);
      view.devicePixelRatio = 1.0;
      addTearDown(() {
        view.resetPhysicalSize();
        view.resetDevicePixelRatio();
      });

      when(
        () => userRepository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          query: any(named: 'query'),
          groupId: any(named: 'groupId'),
          isActive: any(named: 'isActive'),
        ),
      ).thenAnswer(
        (_) async => PaginatedResult<UserAccount>(
          items: [
            UserAccount(
              id: 1,
              employeeNo: 'A001',
              employeeName: '홍길동',
              group: UserGroup(id: 1, groupName: '관리자'),
            ),
          ],
          page: 1,
          pageSize: 20,
          total: 1,
        ),
      );
      when(() => userRepository.resetPassword(any())).thenAnswer(
        (_) async => UserAccount(
          id: 1,
          employeeNo: 'A001',
          employeeName: '홍길동',
          group: UserGroup(id: 1, groupName: '관리자'),
        ),
      );

      await tester.pumpWidget(_buildApp(const UserPage()));
      await tester.pumpAndSettle();

      final rowFinder = find.text('A001');
      expect(rowFinder, findsOneWidget);

      final rowRect = tester.getRect(rowFinder);
      await tester.tapAt(rowRect.center, kind: PointerDeviceKind.mouse);
      await tester.pumpAndSettle();

      expect(find.byType(SuperportDialog), findsOneWidget);

      await tester.tap(find.text('보안'));
      await tester.pumpAndSettle();

      final resetButton = find.widgetWithText(ShadButton, '비밀번호 재설정').first;
      await tester.ensureVisible(resetButton);
      await tester.tap(resetButton, warnIfMissed: false);
      await tester.pumpAndSettle();

      expect(find.text('비밀번호 재설정'), findsWidgets);

      final confirmButton = find.widgetWithText(ShadButton, '재설정').last;
      await tester.ensureVisible(confirmButton);
      await tester.tap(confirmButton, warnIfMissed: false);
      await tester.pumpAndSettle();

      verify(() => userRepository.resetPassword(1)).called(1);
      expect(find.byType(SuperportDialog), findsNothing);
    });
  });
}
