import 'package:flutter/material.dart';

import 'package:provider/provider.dart';

import 'package:flutter_settings_ui/flutter_settings_ui.dart';

import 'package:pweb/models/state/edit_state.dart';
import 'package:pweb/utils/error/snackbar.dart';

import 'package:pweb/generated/i18n/app_localizations.dart';


abstract class BaseEditTile<T> extends AbstractSettingsTile {
  const BaseEditTile({
    super.key,
    required this.icon,
    required this.title,
    required this.valueGetter,
    required this.valueSetter,
    required this.errorSituation,
    this.editStateNotifier,
  });

  final IconData icon;
  final String title;
  final ValueGetter<T?> valueGetter;
  final Future<void> Function(T) valueSetter;
  final String errorSituation;
  final ValueNotifier<EditState>? editStateNotifier;

  Widget buildView(BuildContext context, T? value);

  Widget buildEditor(
    BuildContext context,
    T? initial,
    void Function(T) onSave,
    VoidCallback onCancel,
    bool isSaving,
  );

  bool get useDialogEditor => false;

  @override
  Widget build(BuildContext context) => _BaseEditTileBody<T>(delegate: this);
}

class _BaseEditTileBody<T> extends StatefulWidget {
  const _BaseEditTileBody({required this.delegate});
  final BaseEditTile<T> delegate;
  @override
  State<_BaseEditTileBody<T>> createState() => _BaseEditTileBodyState<T>();
}

class _BaseEditTileBodyState<T> extends State<_BaseEditTileBody<T>> {
  late final ValueNotifier<EditState> _stateNotifier;
  late final bool _ownsNotifier;

  bool get _isSaving => _stateNotifier.value == EditState.saving;

  @override
  void initState() {
    super.initState();
    final providedNotifier = widget.delegate.editStateNotifier ??
        Provider.of<ValueNotifier<EditState>?>(context, listen: false);
    _ownsNotifier = providedNotifier == null;
    _stateNotifier = providedNotifier ?? ValueNotifier(EditState.view);
  }

  @override
  void dispose() {
    if (_ownsNotifier) {
      _stateNotifier.dispose();
    }
    super.dispose();
  }

  Future<void> _performSave(T newValue) async {
    final current = widget.delegate.valueGetter();
    if (newValue == current) {
      _stateNotifier.value = EditState.view;
      return;
    }
    _stateNotifier.value = EditState.saving;
    final scaffoldMessenger = ScaffoldMessenger.of(context);
    final locs = AppLocalizations.of(context)!;
    try {
      await widget.delegate.valueSetter(newValue);
      scaffoldMessenger.showSnackBar(SnackBar(
        content: Text(locs.settingsSuccessfullyUpdated),
        duration: const Duration(milliseconds: 1200),
      ));
    } catch (e) {
      showErrorSnackBar(
        scaffoldMessenger: scaffoldMessenger,
        errorSituation: widget.delegate.errorSituation,
        appLocalizations: locs,
        exception: e,
      );
    } finally {
      if (mounted) _stateNotifier.value = EditState.view;
    }
  }

  Future<void> _openDialogEditor() async {
    final initial = widget.delegate.valueGetter();
    final T? result = await showDialog<T>(
      context: context,
      barrierDismissible: true,
      builder: (ctx) {
        return Dialog(
          insetPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 24),
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(28)),
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: widget.delegate.buildEditor(
              ctx,
              initial,
              (v) => Navigator.of(ctx).pop(v),
              () => Navigator.of(ctx).pop(),
              _isSaving,
            ),
          ),
        );
      },
    );
    if (!mounted) return;
    if (result != null) await _performSave(result);
  }

  @override
  Widget build(BuildContext context) {
    final delegate = widget.delegate;

    if (delegate.useDialogEditor) {
      return ValueListenableBuilder<EditState>(
        valueListenable: _stateNotifier,
        builder: (context, state, _) {
          final current = delegate.valueGetter();
          return SettingsTile.navigation(
            leading: Icon(delegate.icon),
            title: Text(delegate.title),
            value: delegate.buildView(context, current),
            onPressed: state == EditState.saving ? null : (_) => _openDialogEditor(),
          );
        },
      );
    }

    return ValueListenableBuilder<EditState>(
      valueListenable: _stateNotifier,
      builder: (context, state, _) {
        final current = delegate.valueGetter();
        final isView = state == EditState.view;
        final isSaving = state == EditState.saving;

        return SettingsTile.navigation(
          leading: Icon(delegate.icon),
          title: Text(delegate.title),
          value: isView
              ? delegate.buildView(context, current)
              : delegate.buildEditor(
                  context,
                  current,
                  _performSave,
                  () => _stateNotifier.value = EditState.view,
                  isSaving,
                ),
          onPressed: (_) {
            if (isView) _stateNotifier.value = EditState.edit;
          },
        );
      },
    );
  }
}
