import 'package:flutter/widgets.dart';

import '../config/environment.dart';
import 'permission_resources.dart';

/// 권한 체크를 위한 액션 종류.
enum PermissionAction { view, create, edit, delete, restore, approve }

/// 기능별 권한을 확인하고 오버라이드를 지원하는 매니저.
class PermissionManager extends ChangeNotifier {
  PermissionManager({Map<String, Set<PermissionAction>>? overrides}) {
    if (overrides != null) {
      updateOverrides(overrides);
    }
  }

  /// 리소스별 임시 권한 집합을 보관한다.
  final Map<String, Set<PermissionAction>> _overrides = {};

  /// 서버에서 내려받은 실제 권한 집합.
  final Map<String, Set<PermissionAction>> _serverPermissions = {};

  /// 지정한 리소스/행동이 허용되는지 여부를 반환한다.
  bool can(String resource, PermissionAction action) {
    final key = _normalize(resource);
    final override = _overrides[key];
    if (override != null) {
      // View 권한은 최소 접근을 허용하기 위해 별도로 처리한다.
      if (override.contains(PermissionAction.view) &&
          action == PermissionAction.view) {
        return true;
      }
      return override.contains(action);
    }

    final server = _serverPermissions[key];
    if (server != null) {
      if (action == PermissionAction.view) {
        return server.contains(PermissionAction.view);
      }
      return server.contains(action);
    }

    if (key.startsWith('scope:')) {
      return false;
    }

    return Environment.hasPermission(key, action.name);
  }

  /// 개발/테스트 환경에서 사용할 임시 오버라이드 값을 설정한다.
  void updateOverrides(Map<String, Set<PermissionAction>> overrides) {
    _overrides
      ..clear()
      ..addAll(
        overrides.map((key, value) => MapEntry(_normalize(key), value.toSet())),
      );
    notifyListeners();
  }

  /// 서버에서 내려온 권한 정보를 적용한다.
  void applyServerPermissions(Map<String, Set<PermissionAction>> permissions) {
    _serverPermissions
      ..clear()
      ..addAll(
        permissions.map(
          (key, value) => MapEntry(_normalize(key), value.toSet()),
        ),
      );
    notifyListeners();
  }

  /// 서버 권한 정보를 초기화한다.
  void clearServerPermissions() {
    if (_serverPermissions.isEmpty) {
      return;
    }
    _serverPermissions.clear();
    notifyListeners();
  }

  String _normalize(String resource) => PermissionResources.normalize(resource);
}

/// 위젯 트리에 [PermissionManager]를 전달하는 Inherited 위젯.
class PermissionScope extends InheritedNotifier<PermissionManager> {
  const PermissionScope({
    super.key,
    required PermissionManager manager,
    required super.child,
  }) : super(notifier: manager);

  /// 현재 빌드 컨텍스트에서 [PermissionManager]를 조회한다.
  static PermissionManager of(BuildContext context) {
    final scope = context.dependOnInheritedWidgetOfExactType<PermissionScope>();
    assert(
      scope != null,
      'PermissionScope.of() called with no PermissionScope ancestor.',
    );
    return scope!.notifier!;
  }
}

/// 권한에 따라 child/fallback을 노출하거나 숨기는 헬퍼 위젯.
class PermissionGate extends StatelessWidget {
  const PermissionGate({
    super.key,
    required this.resource,
    required this.action,
    required this.child,
    this.fallback,
    this.hide = true,
  });

  final String resource;
  final PermissionAction action;
  final Widget child;
  final Widget? fallback;
  final bool hide;

  @override
  Widget build(BuildContext context) {
    final allowed = PermissionScope.of(context).can(resource, action);
    if (allowed) {
      return child;
    }
    if (hide) {
      return fallback ?? const SizedBox.shrink();
    }
    return IgnorePointer(
      ignoring: true,
      child: Opacity(opacity: 0.4, child: fallback ?? child),
    );
  }
}

extension PermissionActionKey on PermissionAction {
  /// GoRouter 등에서 사용할 문자열 키.
  String get key => name;
}
