import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/material.dart';

import '../../Services/navigation_service.dart';
import '../../Services/theme_service.dart';
import '../Misc/InputFields/dynamic_date_time_field.dart';
import 'dialog_action.dart';
import 'dialog_input_field.dart';
import 'dialog_input_field_select_item.dart';
import 'dialog_input_field_type_enum.dart';
import 'dialog_type_enum.dart';

/// Erstellt einen neuen dynamischen Dialog
class DynamicDialog {
  /// Erstellt eine neue Instanz dieser Klasse
  DynamicDialog({
    this.title,
    this.icon,
    this.content,
    final List<DialogInputField>? inputFields,
    final List<DialogAction>? actions,
    this.backgroundColor,
    this.borderRadius = 16,
    this.barrierDismissible = true,
    this.dialogType = DialogTypeEnum.info,
    this.hiddenValues,
  }) : inputFields = inputFields ?? const [],
       actions = actions ?? [DialogAction(label: 'Schließen')],
       _values = hiddenValues ?? {};

  /// Der Titel des Dialogs
  final String? title;

  /// Der Icon des Dialogs
  final IconData? icon;

  /// Der Inhalt des Dialogs
  final Widget? content;

  /// Die Hintergrundfarbe des Dialogs, Standard wenn nicht gesetzt
  final Color? backgroundColor;

  /// Der BorderRadius des Dialogs
  final double borderRadius;

  /// Ob der Dialog bei einem Klick auf die Barriere geschlossen werden kann
  final bool barrierDismissible;

  /// Die InputFelder des Dialogs
  final List<DialogInputField> inputFields;

  /// Die Aktionen des Dialogs
  final List<DialogAction> actions;

  /// Der Typ des Dialogs
  final DialogTypeEnum dialogType;

  /// Versteckte Werte, die beim Abschicken mit zurückgegeben werden
  final Map<String, dynamic>? hiddenValues;

  final Map<String, dynamic> _values;

  final Map<String, TextEditingController> _controllers = {};

  BuildContext? _dialogContext;

  /// Zeigt den vorher zusammengebauten Dialog an
  Future<void> show() async {
    final BuildContext? context = NavigationService.getCurrentBuildContext();

    if (context != null) {
      final ThemeData theme = Theme.of(context);

      await showDialog(
        context: context,
        barrierDismissible: barrierDismissible,
        builder: (final BuildContext ctx) {
          _dialogContext = ctx;
          final DialogAction primaryAction = actions.firstWhere(
            (final a) => a.isPrimary,
            orElse: () => actions.first,
          );

          Color backgroundColor =
              this.backgroundColor ?? theme.colorScheme.surface;

          if (dialogType == DialogTypeEnum.error) {
            backgroundColor = theme.colorScheme.errorContainer;
          } else if (dialogType == DialogTypeEnum.success) {
            backgroundColor = ThemeService.getSuccessColor(
              brightness: theme.brightness,
            );
          }

          return SingleChildScrollView(
            child: AlertDialog(
              backgroundColor: backgroundColor,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(borderRadius),
              ),
              title: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  if (icon != null)
                    Icon(icon, size: 48, color: theme.colorScheme.primary),
                  if (title != null)
                    Padding(
                      padding: const EdgeInsets.only(top: 8),
                      child: Text(
                        title!,
                        style: theme.textTheme.titleLarge,
                        textAlign: TextAlign.center,
                      ),
                    ),
                ],
              ),
              content: ConstrainedBox(
                constraints: BoxConstraints(
                  maxHeight: MediaQuery.of(ctx).size.height * 0.7,
                ),
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    if (content != null) content!,
                    ...inputFields.map(
                      (final DialogInputField field) => Padding(
                        padding: const EdgeInsets.symmetric(vertical: 6),
                        child: _getInputField(field, primaryAction),
                      ),
                    ),
                  ],
                ),
              ),
              actions: actions.map(_getAction).toList(),
            ),
          );
        },
      );
    }
  }

  /// Schließt den dynamischen Dialog
  void close() {
    if (_dialogContext != null) {
      Navigator.of(_dialogContext!).pop();
    }

    _values.clear();
    _controllers.clear();
  }

  void _submit(final DialogAction action) {
    final Map<String, dynamic> values = {}
    ..addEntries(_values.entries);

    _controllers.forEach((
      final String id,
      final TextEditingController controller,
    ) {
      values[id] = controller.text;
    });

    close();
    action.onPressed?.call(values);
  }

  Widget _getInputField(
    final DialogInputField inputField,
    final DialogAction primaryAction,
  ) {
    if (_values[inputField.id] == null) {
      _values[inputField.id] = inputField.initialValue;
    }

    if (inputField.inputType == DialogInputFieldTypeEnum.date) {
      return DynamicDateTimeField(
        initialValue: _values[inputField.id],
        autofocus: inputField.autoFocus,
        onChanged: (final value) {
          inputField.onChanged?.call(value);
          _values[inputField.id] = value;
        },
        decoration: InputDecoration(
          labelText: inputField.label,
          border: const OutlineInputBorder(),
          isDense: true,
        ),
      );
    } else if (inputField.inputType == DialogInputFieldTypeEnum.select) {
      if (_values[inputField.id] is Enum) {
        final Enum inputFieldInitialValue = _values[inputField.id];

        for (final DialogInputFieldSelectItem value in inputField.selectItems) {
          if (value.id == inputFieldInitialValue.index) {
            _values[inputField.id] = value;
          }
        }
      }

      return DropdownSearch<DialogInputFieldSelectItem>(
        items: (final f, final cs) => inputField.selectItems,
        itemAsString: (final DialogInputFieldSelectItem value) => value.value,
        selectedItem: _values[inputField.id],
        onChanged: (final DialogInputFieldSelectItem? value) {
          inputField.onChanged?.call(value);
          _values[inputField.id] = value;
        },
        decoratorProps: DropDownDecoratorProps(
          decoration: InputDecoration(
            labelText: inputField.label,
            border: const OutlineInputBorder(),
            isDense: true,
          ),
        ),
        compareFn:
            (
              final DialogInputFieldSelectItem v1,
              final DialogInputFieldSelectItem v2,
            ) => v1.id == v2.id,
      );
    } else {
      if (_controllers[inputField.id] == null) {
        _controllers[inputField.id] = TextEditingController(
          text: (_values[inputField.id] ?? '').toString()
        );
      }

      return TextField(
        controller: _controllers[inputField.id],
        autofocus: inputField.autoFocus,
        keyboardType: inputField.keyboardType,
        obscureText: inputField.obscureText,
        onChanged: (final value) {
          inputField.onChanged?.call(value);
          _values[inputField.id] = value;
        },
        decoration: InputDecoration(
          labelText: inputField.label,
          border: const OutlineInputBorder(),
          isDense: true,
        ),
        onSubmitted: (_) {
          _submit(primaryAction);
        },
      );
    }
  }

  Widget _getAction(final DialogAction action) {
    if (action.isPrimary) {
      return ElevatedButton(
        onPressed: () {
          _submit(action);
        },
        child: Text(action.label),
      );
    } else {
      return TextButton(
        onPressed: () {
          _submit(action);
        },
        child: Text(action.label),
      );
    }
  }
}
