import 'dart:async';

import 'package:app_settings/app_settings.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import '../../../../api/errors/error_mapper.dart';
import '../../../../api/marianumconnect/queries/push_device_test/push_device_test.dart';
import '../../../../extensions/date_time.dart';
import '../../../../push/push_registration.dart';
import '../../../../push/push_status.dart';
import '../../../../state/app/modules/capabilities/bloc/capabilities_cubit.dart';
import '../../../../state/app/modules/settings/bloc/settings_cubit.dart';
import '../../../../widget/app_progress_indicator.dart';
import '../../../../widget/demo_restricted.dart';
import '../../../../widget/details_bottom_sheet.dart';

/// Opens the push status checklist: one row per link in the push chain with
/// an explanation where it is broken, the last registration attempt (incl.
/// verbatim error), a manual re-register action and — once the chain is
/// operational — a test notification. Loads once on open; the refresh action
/// re-collects on demand (no polling).
Future<void> showPushStatusSheet(BuildContext context) {
  // Captured here: the sheet outlives this build context's element tree.
  final settings = context.read<SettingsCubit>();
  final capabilities = context.read<CapabilitiesCubit>();
  return showDetailsBottomSheet(
    context,
    header: const ListTile(
      leading: Icon(Icons.monitor_heart_outlined),
      title: Text('Status der Push-Benachrichtigungen'),
    ),
    children: (sheetCtx) => [
      _PushStatusBody(settings: settings, capabilities: capabilities),
    ],
  );
}

class _PushStatusBody extends StatefulWidget {
  final SettingsCubit settings;
  final CapabilitiesCubit capabilities;

  const _PushStatusBody({required this.settings, required this.capabilities});

  @override
  State<_PushStatusBody> createState() => _PushStatusBodyState();
}

