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

import '../../../core/constants/ui_constants.dart';
import '../../../core/providers/dynamic_theme_provider.dart';
import '../ui/entity_form.dart';
import 'autocomplete_dropdown_field.dart' as FAutoDropdown;
import 'autocomplete_multiselect_field.dart' as FAutoMultiSelect;
import 'base_field.dart';
import 'captcha_field.dart' as FCaptcha;
import 'checkbox_field.dart' as FCheckbox;
import 'custom_text_field.dart' as FText;
import 'date_field.dart' as FDate;
import 'datetime_field.dart' as FDateTime;
import 'dropdown_field.dart' as FDropdown;
import 'email_field.dart' as FEmail;
import 'number_field.dart' as FNumber;
import 'password_field.dart' as FPassword;
import 'phone_field.dart' as FPhone;
import 'switch_field.dart' as FSwitch;
import 'url_field.dart' as FUrl;

/// A generic field that shows an Insert button to create a related entity inline.
///
/// Usage:
/// - Provide `fieldKey` for where to store the selected/created value
/// - Provide `label` for display
/// - Provide `relationTitle` for the modal header
/// - Either provide `fieldSchema` (preferred, like OneToOne/OneToMany) or `formFieldsBuilder`
/// - Provide `onCreate` callback which receives form data and returns created entity map
/// - Configure `valueKey` to pick what to write back (e.g., 'id') into the parent controller
/// - Optionally set `displayKey` to show a summary of the selected value
class RelatedEntityInsertField extends BaseField {
  final String fieldKey;
  final String label;
  final String hint;
  final bool isRequired;
  final String relationTitle;
  final Future<Map<String, dynamic>> Function(Map<String, dynamic> formData)
      onCreate;
  final List<Map<String, dynamic>>? fieldSchema;
  final List<BaseField> Function()? formFieldsBuilder;
  final String valueKey;
  final String? displayKey;

  RelatedEntityInsertField({
    required this.fieldKey,
    required this.label,
    this.hint = '',
    this.isRequired = false,
    required this.relationTitle,
    required this.onCreate,
    this.fieldSchema,
    this.formFieldsBuilder,
    this.valueKey = 'id',
    this.displayKey,
  }) : assert(fieldSchema != null || formFieldsBuilder != null,
            'Either fieldSchema or formFieldsBuilder must be provided');

  @override
  String? Function(String?)? get validator => (value) {
        if (!isRequired) return null;
        if (value == null || value.trim().isEmpty) return '$label is required';
        return null;
      };

  @override
  Map<String, dynamic>? get customProperties => {
        'isRelationInsert': true,
        'relationTitle': relationTitle,
        'valueKey': valueKey,
        'displayKey': displayKey,
      };

  @override
  Widget buildField({
    required TextEditingController controller,
    required ColorScheme colorScheme,
    VoidCallback? onChanged,
  }) {
    return _RelatedEntityInsertWidget(
      label: label,
      hint: hint,
      controller: controller,
      colorScheme: colorScheme,
      relationTitle: relationTitle,
      onCreate: onCreate,
      fieldSchema: fieldSchema,
      formFieldsBuilder: formFieldsBuilder,
      valueKey: valueKey,
      displayKey: displayKey,
      validate: validator,
      onChanged: onChanged,
    );
  }

