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/group_permission/presentation/controllers/group_permission_controller.dart';
import 'package:superport_v2/features/masters/menu/domain/entities/menu.dart';
import 'package:superport_v2/features/masters/menu/domain/repositories/menu_repository.dart';

class _MockPermissionRepository extends Mock
    implements GroupPermissionRepository {}

class _MockGroupRepository extends Mock implements GroupRepository {}

class _MockMenuRepository extends Mock implements MenuRepository {}

class _FakeGroupPermissionInput extends Fake implements GroupPermissionInput {}

void main() {
  late GroupPermissionController controller;
  late _MockPermissionRepository permissionRepository;
  late _MockGroupRepository groupRepository;
  late _MockMenuRepository menuRepository;
  late PermissionManager permissionManager;

  final samplePermission = GroupPermission(
    id: 1,
    group: GroupPermissionGroup(id: 1, groupName: '관리자'),
    menu: GroupPermissionMenu(
      id: 10,
      menuCode: 'DASHBOARD',
      menuName: '대시보드',
      path: '/dashboard',
    ),
    canCreate: true,
    canRead: true,
    canUpdate: false,
    canDelete: false,
  );

  PaginatedResult<GroupPermission> createResult(List<GroupPermission> items) {
    return PaginatedResult<GroupPermission>(
      items: items,
      page: 1,
      pageSize: 20,
      total: items.length,
    );
  }

  setUpAll(() {
    registerFallbackValue(_FakeGroupPermissionInput());
  });

  setUp(() {
    permissionRepository = _MockPermissionRepository();
    groupRepository = _MockGroupRepository();
    menuRepository = _MockMenuRepository();
    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 => createResult([samplePermission]));
    controller = GroupPermissionController(
      permissionRepository: permissionRepository,
      groupRepository: groupRepository,
      menuRepository: menuRepository,
      permissionManager: permissionManager,
    );
  });

  group('loadGroups/loadMenus', () {
    test('그룹 목록을 로드한다', () 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);
    });

    test('메뉴 목록을 로드한다', () async {
      when(
        () => menuRepository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          query: any(named: 'query'),
          parentId: any(named: 'parentId'),
          isActive: any(named: 'isActive'),
          includeDeleted: any(named: 'includeDeleted'),
        ),
      ).thenAnswer(
        (_) async => PaginatedResult<MenuItem>(
          items: [MenuItem(id: 10, menuCode: 'MENU001', menuName: '대시보드')],
          page: 1,
          pageSize: 20,
          total: 1,
        ),
      );

      await controller.loadMenus();

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

  group('fetch', () {
    setUp(() {
      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 => createResult([samplePermission]));
    });

    test('정상 조회 시 데이터를 보관한다', () async {
      await controller.fetch();

      expect(controller.result?.items, isNotEmpty);
      expect(controller.errorMessage, isNull);
    });

    test('필터 값을 전달한다', () async {
      controller.updateGroupFilter(2);
      controller.updateMenuFilter(5);
      controller.updateStatusFilter(GroupPermissionStatusFilter.inactiveOnly);
      controller.updateIncludeDeleted(true);

      await controller.fetch(page: 3);

      verify(
        () => permissionRepository.list(
          page: 3,
          pageSize: 20,
          groupId: 2,
          menuId: 5,
          isActive: false,
          includeDeleted: true,
        ),
      ).called(1);
    });

    test('에러 발생 시 errorMessage에 저장', () async {
      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'),
        ),
      ).thenThrow(Exception('fail'));

      await controller.fetch();

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

  test('필터 업데이트 메서드', () {
    controller.updateGroupFilter(3);
    controller.updateMenuFilter(7);
    controller.updateStatusFilter(GroupPermissionStatusFilter.activeOnly);
    controller.updateIncludeDeleted(true);

    expect(controller.groupFilter, 3);
    expect(controller.menuFilter, 7);
    expect(controller.statusFilter, GroupPermissionStatusFilter.activeOnly);
    expect(controller.includeDeleted, isTrue);
  });

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

    final input = GroupPermissionInput(groupId: 1, menuId: 2);

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

      final created = await controller.create(input);

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

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

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

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

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

      final result = await controller.delete(1);

      expect(result, isTrue);
      verify(() => permissionRepository.delete(1)).called(1);
    });

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

      final restored = await controller.restore(1);

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

    test('restore 이후 includeDeleted 상태를 유지하며 재조회한다', () async {
      controller.updateIncludeDeleted(true);
      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 => createResult([samplePermission]));
      when(
        () => permissionRepository.restore(any()),
      ).thenAnswer((_) async => samplePermission);

      await controller.fetch();
      await controller.restore(1);

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