import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.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/controllers/user_controller.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 {}

void main() {
  late UserController controller;
  late _MockUserRepository userRepository;
  late _MockGroupRepository groupRepository;
  late _MockGroupPermissionRepository permissionRepository;
  late PermissionManager permissionManager;

  final sampleUser = UserAccount(
    id: 1,
    employeeNo: 'A001',
    employeeName: '홍길동',
    email: 'hong@superport.com',
    group: UserGroup(id: 2, groupName: '관리자'),
  );

  PaginatedResult<UserAccount> createResult({List<UserAccount>? items}) {
    final list = items ?? [sampleUser];
    return PaginatedResult<UserAccount>(
      items: list,
      page: 1,
      pageSize: 20,
      total: list.length,
    );
  }

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

  setUp(() {
    userRepository = _MockUserRepository();
    groupRepository = _MockGroupRepository();
    permissionRepository = _MockGroupPermissionRepository();
    permissionManager = PermissionManager();
    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,
      ),
    );
    controller = UserController(
      userRepository: userRepository,
      groupRepository: groupRepository,
      permissionRepository: permissionRepository,
      permissionManager: permissionManager,
    );
  });

  test('loadGroups 호출 시 그룹 목록 저장', () async {
    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,
      ),
    );

    await controller.loadGroups();

    expect(controller.groups, isNotEmpty);
  });

  group('fetch', () {
    setUp(() {
      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: 2, groupName: '관리자')],
          page: 1,
          pageSize: 20,
          total: 1,
        ),
      );
    });

    test('정상 조회', () 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 => createResult());

      await controller.fetch();

      expect(controller.result?.items, isNotEmpty);
      verify(
        () => userRepository.list(
          page: 1,
          pageSize: 20,
          query: null,
          groupId: null,
          isActive: null,
        ),
      ).called(1);
    });

    test('에러 발생 시 errorMessage 설정', () async {
      when(
        () => userRepository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          query: any(named: 'query'),
          groupId: any(named: 'groupId'),
          isActive: any(named: 'isActive'),
        ),
      ).thenThrow(Exception('fail'));

      await controller.fetch();

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

  test('필터 업데이트', () {
    controller.updateQuery('hong');
    controller.updateGroupFilter(2);
    controller.updateStatusFilter(UserStatusFilter.inactiveOnly);

    expect(controller.query, 'hong');
    expect(controller.groupFilter, 2);
    expect(controller.statusFilter, UserStatusFilter.inactiveOnly);
  });

  group('mutations', () {
    setUp(() {
      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 => createResult());
    });

    final input = UserInput(
      employeeNo: 'A001',
      employeeName: '홍길동',
      groupId: 2,
    );

    test('create 성공', () async {
      when(
        () => userRepository.create(any()),
      ).thenAnswer((_) async => sampleUser);

      final created = await controller.create(input);

      expect(created, isNotNull);
      verify(() => userRepository.create(any())).called(1);
    });

    test('update 성공', () async {
      when(
        () => userRepository.update(any(), any()),
      ).thenAnswer((_) async => sampleUser);

      final updated = await controller.update(1, input);

      expect(updated, isNotNull);
      verify(() => userRepository.update(1, any())).called(1);
    });

    test('delete 성공', () async {
      when(() => userRepository.delete(any())).thenAnswer((_) async {});

      final success = await controller.delete(1);

      expect(success, isTrue);
      verify(() => userRepository.delete(1)).called(1);
    });

    test('delete 이후 권한 동기화를 시도한다', () async {
      when(() => userRepository.delete(any())).thenAnswer((_) async {});

      await controller.fetch();
      await controller.delete(sampleUser.id!);

      verify(
        () => permissionRepository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          groupId: sampleUser.group!.id,
          menuId: any(named: 'menuId'),
          isActive: any(named: 'isActive'),
          includeDeleted: any(named: 'includeDeleted'),
        ),
      ).called(greaterThanOrEqualTo(1));
    });

    test('restore 성공', () async {
      when(
        () => userRepository.restore(any()),
      ).thenAnswer((_) async => sampleUser);

      final restored = await controller.restore(1);

      expect(restored, isNotNull);
      verify(() => userRepository.restore(1)).called(1);
    });

    test('resetPassword 성공', () async {
      when(
        () => userRepository.resetPassword(any()),
      ).thenAnswer((_) async => sampleUser);

      final result = await controller.resetPassword(1);

      expect(result, isNotNull);
      verify(() => userRepository.resetPassword(1)).called(1);
    });
  });
}
