content: import 'dart:convert';
  
  import 'package:flutter/material.dart';
  
  import 'package:matrix/matrix.dart';
  
  import 'package:fluffychat/config/app_config.dart';
  import 'package:fluffychat/l10n/l10n.dart';
  import 'package:fluffychat/pages/settings_notifications/push_rule_extensions.dart';
  import 'package:fluffychat/utils/localized_exception_extension.dart';
  import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart';
  import 'package:fluffychat/widgets/adaptive_dialogs/show_modal_action_popup.dart';
  import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
  import 'package:fluffychat/widgets/future_loading_dialog.dart';
  import '../../widgets/matrix.dart';
  import 'settings_notifications_view.dart';
  
  class SettingsNotifications extends StatefulWidget {
    const SettingsNotifications({super.key});
  
    @override
    SettingsNotificationsController createState() =>
        SettingsNotificationsController();
  }
  
  class SettingsNotificationsController extends State<SettingsNotifications> {
    bool isLoading = false;
  
    void onPusherTap(Pusher pusher) async {
      final delete = await showModalActionPopup<bool>(
        context: context,
        title: pusher.deviceDisplayName,
        message: '${pusher.appDisplayName} (${pusher.appId})',
        cancelLabel: L10n.of(context).cancel,
        actions: [
          AdaptiveModalAction(
            label: L10n.of(context).delete,
            isDestructive: true,
            value: true,
          ),
        ],
      );
      if (delete != true) return;
  
      final success = await showFutureLoadingDialog(
        context: context,
        future: () => Matrix.of(context).client.deletePusher(
              PusherId(
                appId: pusher.appId,
                pushkey: pusher.pushkey,
              ),
            ),
      );
  
      if (success.error != null) return;
  
      setState(() {
        pusherFuture = null;
      });
    }
  
    Future<List<Pusher>?>? pusherFuture;
  
    void togglePushRule(PushRuleKind kind, PushRule pushRule) async {
      setState(() {
        isLoading = true;
      });
      try {
        final updateFromSync = Matrix.of(context)
            .client
            .onSync
            .stream
            .where(
              (syncUpdate) =>
                  syncUpdate.accountData?.any(
                    (accountData) => accountData.type == 'm.push_rules',
                  ) ??
                  false,
            )
            .first;
        await Matrix.of(context).client.setPushRuleEnabled(
              kind,
              pushRule.ruleId,
              !pushRule.enabled,
            );
        await updateFromSync;
      } catch (e, s) {
        Logs().w('Unable to toggle push rule', e, s);
        if (!mounted) return;
        ScaffoldMessenger.of(context)
            .showSnackBar(SnackBar(content: Text(e.toLocalizedString(context))));
      } finally {
        if (mounted) {
          setState(() {
            isLoading = false;
          });
        }
      }
    }
  
    void editPushRule(PushRule rule, PushRuleKind kind) async {
      final theme = Theme.of(context);
      final action = await showAdaptiveDialog<PushRuleDialogAction>(
        context: context,
        builder: (context) => ConstrainedBox(
          constraints: const BoxConstraints(maxWidth: 256),
          child: AlertDialog.adaptive(
            title: Text(rule.getPushRuleName(L10n.of(context))),
            content: Padding(
              padding: const EdgeInsets.only(top: 16.0),
              child: Material(
                borderRadius: BorderRadius.circular(AppConfig.borderRadius),
                color: theme.colorScheme.surfaceContainer,
                child: SingleChildScrollView(
                  padding: const EdgeInsets.all(16),
                  scrollDirection: Axis.horizontal,
                  child: SelectableText(
                    prettyJson(rule.toJson()),
                    style: TextStyle(
                      color: theme.colorScheme.onSurface,
                    ),
                  ),
                ),
              ),
            ),
            actions: [
              AdaptiveDialogAction(
                onPressed: Navigator.of(context).pop,
                child: Text(L10n.of(context).close),
              ),
              if (!rule.ruleId.startsWith('.m.'))
                AdaptiveDialogAction(
                  onPressed: () =>
                      Navigator.of(context).pop(PushRuleDialogAction.delete),
                  child: Text(
                    L10n.of(context).delete,
                    style: TextStyle(color: theme.colorScheme.error),
                  ),
                ),
            ],
          ),
        ),
      );
      if (action == null) return;
      if (!mounted) return;
      switch (action) {
        case PushRuleDialogAction.delete:
          final consent = await showOkCancelAlertDialog(
            context: context,
            title: L10n.of(context).areYouSure,
            message: L10n.of(context).deletePushRuleCanNotBeUndone,
            okLabel: L10n.of(context).delete,
            isDestructive: true,
          );
          if (consent != OkCancelResult.ok) return;
          if (!mounted) return;
          setState(() {
            isLoading = true;
          });
          try {
            final updateFromSync = Matrix.of(context)
                .client
                .onSync
                .stream
                .where(
                  (syncUpdate) =>
                      syncUpdate.accountData?.any(
                        (accountData) => accountData.type == 'm.push_rules',
                      ) ??
                      false,
                )
                .first;
            await Matrix.of(context).client.deletePushRule(
                  kind,
                  rule.ruleId,
                );
            await updateFromSync;
          } catch (e, s) {
            Logs().w('Unable to delete push rule', e, s);
            if (!mounted) return;
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text(e.toLocalizedString(context))),
            );
          } finally {
            if (mounted) {
              setState(() {
                isLoading = false;
              });
            }
          }
          return;
      }
    }
  
    @override
    Widget build(BuildContext context) => SettingsNotificationsView(this);
  }
  
  enum PushRuleDialogAction { delete }
  
  String prettyJson(Map<String, Object?> json) {
    const decoder = JsonDecoder();
    const encoder = JsonEncoder.withIndent('    ');
    final object = decoder.convert(jsonEncode(json));
    return encoder.convert(object);
  }
  