  /// Static helper to open the insert dialog anywhere (e.g., in list actions)
  /// Returns the written value (e.g., created id as string) or null if cancelled
  static Future<String?> showInsertDialog({
    required BuildContext context,
    required String relationTitle,
    required Future<Map<String, dynamic>> Function(Map<String, dynamic>)
        onCreate,
    List<Map<String, dynamic>>? fieldSchema,
    List<BaseField> Function()? formFieldsBuilder,
    String valueKey = 'id',
    String? displayKey,
  }) async {
    final TextEditingController ctrl = TextEditingController();
    bool isSubmitting = false;
    final List<BaseField> fields = (formFieldsBuilder != null)
        ? formFieldsBuilder()
        : _fieldsFromSchemaStatic(fieldSchema ?? const []);

    final Map<String, dynamic>? created =
        await showDialog<Map<String, dynamic>>(
      context: context,
      barrierDismissible: false,
      builder: (dialogContext) {
        return Consumer<DynamicThemeProvider>(
          builder: (context, theme, child) {
            final cs = theme.getCurrentColorScheme(
                Theme.of(dialogContext).brightness == Brightness.dark);
            return StatefulBuilder(
              builder: (context, setLocal) {
                return AlertDialog(
                  backgroundColor: cs.surface,
                  shape: RoundedRectangleBorder(
                      borderRadius:
                          BorderRadius.circular(UIConstants.radius16)),
                  title: Text(
                    relationTitle,
                    style: TextStyle(
                        color: cs.onSurface, fontWeight: FontWeight.w700),
                  ),
                  content: SingleChildScrollView(
                    child: EntityForm(
                      fields: fields,
                      onSubmit: (formData) async {
                        if (isSubmitting) return;
                        setLocal(() => isSubmitting = true);
                        try {
                          final res = await onCreate(formData);
                          if (!Navigator.of(dialogContext).mounted) return;
                          Navigator.of(dialogContext).pop(res);
                        } catch (_) {
                          setLocal(() => isSubmitting = false);
                        }
                      },
                      submitButtonText: 'Create',
                      isLoading: isSubmitting,
                    ),
                  ),
                  actions: [
                    TextButton(
                      onPressed: isSubmitting
                          ? null
                          : () => Navigator.of(dialogContext).pop(),
                      child: const Text('Cancel'),
                    ),
                  ],
                );
              },
            );
          },
        );
      },
    );

    if (created != null) {
      ctrl.text = (created[valueKey])?.toString() ?? '';
      return ctrl.text.isEmpty ? null : ctrl.text;
    }
    return null;
  }

  // Static schema â fields mapper for the dialog helper
  static List<BaseField> _fieldsFromSchemaStatic(
      List<Map<String, dynamic>> schema) {
    return schema.map<BaseField>((f) {
      final String type = f['type']?.toString() ?? 'text';
      final String flabel = f['label']?.toString() ?? '';
      final String fhint = f['hint']?.toString() ?? '';
      final String path = f['path']?.toString() ?? '';
      final bool requiredField = f['required'] == true;
      final List<Map<String, dynamic>>? options =
          (f['options'] as List?)?.cast<Map<String, dynamic>>();
      final String valueKey = f['valueKey']?.toString() ?? 'id';
      final String displayKey = f['displayKey']?.toString() ?? 'name';
      final int? maxLength =
          f['maxLength'] is int ? f['maxLength'] as int : null;
      final int? decimalPlaces =
          f['decimalPlaces'] is int ? f['decimalPlaces'] as int : null;
      final double? min =
          (f['min'] is num) ? (f['min'] as num).toDouble() : null;
      final double? max =
          (f['max'] is num) ? (f['max'] as num).toDouble() : null;

      switch (type) {
        case 'number':
          return FNumber.NumberField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
            min: min,
            max: max,
            decimalPlaces: decimalPlaces,
          );
        case 'email':
          return FEmail.EmailField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
          );
        case 'phone':
          return FPhone.PhoneField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
          );
        case 'password':
          return FPassword.PasswordField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
          );
        case 'dropdown':
          return FDropdown.DropdownField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
            options: options ?? const [],
            valueKey: valueKey,
            displayKey: displayKey,
          );
        case 'date':
          return FDate.DateField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
          );
        case 'datetime':
          return FDateTime.DateTimeField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
          );
        case 'switch':
          return FSwitch.SwitchField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
          );
        case 'checkbox':
          return FCheckbox.CheckboxField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
          );
        case 'captcha':
          return FCaptcha.CaptchaField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
          );
        case 'url':
          return FUrl.UrlField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
          );
        case 'autocomplete_dropdown':
          return FAutoDropdown.AutocompleteDropdownField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
            optionsLoader: () async => options ?? [],
            valueKey: valueKey,
            displayKey: displayKey,
          );
        case 'autocomplete_multiselect':
          return FAutoMultiSelect.AutocompleteMultiSelectField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
            optionsLoader: () async => options ?? [],
            valueKey: valueKey,
            displayKey: displayKey,
          );
        case 'text':
        default:
          return FText.CustomTextField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
            maxLength: maxLength,
          );
      }
    }).toList();
  }
}