class _PushStatusBodyState extends State<_PushStatusBody>
    with WidgetsBindingObserver {
  PushStatusReport? _report;
  bool _busy = false;

  /// Set while the user is in the OS settings — the next `resumed` lifecycle
  /// event then re-collects the status so a just-changed permission shows up.
  bool _reloadOnResume = false;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    _load();
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed && _reloadOnResume) {
      _reloadOnResume = false;
      unawaited(_load());
    }
  }

  Future<void> _openNotificationSettings() async {
    _reloadOnResume = true;
    await AppSettings.openAppSettings(type: AppSettingsType.notification);
  }

  Future<void> _load() async {
    final capabilitiesState = widget.capabilities.state;
    final report = await collectPushStatus(
      settingEnabled: widget.settings.val().notificationSettings.enabled,
      capabilityPush: capabilitiesState.pushNotifications,
      capabilitiesLoaded: capabilitiesState.loaded,
    );
    if (!mounted) return;
    setState(() => _report = report);
  }

  Future<void> _reRegister() async {
    if (guardDemoAction(context)) return;
    if (_busy) return;
    setState(() => _busy = true);
    final messenger = ScaffoldMessenger.of(context);
    final ok = await PushRegistration().register();
    if (!mounted) return;
    setState(() => _busy = false);
    await _load();
    messenger.showSnackBar(
      SnackBar(
        content: Text(
          ok
              ? 'Registrierung erfolgreich abgeschlossen'
              : 'Registrierung fehlgeschlagen — Details in der Statusübersicht',
        ),
      ),
    );
  }

  Future<void> _sendTest() async {
    if (guardDemoAction(context)) return;
    if (_busy) return;
    setState(() => _busy = true);
    final messenger = ScaffoldMessenger.of(context);
    String message;
    try {
      final devices = await PushDeviceTest().run();
      message = switch (devices) {
        0 => 'Es ist kein Gerät registriert — bitte erneut registrieren',
        1 => 'Testbenachrichtigung an 1 Gerät gesendet',
        _ => 'Testbenachrichtigung an $devices Geräte gesendet',
      };
    } on Object catch (e) {
      message = errorToUserMessage(e);
    }
    if (!mounted) return;
    setState(() => _busy = false);
    messenger.showSnackBar(SnackBar(content: Text(message)));
  }

  @override
  Widget build(BuildContext context) {
    final report = _report;
    if (report == null) {
      return const Padding(
        padding: EdgeInsets.all(32),
        child: Center(child: AppProgressIndicator.medium()),
      );
    }
    final theme = Theme.of(context);
    final rows = buildPushStatusRows(report);
    return Column(
      mainAxisSize: MainAxisSize.min,
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        ...rows.map((row) {
          // Offer the OS-settings shortcut on the permission row whenever it
          // isn't granted — after a denial that's the only place to fix it.
          final canOpenSettings =
              row.opensNotificationSettings && row.state != PushCheck.ok;
          return ListTile(
            dense: true,
            leading: _stateIcon(row.state, theme),
            title: Text(row.label),
            subtitle: row.detail == null ? null : Text(row.detail!),
            trailing: canOpenSettings
                ? TextButton(
                    onPressed: _openNotificationSettings,
                    child: const Text('Einstellungen'),
                  )
                : null,
          );
        }),
        if (report.general.lastRegistrationAt != null ||
            report.talk.lastRegistrationAt != null)
          const Divider(height: 1),
        ..._lastAttempt(theme, 'Allgemein', report.general),
        ..._lastAttempt(theme, 'Talk', report.talk),
        Padding(
          padding: const EdgeInsets.fromLTRB(16, 8, 16, 0),
          child: _actions(report),
        ),
      ],
    );
  }

  /// Last-attempt line (+ verbatim error) for one registration type.
  List<Widget> _lastAttempt(
    ThemeData theme,
    String label,
    PushTypeStatus status,
  ) {
    final at = status.lastRegistrationAt;
    if (at == null) return const [];
    final error = status.lastRegistrationError;
    return [
      ListTile(
        dense: true,
        leading: Icon(
          error == null ? Icons.history : Icons.error_outline,
          color: error == null ? null : theme.colorScheme.error,
        ),
        title: Text(
          'Letzte Registrierung ($label): ${at.formatDateTime()} — '
          '${error == null ? 'erfolgreich' : 'fehlgeschlagen'}',
        ),
      ),
      if (error != null)
        Padding(
          padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
          child: Container(
            width: double.infinity,
            padding: const EdgeInsets.all(12),
            decoration: BoxDecoration(
              color: theme.colorScheme.surfaceContainerHighest,
              borderRadius: BorderRadius.circular(8),
            ),
            child: Text(
              error,
              style: TextStyle(
                fontFamily: 'monospace',
                fontSize: 12,
                color: theme.colorScheme.onSurfaceVariant,
              ),
            ),
          ),
        ),
    ];
  }

  /// Action hierarchy: refresh stays a secondary icon action; the primary
  /// (filled) button is the test notification once the chain is operational,
  /// otherwise re-registering IS the primary next step and the test action is
  /// omitted (it could not succeed and the checklist explains why).
  Widget _actions(PushStatusReport report) {
    const spinner = SizedBox(
      width: 16,
      height: 16,
      child: CircularProgressIndicator(strokeWidth: 2),
    );
    final ready = report.readyForTestNotification;
    return Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        IconButton(
          onPressed: _busy ? null : _load,
          tooltip: 'Aktualisieren',
          icon: const Icon(Icons.refresh),
        ),
        // OverflowBar stacks the buttons vertically on narrow screens instead
        // of overflowing the row.
        Expanded(
          child: OverflowBar(
            alignment: MainAxisAlignment.end,
            overflowAlignment: OverflowBarAlignment.end,
            spacing: 8,
            overflowSpacing: 4,
            children: [
              if (ready) ...[
                TextButton(
                  onPressed: _busy ? null : _reRegister,
                  child: const Text('Erneut registrieren'),
                ),
                FilledButton.icon(
                  onPressed: _busy ? null : _sendTest,
                  icon: _busy ? spinner : const Icon(Icons.send_outlined),
                  label: const Text('Testbenachrichtigung'),
                ),
              ] else
                FilledButton.icon(
                  onPressed: _busy ? null : _reRegister,
                  icon: _busy ? spinner : const Icon(Icons.sync),
                  label: const Text('Erneut registrieren'),
                ),
            ],
          ),
        ),
      ],
    );
  }

  Widget _stateIcon(PushCheck state, ThemeData theme) {
    switch (state) {
      case PushCheck.ok:
        return const Icon(
          Icons.check_circle_outline,
          color: Colors.green,
          semanticLabel: 'In Ordnung',
        );
      case PushCheck.fail:
        return Icon(
          Icons.cancel_outlined,
          color: theme.colorScheme.error,
          semanticLabel: 'Fehler',
        );
      case PushCheck.unknown:
        return Icon(
          Icons.remove_circle_outline,
          color: theme.colorScheme.onSurfaceVariant,
          semanticLabel: 'Unbekannt',
        );
    }
  }
}
