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/presentation/controllers/group_controller.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';

class _MockGroupRepository extends Mock implements GroupRepository {}

class _MockGroupPermissionRepository extends Mock
    implements GroupPermissionRepository {}

class _FakeGroupInput extends Fake implements GroupInput {}

void main() {
  late GroupController controller;
  late _MockGroupRepository repository;

  final sampleGroup = Group(
    id: 1,
    groupName: '관리자',
    description: '전체 권한',
    isDefault: true,
    isActive: true,
    isDeleted: false,
  );

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

  setUpAll(() {
    registerFallbackValue(_FakeGroupInput());
  });

  setUp(() {
    repository = _MockGroupRepository();
    controller = GroupController(repository: repository);
  });

  group('fetch', () {
    test('정상 조회 시 결과가 갱신된다', () async {
      when(
        () => repository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          query: any(named: 'query'),
          isDefault: any(named: 'isDefault'),
          isActive: any(named: 'isActive'),
        ),
      ).thenAnswer((_) async => createResult());

      await controller.fetch();

      expect(controller.isLoading, isFalse);
      expect(controller.errorMessage, isNull);
      expect(controller.result?.items, isNotEmpty);
      verify(
        () => repository.list(
          page: 1,
          pageSize: 20,
          query: null,
          isDefault: null,
          isActive: null,
        ),
      ).called(1);
    });

    test('에러 발생 시 errorMessage에 저장한다', () async {
      when(
        () => repository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          query: any(named: 'query'),
          isDefault: any(named: 'isDefault'),
          isActive: any(named: 'isActive'),
        ),
      ).thenThrow(Exception('fail'));

      await controller.fetch();

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

    test('필터 상태를 파라미터에 반영한다', () async {
      when(
        () => repository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          query: any(named: 'query'),
          isDefault: any(named: 'isDefault'),
          isActive: any(named: 'isActive'),
        ),
      ).thenAnswer((_) async => createResult());

      controller.updateQuery('admin');
      controller.updateDefaultFilter(GroupDefaultFilter.defaultOnly);
      controller.updateStatusFilter(GroupStatusFilter.inactiveOnly);

      await controller.fetch();

      verify(
        () => repository.list(
          page: 1,
          pageSize: 20,
          query: 'admin',
          isDefault: true,
          isActive: false,
        ),
      ).called(1);
    });
  });

  test('필터 업데이트 메서드', () {
    controller.updateQuery('abc');
    controller.updateDefaultFilter(GroupDefaultFilter.nonDefault);
    controller.updateStatusFilter(GroupStatusFilter.activeOnly);

    expect(controller.query, 'abc');
    expect(controller.defaultFilter, GroupDefaultFilter.nonDefault);
    expect(controller.statusFilter, GroupStatusFilter.activeOnly);
  });

  group('permission sync', () {
    late _MockGroupPermissionRepository permissionRepository;
    late PermissionManager permissionManager;

    setUp(() {
      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: const <GroupPermission>[],
          page: 1,
          pageSize: 200,
          total: 0,
        ),
      );
    });

    test('그룹 생성 후 권한 동기화를 시도한다', () async {
      when(() => repository.create(any())).thenAnswer((_) async => sampleGroup);
      when(
        () => repository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          query: any(named: 'query'),
          isDefault: any(named: 'isDefault'),
          isActive: any(named: 'isActive'),
        ),
      ).thenAnswer((_) async => createResult());

      final controllerWithSync = GroupController(
        repository: repository,
        permissionRepository: permissionRepository,
        permissionManager: permissionManager,
      );

      await controllerWithSync.create(GroupInput(groupName: '신규 그룹'));

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

  group('mutations', () {
    setUp(() {
      when(
        () => repository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          query: any(named: 'query'),
          isDefault: any(named: 'isDefault'),
          isActive: any(named: 'isActive'),
        ),
      ).thenAnswer((_) async => createResult());
    });

    final input = GroupInput(groupName: '관리자');

    test('create 성공 시 목록 재조회', () async {
      when(() => repository.create(any())).thenAnswer((_) async => sampleGroup);

      final created = await controller.create(input);

      expect(created, isNotNull);
      verify(() => repository.create(any())).called(1);
      verify(
        () => repository.list(
          page: 1,
          pageSize: 20,
          query: null,
          isDefault: null,
          isActive: null,
        ),
      ).called(greaterThanOrEqualTo(1));
    });

    test('update 성공 시 현재 페이지 유지', () async {
      when(
        () => repository.update(any(), any()),
      ).thenAnswer((_) async => sampleGroup);

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

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

    test('delete 성공 시 true 반환', () async {
      when(() => repository.delete(any())).thenAnswer((_) async {});

      final deleted = await controller.delete(1);

      expect(deleted, isTrue);
      verify(() => repository.delete(1)).called(1);
    });

    test('restore 성공 시 그룹 반환', () async {
      when(
        () => repository.restore(any()),
      ).thenAnswer((_) async => sampleGroup);

      final restored = await controller.restore(1);

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