import 'dart:async';

import 'package:flutter/widgets.dart';

import '../common/utils/pagination_utils.dart';
import '../network/failure.dart';
import '../../features/masters/menu/domain/entities/menu.dart';
import '../../features/masters/menu/domain/repositories/menu_repository.dart';

/// 메뉴 목록을 한 번만 로드해 전역에서 재사용하도록 제공하는 카탈로그.
///
/// - 메뉴 테이블 전체를 페이지네이션 API로 받아 캐시에 저장한다.
/// - AppShell/권한 설정 화면 등에서 공통 데이터를 구독해 재렌더링 비용을 줄인다.
class MenuCatalog extends ChangeNotifier {
  MenuCatalog({required MenuRepository repository}) : _repository = repository;

  final MenuRepository _repository;

  List<MenuItem> _menus = const [];
  bool _isLoading = false;
  bool _initialized = false;
  String? _errorMessage;
  Completer<void>? _pendingLoad;

  /// 최신 메뉴 목록.
  List<MenuItem> get menus => _menus;

  /// 현재 로딩 상태.
  bool get isLoading => _isLoading;

  /// 최초 로드 여부.
  bool get isInitialized => _initialized;

  /// 마지막 로드 실패 메시지.
  String? get errorMessage => _errorMessage;

  /// 캐시된 메뉴를 반환하며 필요 시 자동으로 로딩한다.
  Future<List<MenuItem>> ensureLoaded({bool forceRefresh = false}) async {
    if (_initialized && !forceRefresh) {
      return _menus;
    }
    await refresh();
    return _menus;
  }

  /// 백엔드에서 메뉴를 다시 불러와 캐시를 갱신한다.
  Future<void> refresh() async {
    if (_pendingLoad != null) {
      await _pendingLoad!.future;
      if (_errorMessage != null) {
        throw StateError(_errorMessage!);
      }
      return;
    }

    final completer = Completer<void>();
    _pendingLoad = completer;
    _isLoading = true;
    notifyListeners();

    try {
      final menus = await fetchAllPaginatedItems<MenuItem>(
        request: (page, pageSize) => _repository.list(
          page: page,
          pageSize: pageSize,
          includeDeleted: false,
        ),
      );
      _menus = List.unmodifiable(menus);
      _initialized = true;
      _errorMessage = null;
      completer.complete();
    } catch (error, stackTrace) {
      final failure = Failure.from(error);
      _errorMessage = failure.describe();
      completer.completeError(error, stackTrace);
      rethrow;
    } finally {
      _isLoading = false;
      _pendingLoad = null;
      notifyListeners();
    }
  }

  /// 외부에서 최신 메뉴 셋을 주입해 테스트/동기화를 돕는다.
  void replaceAll(List<MenuItem> menus) {
    _menus = List.unmodifiable(menus);
    _initialized = true;
    _errorMessage = null;
    notifyListeners();
  }

  /// 특정 코드에 해당하는 메뉴를 조회한다.
  MenuItem? findByCode(String menuCode) {
    for (final menu in _menus) {
      if (menu.menuCode == menuCode) {
        return menu;
      }
    }
    return null;
  }

  /// 캐시를 초기 상태로 되돌린다.
  void reset() {
    _menus = const [];
    _initialized = false;
    _errorMessage = null;
    notifyListeners();
  }
}

/// [MenuCatalog]를 위젯 트리에 노출하는 Inherited 래퍼.
class MenuCatalogScope extends InheritedNotifier<MenuCatalog> {
  const MenuCatalogScope({
    super.key,
    required MenuCatalog catalog,
    required super.child,
  }) : super(notifier: catalog);

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