import 'package:flutter/material.dart';
import 'base_field.dart';
import '../ui/entity_form.dart';
import 'dart:convert';
import 'custom_text_field.dart' as FText;
import 'number_field.dart' as FNumber;
import 'email_field.dart' as FEmail;
import 'phone_field.dart' as FPhone;
import 'password_field.dart' as FPassword;
import 'dropdown_field.dart' as FDropdown;
import 'date_field.dart' as FDate;
import 'datetime_field.dart' as FDateTime;
import 'switch_field.dart' as FSwitch;
import 'checkbox_field.dart' as FCheckbox;
import 'captcha_field.dart' as FCaptcha;
import 'url_field.dart' as FUrl;
import 'autocomplete_dropdown_field.dart' as FAutoDropdown;
import 'autocomplete_multiselect_field.dart' as FAutoMultiSelect;

/// One-to-many subform builder
/// - Renders repeatable group of sub-fields
/// - Stores JSON array (as string) in controller
class OneToManyField extends BaseField {
  final String fieldKey;
  final String label;
  final String hint;
  final bool isRequired;
  final List<BaseField>? subFields;
  final List<Map<String, dynamic>>? fieldSchema;

  OneToManyField({
    required this.fieldKey,
    required this.label,
    this.hint = '',
    this.isRequired = false,
    this.subFields,
    this.fieldSchema,
  }) : assert(subFields != null || fieldSchema != null,
            'Either subFields or fieldSchema must be provided');

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

  @override
  Map<String, dynamic>? get customProperties => {
        'isOneToMany': true,
        'excludeFromSubmit': false,
        'parseAsJson': true, // Parse JSON string to object before submission
      };

  @override
  Widget buildField({
    required TextEditingController controller,
    required ColorScheme colorScheme,
    VoidCallback? onChanged,
  }) {
    return _OneToManyWidget(
      label: label,
      controller: controller,
      colorScheme: colorScheme,
      subFields: subFields,
      fieldSchema: fieldSchema,
      validate: validator,
      onChanged: onChanged,
    );
  }
}

class _OneToManyWidget extends StatefulWidget {
  final String label;
  final TextEditingController controller;
  final ColorScheme colorScheme;
  final List<BaseField>? subFields;
  final List<Map<String, dynamic>>? fieldSchema;
  final String? Function(String?)? validate;
  final VoidCallback? onChanged;

  const _OneToManyWidget({
    required this.label,
    required this.controller,
    required this.colorScheme,
    this.subFields,
    this.fieldSchema,
    required this.validate,
    required this.onChanged,
  });

  @override
  State<_OneToManyWidget> createState() => _OneToManyWidgetState();
}

class _OneToManyWidgetState extends State<_OneToManyWidget> {
  final List<Map<String, TextEditingController>> _rows = [];

