import 'dart:developer';

import 'package:hydrated_bloc/hydrated_bloc.dart';

import '../../../../../api/demo/data/demo_capabilities.dart';
import '../../../../../api/demo/demo_mode.dart';
import '../../../../../api/marianumcloud/capabilities/get_nextcloud_capabilities.dart';
import 'nextcloud_capabilities_state.dart';

/// Holds the current user's Nextcloud `files_sharing` capabilities. Hydrated so
/// the last known flags are available immediately on cold start (no
/// feature-flicker) and offline. [load] refreshes them from the server after
/// login. Same fail-safe contract as [CapabilitiesCubit]: a failed fetch keeps
/// the cached flags and never silently grants a capability.
class NextcloudCapabilitiesCubit
    extends HydratedCubit<NextcloudCapabilitiesState> {
  NextcloudCapabilitiesCubit() : super(const NextcloudCapabilitiesState());

  /// Master switch — the user may create at least one kind of share.
  bool get canShareAtAll => state.apiEnabled;

  /// User (person) shares are allowed whenever the share API is enabled;
  /// Nextcloud has no separate per-type toggle for them.
  bool get canShareWithUsers => state.apiEnabled;

  bool get canShareWithGroups => state.apiEnabled && state.groupEnabled;

  bool get canCreatePublicLinks => state.apiEnabled && state.publicEnabled;

  bool get passwordEnforced => state.publicPasswordEnforced;

  bool get expireEnforced =>
      state.publicExpireEnabled && state.publicExpireEnforced;

  int? get expireDays => state.publicExpireDays;

  bool get canReshare => state.resharing;

  bool get allowsMultipleLinks => state.publicMultipleLinks;

  bool get supportsFileDrop => state.publicUploadEnabled;

  /// Human-readable summary of the server's link-password rules, or null if no
  /// rules are advertised. The breach-list ("non-common password") check is
  /// server-only and intentionally omitted — it can't be validated up front.
  String? get passwordPolicyHint {
    final parts = <String>[];
    final min = state.passwordMinLength;
    if (min != null && min > 0) parts.add('mind. $min Zeichen');
    if (state.passwordEnforceUpperLower) {
      parts.add('Groß- und Kleinbuchstaben');
    }
    if (state.passwordEnforceNumeric) parts.add('mind. eine Zahl');
    if (state.passwordEnforceSpecial) parts.add('mind. ein Sonderzeichen');
    if (parts.isEmpty) return null;
    return 'Anforderungen: ${parts.join(', ')}';
  }

  /// Refreshes capabilities from the server. On any failure the previously
  /// hydrated flags are kept but the state is marked `loaded`.
  Future<void> load() async {
    if (DemoMode.active) {
      emit(DemoNextcloudCapabilities.state());
      return;
    }
    try {
      final caps = await GetNextcloudCapabilities().run();
      emit(
        NextcloudCapabilitiesState(
          apiEnabled: caps.apiEnabled,
          publicEnabled: caps.publicEnabled,
          publicMultipleLinks: caps.publicMultipleLinks,
          publicUploadEnabled: caps.publicUploadEnabled,
          publicPasswordEnforced: caps.publicPasswordEnforced,
          publicExpireEnabled: caps.publicExpireEnabled,
          publicExpireDays: caps.publicExpireDays,
          publicExpireEnforced: caps.publicExpireEnforced,
          groupEnabled: caps.groupEnabled,
          resharing: caps.resharing,
          passwordMinLength: caps.passwordMinLength,
          passwordEnforceUpperLower: caps.passwordEnforceUpperLower,
          passwordEnforceNumeric: caps.passwordEnforceNumeric,
          passwordEnforceSpecial: caps.passwordEnforceSpecial,
          loaded: true,
        ),
      );
    } catch (e) {
      log('Failed to load Nextcloud capabilities: $e');
      emit(state.copyWith(loaded: true));
    }
  }

  Future<void> reset() async => emit(const NextcloudCapabilitiesState());

  @override
  NextcloudCapabilitiesState fromJson(Map<String, dynamic> json) {
    try {
      return NextcloudCapabilitiesState.fromJson(json);
    } catch (_) {
      return const NextcloudCapabilitiesState();
    }
  }

  @override
  Map<String, dynamic>? toJson(NextcloudCapabilitiesState state) =>
      state.toJson();
}
