import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get_it/get_it.dart';
import 'package:go_router/go_router.dart';
import 'package:mocktail/mocktail.dart';

import 'package:shadcn_ui/shadcn_ui.dart';

import 'package:superport_v2/core/services/token_storage.dart';
import 'package:superport_v2/core/common/models/paginated_result.dart';
import 'package:superport_v2/core/constants/app_sections.dart';
import 'package:superport_v2/core/permissions/permission_manager.dart';
import 'package:superport_v2/core/permissions/permission_resources.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';
import 'package:superport_v2/features/auth/domain/entities/login_request.dart';
import 'package:superport_v2/features/auth/domain/repositories/auth_repository.dart';
import 'package:superport_v2/features/login/presentation/pages/login_page.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';

class _MockGroupRepository extends Mock implements GroupRepository {}

class _MockGroupPermissionRepository extends Mock
    implements GroupPermissionRepository {}

class _MockAuthRepository extends Mock implements AuthRepository {}

class _FakeTokenStorage implements TokenStorage {
  String? _accessToken;
  String? _refreshToken;

  @override
  Future<void> clear() async {
    _accessToken = null;
    _refreshToken = null;
  }

  @override
  Future<String?> readAccessToken() async => _accessToken;

  @override
  Future<String?> readRefreshToken() async => _refreshToken;

  @override
  Future<void> writeAccessToken(String? token) async {
    _accessToken = token;
  }

  @override
  Future<void> writeRefreshToken(String? token) async {
    _refreshToken = token;
  }
}

void main() {
  TestWidgetsFlutterBinding.ensureInitialized();

  setUpAll(() {
    registerFallbackValue(
      LoginRequest(identifier: '', password: '', rememberMe: false),
    );
  });

  late _MockAuthRepository authRepository;
  late _FakeTokenStorage tokenStorage;
  late AuthService authService;

  setUp(() {
    authRepository = _MockAuthRepository();
    tokenStorage = _FakeTokenStorage();
    authService = AuthService(
      repository: authRepository,
      tokenStorage: tokenStorage,
    );
    final sampleSession = AuthSession(
      accessToken: 'access-token',
      refreshToken: 'refresh-token',
      expiresAt: DateTime.now().add(const Duration(hours: 1)),
      user: const AuthenticatedUser(id: 1, name: '테스터', phone: null),
      permissions: const [],
    );

    GetIt.I.registerSingleton<AuthService>(authService);

    when(
      () => authRepository.login(any()),
    ).thenAnswer((_) async => sampleSession);
    when(() => authRepository.refresh(any())).thenThrow(UnimplementedError());
  });

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

  testWidgets('로그인 성공 시 권한 동기화 후 대시보드로 이동한다', (tester) async {
    final groupRepository = _MockGroupRepository();
    final permissionRepository = _MockGroupPermissionRepository();
    GetIt.I.registerSingleton<GroupRepository>(groupRepository);
    GetIt.I.registerSingleton<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((invocation) async {
      final items = [Group(id: 1, groupName: '관리자')];
      return PaginatedResult<Group>(
        items: items,
        page: 1,
        pageSize: 1,
        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: 'INBOUND',
              menuName: '입고',
              path: '/inventory/inbound',
            ),
            canCreate: true,
            canRead: true,
          ),
        ],
        page: 1,
        pageSize: 200,
        total: 1,
      ),
    );

    final manager = PermissionManager();

    final router = GoRouter(
      initialLocation: loginRoutePath,
      routes: [
        GoRoute(
          path: loginRoutePath,
          builder: (context, state) => const LoginPage(),
        ),
        GoRoute(
          path: dashboardRoutePath,
          builder: (context, state) =>
              const Scaffold(body: Center(child: Text('대시보드'))),
        ),
      ],
    );

    await tester.pumpWidget(
      PermissionScope(
        manager: manager,
        child: ShadApp.router(routerConfig: router),
      ),
    );

    await tester.enterText(find.byType(EditableText).at(0), 'user@superport');
    await tester.enterText(find.byType(EditableText).at(1), 'password');
    await tester.tap(find.text('로그인'));
    await tester.pumpAndSettle();

    expect(
      router.routerDelegate.currentConfiguration.last.matchedLocation,
      dashboardRoutePath,
    );
    expect(
      manager.can(PermissionResources.stockTransactions, PermissionAction.view),
      isTrue,
    );
    expect(
      manager.can(
        PermissionResources.stockTransactions,
        PermissionAction.create,
      ),
      isTrue,
    );
  });

  testWidgets('권한 동기화 실패 시 오류 메시지를 표시한다', (tester) async {
    final groupRepository = _MockGroupRepository();
    final permissionRepository = _MockGroupPermissionRepository();
    GetIt.I.registerSingleton<GroupRepository>(groupRepository);
    GetIt.I.registerSingleton<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'),
      ),
    ).thenThrow(Exception('network error'));

    final manager = PermissionManager();
    final router = GoRouter(
      initialLocation: loginRoutePath,
      routes: [
        GoRoute(
          path: loginRoutePath,
          builder: (context, state) => const LoginPage(),
        ),
        GoRoute(
          path: dashboardRoutePath,
          builder: (context, state) =>
              const Scaffold(body: Center(child: Text('대시보드'))),
        ),
      ],
    );

    await tester.pumpWidget(
      PermissionScope(
        manager: manager,
        child: ShadApp.router(routerConfig: router),
      ),
    );

    await tester.enterText(find.byType(EditableText).at(0), 'user@superport');
    await tester.enterText(find.byType(EditableText).at(1), 'password');
    await tester.tap(find.text('로그인'));
    await tester.pumpAndSettle();

    expect(find.text('권한 정보를 불러오지 못했습니다. 잠시 후 다시 시도하세요.'), findsOneWidget);
    expect(
      router.routerDelegate.currentConfiguration.last.matchedLocation,
      loginRoutePath,
    );
    expect(
      manager.can(PermissionResources.stockTransactions, PermissionAction.view),
      isTrue,
    );
  });
}