  @override
  void initState() {
    super.initState();
    // Use WidgetsBinding to defer the initialization after build
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _initializeRows();
    });
  }

  void _initializeRows() {
    if (widget.controller.text.isNotEmpty) {
      // Try to parse as JSON first
      try {
        final decoded = const JsonDecoder().convert(widget.controller.text);
        if (decoded is List) {
          for (final item in decoded) {
            if (item is Map) {
              final prefillData = <String, String>{};
              item.forEach((key, value) {
                prefillData[key.toString()] = value?.toString() ?? '';
              });
              _addRow(prefill: prefillData);
            }
          }
        }
      } catch (e) {
        // If JSON parsing fails, the data might already be in object format
        // This happens when data comes from backend API response
        print('JSON parsing failed, trying to handle as object: $e');

        // Try to handle the data as if it's already parsed
        try {
          // Check if controller text looks like a list representation
          final text = widget.controller.text.trim();
          if (text.startsWith('[') && text.endsWith(']')) {
            // This might be a string representation of a list
            // Try to extract the data specifically
            _handleBackendDataFormat(text);
          }
        } catch (e2) {
          print('Error handling backend data format: $e2');
        }
      }
    }
    // Always add at least one empty row if no data exists
    if (_rows.isEmpty) _addRow();
  }

  void _handleBackendDataFormat(String data) {
    // Handle backend data format: [{id: 21, name: g1, description: gdesc}, {id: 22, name: g2, description: g2desc}]
    try {
      // Remove outer brackets and split by }, {
      String cleanData = data.trim();
      if (cleanData.startsWith('[') && cleanData.endsWith(']')) {
        cleanData = cleanData.substring(1, cleanData.length - 1);
      }

      // Split by }, { to get individual objects
      List<String> objects = [];
      if (cleanData.contains('}, {')) {
        objects = cleanData.split('}, {');
        // Fix the first and last objects
        if (objects.isNotEmpty) {
          objects[0] = objects[0].replaceFirst('{', '');
          objects[objects.length - 1] =
              objects[objects.length - 1].replaceFirst('}', '');
        }
      } else if (cleanData.startsWith('{') && cleanData.endsWith('}')) {
        // Single object
        objects = [cleanData.substring(1, cleanData.length - 1)];
      }

      // Parse each object
      for (String obj in objects) {
        final prefillData = <String, String>{};

        // Split by comma to get key-value pairs
        List<String> pairs = obj.split(', ');
        for (String pair in pairs) {
          if (pair.contains(':')) {
            List<String> keyValue = pair.split(': ');
            if (keyValue.length == 2) {
              String key = keyValue[0].trim();
              String value = keyValue[1].trim();

              // Remove null values
              if (value != 'null') {
                prefillData[key] = value;
              }
            }
          }
        }

        // Only add row if we have meaningful data
        if (prefillData.isNotEmpty) {
          _addRow(prefill: prefillData);
        }
      }

      print('Successfully parsed ${objects.length} objects from backend data');
    } catch (e) {
      print('Error parsing backend data format: $e');
      // Fallback: add empty row
    }
  }

  void _addRow({Map<String, String>? prefill}) {
    final row = <String, TextEditingController>{};

    if (widget.subFields != null) {
      // Legacy mode: using subFields
      for (final f in widget.subFields!) {
        row[f.fieldKey] =
            TextEditingController(text: prefill?[f.fieldKey] ?? '');
      }
    } else if (widget.fieldSchema != null) {
      // Schema mode: using fieldSchema
      for (final f in widget.fieldSchema!) {
        final String path = f['path']?.toString() ?? '';
        if (path.isNotEmpty) {
          row[path] = TextEditingController(text: prefill?[path] ?? '');
        }
      }
    }

    setState(() => _rows.add(row));
    _syncToParent();
  }

  void _removeRow(int index) {
    if (index < 0 || index >= _rows.length) return;
    final removed = _rows.removeAt(index);
    for (final c in removed.values) {
      c.dispose();
    }
    setState(() {});
    _syncToParent();
  }

  void _syncToParent() {
    final list = <Map<String, String>>[];
    for (final row in _rows) {
      final map = <String, String>{};
      row.forEach((k, c) => map[k] = c.text.trim());
      list.add(map);
    }
    widget.controller.text = const JsonEncoder().convert(list);
    widget.onChanged?.call();
    EntityFormScope.of(context)?.notifyParent();
  }

  List<Widget> _buildFields(
      Map<String, TextEditingController> ctrls, ColorScheme cs) {
    if (widget.subFields != null) {
      // Legacy mode: using subFields
      return widget.subFields!
          .map((f) => Padding(
                padding: const EdgeInsets.only(bottom: 12),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    if (f.label != null && f.label!.isNotEmpty)
                      Padding(
                        padding: const EdgeInsets.only(bottom: 8.0),
                        child: Text(
                          f.label!,
                          style: TextStyle(
                            fontWeight: FontWeight.w600,
                            color: cs.onSurface,
                            fontSize: 14,
                          ),
                        ),
                      ),
                    f.buildField(
                      controller: ctrls[f.fieldKey]!,
                      colorScheme: cs,
                      onChanged: _syncToParent,
                    ),
                  ],
                ),
              ))
          .toList();
    } else if (widget.fieldSchema != null) {
      // Schema mode: using fieldSchema
      return widget.fieldSchema!
          .map((f) => _buildSchemaField(f, ctrls, cs))
          .toList();
    }
    return [];
  }

  Widget _buildSchemaField(Map<String, dynamic> f,
      Map<String, TextEditingController> ctrls, ColorScheme cs) {
    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;

    final TextEditingController subController = ctrls[path]!;

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

    return Padding(
      padding: const EdgeInsets.only(bottom: 12),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          if (flabel.isNotEmpty)
            Padding(
              padding: const EdgeInsets.only(bottom: 8.0),
              child: Text(
                flabel,
                style: TextStyle(
                  fontWeight: FontWeight.w600,
                  color: cs.onSurface,
                  fontSize: 14,
                ),
              ),
            ),
          buildShared(),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    final cs = widget.colorScheme;
    return Container(
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(12),
        border: Border.all(color: cs.primary.withOpacity(0.15)),
      ),
      child: Padding(
        padding: const EdgeInsets.all(12),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(widget.label,
                style: TextStyle(
                    color: cs.onSurface, fontWeight: FontWeight.w700)),
            const SizedBox(height: 8),
            ..._rows.asMap().entries.map((entry) {
              final index = entry.key;
              final ctrls = entry.value;
              return Card(
                elevation: 0,
                color: cs.surface,
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(10),
                  side: BorderSide(color: cs.outline.withOpacity(0.12)),
                ),
                child: Padding(
                  padding: const EdgeInsets.all(12),
                  child: Column(
                    children: [
                      ..._buildFields(ctrls, cs),
                      Align(
                        alignment: Alignment.centerRight,
                        child: IconButton(
                          icon: Icon(Icons.delete_outline, color: cs.error),
                          tooltip: 'Remove',
                          onPressed: () => _removeRow(index),
                        ),
                      ),
                    ],
                  ),
                ),
              );
            }),
            const SizedBox(height: 12),
            // Add button at the bottom
            Center(
              child: ElevatedButton.icon(
                onPressed: () => _addRow(),
                icon: Icon(Icons.add, color: cs.onPrimary),
                label: Text('Add ${widget.label}',
                    style: TextStyle(color: cs.onPrimary)),
                style: ElevatedButton.styleFrom(
                  backgroundColor: cs.primary,
                  padding:
                      const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
                ),
              ),
            ),
            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)),
                        )
                      : const SizedBox.shrink();
                },
              )
          ],
        ),
      ),
    );
  }
}