class _RelatedEntityInsertWidget extends StatefulWidget {
  final String label;
  final String hint;
  final TextEditingController controller;
  final ColorScheme colorScheme;
  final String relationTitle;
  final Future<Map<String, dynamic>> Function(Map<String, dynamic>) onCreate;
  final List<Map<String, dynamic>>? fieldSchema;
  final List<BaseField> Function()? formFieldsBuilder;
  final String valueKey;
  final String? displayKey;
  final String? Function(String?)? validate;
  final VoidCallback? onChanged;

  const _RelatedEntityInsertWidget({
    required this.label,
    required this.hint,
    required this.controller,
    required this.colorScheme,
    required this.relationTitle,
    required this.onCreate,
    this.fieldSchema,
    this.formFieldsBuilder,
    required this.valueKey,
    required this.displayKey,
    required this.validate,
    required this.onChanged,
  });

  @override
  State<_RelatedEntityInsertWidget> createState() =>
      _RelatedEntityInsertWidgetState();
}

class _RelatedEntityInsertWidgetState
    extends State<_RelatedEntityInsertWidget> {
  bool _creating = false;

  @override
  Widget build(BuildContext context) {
    final cs = widget.colorScheme;
    final String selectedDisplay = _computeDisplay();

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        if (widget.label.isNotEmpty)
          Padding(
            padding: const EdgeInsets.only(bottom: 8),
            child: Text(
              widget.label,
              style: TextStyle(
                  fontWeight: FontWeight.w700,
                  color: cs.onSurface,
                  fontSize: 14),
            ),
          ),
        Container(
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(12),
            border: Border.all(color: cs.primary.withOpacity(0.15)),
            gradient: LinearGradient(
              begin: Alignment.topLeft,
              end: Alignment.bottomRight,
              colors: [
                cs.surface,
                cs.surface.withOpacity(0.97),
              ],
            ),
          ),
          padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
          child: Row(
            children: [
              Expanded(
                child: Text(
                  selectedDisplay.isEmpty
                      ? (widget.hint.isEmpty ? 'No selection' : widget.hint)
                      : selectedDisplay,
                  style: TextStyle(
                      color: cs.onSurface.withOpacity(0.75),
                      fontWeight: FontWeight.w500),
                  overflow: TextOverflow.ellipsis,
                ),
              ),
              const SizedBox(width: 8),
              ElevatedButton.icon(
                onPressed: _creating ? null : _openInsertDialog,
                icon: Icon(Icons.add_rounded, color: cs.onPrimary, size: 18),
                label: Text('Insert',
                    style: TextStyle(
                        color: cs.onPrimary, fontWeight: FontWeight.w600)),
                style: ElevatedButton.styleFrom(
                  backgroundColor: cs.primary,
                  padding:
                      const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
                  elevation: 2,
                  shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(10)),
                ),
              ),
            ],
          ),
        ),
        if (widget.validate != null)
          Builder(
            builder: (context) {
              final error = widget.validate!(widget.controller.text);
              return error != null
                  ? Padding(
                      padding: const EdgeInsets.only(top: 6),
                      child: Text(error,
                          style: TextStyle(
                              color: cs.error,
                              fontSize: 12,
                              fontWeight: FontWeight.w600)),
                    )
                  : const SizedBox.shrink();
            },
          )
      ],
    );
  }

  String _computeDisplay() {
    if (widget.displayKey == null || widget.displayKey!.isEmpty) {
      return widget.controller.text.trim();
    }
    return widget.controller.text.trim();
  }

  Future<void> _openInsertDialog() async {
    setState(() => _creating = true);
    try {
      final List<BaseField> fields = (widget.formFieldsBuilder != null)
          ? widget.formFieldsBuilder!()
          : _fieldsFromSchema(widget.fieldSchema ?? const []);

      final result = await showDialog<Map<String, dynamic>>(
        context: context,
        barrierDismissible: false,
        builder: (context) {
          bool isSubmitting = false;
          return StatefulBuilder(
            builder: (context, setLocal) {
              return AlertDialog(
                backgroundColor: Theme.of(context).colorScheme.surface,
                shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(16)),
                title: Text(
                  widget.relationTitle,
                  style: TextStyle(
                    color: Theme.of(context).colorScheme.onSurface,
                    fontWeight: FontWeight.w700,
                  ),
                ),
                content: SingleChildScrollView(
                  child: EntityForm(
                    fields: fields,
                    onSubmit: (formData) async {
                      if (isSubmitting) return;
                      setLocal(() => isSubmitting = true);
                      try {
                        final created = await widget.onCreate(formData);
                        if (!mounted) return;
                        Navigator.of(context).pop(created);
                      } catch (_) {
                        setLocal(() => isSubmitting = false);
                      }
                    },
                    submitButtonText: 'Create',
                    isLoading: isSubmitting,
                  ),
                ),
                actions: [
                  TextButton(
                    onPressed:
                        isSubmitting ? null : () => Navigator.of(context).pop(),
                    child: const Text('Cancel'),
                  ),
                ],
              );
            },
          );
        },
      );

      if (result != null) {
        final dynamic value = result[widget.valueKey];
        final String textValue = value?.toString() ?? '';
        widget.controller.text = textValue;
        widget.onChanged?.call();
        EntityFormScope.of(context)?.notifyParent();
      }
    } finally {
      if (mounted) setState(() => _creating = false);
    }
  }

  List<BaseField> _fieldsFromSchema(List<Map<String, dynamic>> schema) {
    return schema.map<BaseField>((f) {
      final String type = f['type']?.toString() ?? 'text';
      final String flabel = f['label']?.toString() ?? '';
      final String fhint = f['hint']?.toString() ?? '';
      final String path = f['path']?.toString() ?? '';
      final bool requiredField = f['required'] == true;
      final List<Map<String, dynamic>>? options =
          (f['options'] as List?)?.cast<Map<String, dynamic>>();
      final String valueKey = f['valueKey']?.toString() ?? 'id';
      final String displayKey = f['displayKey']?.toString() ?? 'name';
      final int? maxLength =
          f['maxLength'] is int ? f['maxLength'] as int : null;
      final int? decimalPlaces =
          f['decimalPlaces'] is int ? f['decimalPlaces'] as int : null;
      final double? min =
          (f['min'] is num) ? (f['min'] as num).toDouble() : null;
      final double? max =
          (f['max'] is num) ? (f['max'] as num).toDouble() : null;

      switch (type) {
        case 'number':
          return FNumber.NumberField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
            min: min,
            max: max,
            decimalPlaces: decimalPlaces,
          );
        case 'email':
          return FEmail.EmailField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
          );
        case 'phone':
          return FPhone.PhoneField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
          );
        case 'password':
          return FPassword.PasswordField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
          );
        case 'dropdown':
          return FDropdown.DropdownField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
            options: options ?? const [],
            valueKey: valueKey,
            displayKey: displayKey,
          );
        case 'date':
          return FDate.DateField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
          );
        case 'datetime':
          return FDateTime.DateTimeField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
          );
        case 'switch':
          return FSwitch.SwitchField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
          );
        case 'checkbox':
          return FCheckbox.CheckboxField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
          );
        case 'captcha':
          return FCaptcha.CaptchaField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
          );
        case 'url':
          return FUrl.UrlField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
          );
        case 'autocomplete_dropdown':
          return FAutoDropdown.AutocompleteDropdownField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
            optionsLoader: () async => options ?? [],
            valueKey: valueKey,
            displayKey: displayKey,
          );
        case 'autocomplete_multiselect':
          return FAutoMultiSelect.AutocompleteMultiSelectField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
            optionsLoader: () async => options ?? [],
            valueKey: valueKey,
            displayKey: displayKey,
          );
        case 'text':
        default:
          return FText.CustomTextField(
            fieldKey: path,
            label: flabel,
            hint: fhint,
            isRequired: requiredField,
            maxLength: maxLength,
          );
      }
    }).toList();
  }
}
