import 'package:superport_v2/core/navigation/menu_catalog.dart';
import 'package:superport_v2/core/permissions/permission_manager.dart';
import 'package:superport_v2/core/permissions/permission_resources.dart';

import '../domain/entities/group_permission.dart';
import '../domain/mappers/group_permission_mapper.dart';
import '../domain/repositories/group_permission_repository.dart';

/// 서버에서 그룹 메뉴 권한을 조회해 [PermissionManager]에 반영하는 동기화기.
class PermissionSynchronizer {
  PermissionSynchronizer({
    required GroupPermissionRepository repository,
    required PermissionManager manager,
    MenuCatalog? menuCatalog,
    this.pageSize = 200,
  }) : _repository = repository,
       _manager = manager,
       _menuCatalog = menuCatalog;

  final GroupPermissionRepository _repository;
  final PermissionManager _manager;
  final MenuCatalog? _menuCatalog;
  final int pageSize;

  /// 지정한 [groupId]의 메뉴 권한을 조회해 [PermissionManager]에 적용한다.
  Future<void> syncForGroup(int groupId) async {
    final permissionMap = await fetchPermissionMap(groupId);
    _manager.applyServerPermissions(permissionMap);
  }

  /// 지정한 [groupId]의 메뉴 권한을 조회해 맵 형태로 반환한다.
  Future<Map<String, Set<PermissionAction>>> fetchPermissionMap(
    int groupId,
  ) async {
    final collected = await _collectPermissions(groupId);
    if (collected.isEmpty) {
      return const {};
    }
    final synchronized = await _alignMenuRoutes(collected);
    return buildPermissionMap(synchronized);
  }

  Future<List<GroupPermission>> _collectPermissions(int groupId) async {
    final collected = <GroupPermission>[];
    var page = 1;

    while (true) {
      final response = await _repository.list(
        page: page,
        pageSize: pageSize,
        groupId: groupId,
        includeDeleted: false,
        isActive: true,
      );
      collected.addAll(response.items);

      final currentPageSize = response.pageSize == 0
          ? response.items.length
          : response.pageSize;
      if (currentPageSize == 0) {
        break;
      }
      final fetched = page * currentPageSize;
      if (fetched >= response.total) {
        break;
      }
      page += 1;
    }

    return collected;
  }

  Future<List<GroupPermission>> _alignMenuRoutes(
    List<GroupPermission> permissions,
  ) async {
    final catalog = _menuCatalog;
    if (catalog == null || permissions.isEmpty) {
      return permissions;
    }
    final codeToRoute = await _buildMenuRouteMap(catalog);
    if (codeToRoute.isEmpty) {
      return permissions;
    }
    var mutated = false;
    final adjusted = <GroupPermission>[];
    for (final permission in permissions) {
      final resolvedPath = _resolveRouteForPermission(
        permission.menu.menuCode,
        permission.menu.path,
        codeToRoute,
      );
      if (resolvedPath == null) {
        adjusted.add(permission);
        continue;
      }
      final normalizedCurrent = PermissionResources.normalize(
        permission.menu.path ?? '',
      );
      final normalizedResolved = PermissionResources.normalize(resolvedPath);
      if (normalizedCurrent == normalizedResolved) {
        adjusted.add(permission);
        continue;
      }
      final updatedMenu = GroupPermissionMenu(
        id: permission.menu.id,
        menuCode: permission.menu.menuCode,
        menuName: permission.menu.menuName,
        path: resolvedPath,
      );
      adjusted.add(permission.copyWith(menu: updatedMenu));
      mutated = true;
    }
    return mutated ? adjusted : permissions;
  }

  Future<Map<String, String>> _buildMenuRouteMap(MenuCatalog catalog) async {
    try {
      final menus = await catalog.ensureLoaded();
      final map = <String, String>{};
      for (final menu in menus) {
        final code = menu.menuCode;
        final path = menu.path;
        if (code.isEmpty || path == null || path.isEmpty) {
          continue;
        }
        map[code] = path;
      }
      return map;
    } catch (_) {
      return const {};
    }
  }

  String? _resolveRouteForPermission(
    String menuCode,
    String? currentPath,
    Map<String, String> codeToRoute,
  ) {
    final expectedRaw = codeToRoute[menuCode];
    final normalizedCurrent = PermissionResources.normalize(currentPath ?? '');
    if (normalizedCurrent.isNotEmpty) {
      if (expectedRaw == null || expectedRaw.isEmpty) {
        return null;
      }
      final normalizedExpected = PermissionResources.normalize(expectedRaw);
      return normalizedCurrent == normalizedExpected ? null : expectedRaw;
    }
    if (expectedRaw == null || expectedRaw.isEmpty) {
      return null;
    }
    return expectedRaw;
  }
}
