import 'dart:convert';

import 'package:flutter/material.dart';

import 'base_field.dart';
import 'captcha_field.dart' as FCaptcha;
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;

class OneToOneRelationField extends BaseField {
  final String fieldKey;
  final String label;
  final String hint;
  final bool isRequired;
  final Map<String, dynamic> relationSchema;

  OneToOneRelationField({
    required this.fieldKey,
    required this.label,
    required this.hint,
    required this.relationSchema,
    this.isRequired = false,
  });

  @override
  String? Function(String?)? get validator => (value) {
        // If not required, allow empty
        if (!isRequired) return null;

        // Required: ensure at least one inner field has a non-empty value
        if (value == null || value.isEmpty) {
          return '$label is required';
        }
        try {
          final decoded = json.decode(value);
          if (decoded is! Map<String, dynamic>) {
            return '$label is required';
          }
          final List fields = (relationSchema['fields'] as List?) ?? const [];
          for (final f in fields) {
            final String? path = f['path']?.toString();
            if (path == null) continue;
            final dynamic v = decoded[path];
            if (v != null && v.toString().trim().isNotEmpty) {
              return null; // at least one provided
            }
          }
          return '$label is required';
        } catch (_) {
          // If invalid JSON and required, treat as empty
          return '$label is required';
        }
      };

  @override
  Map<String, dynamic>? get customProperties => {
        'isRelation': true,
        'assignByJsonPaths': true,
        'paths': (relationSchema['fields'] as List?)
                ?.map((f) => f['path'])
                .where((p) => p != null)
                .toList() ??
            [],
      };

  @override
  Widget buildField({
    required TextEditingController controller,
    required ColorScheme colorScheme,
    VoidCallback? onChanged,
  }) {
    final List fields = (relationSchema['fields'] as List?) ?? const [];
    final bool boxed = relationSchema['box'] == true;
    final String title = relationSchema['title']?.toString() ?? label;

    Widget content = Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        if (title.isNotEmpty)
          Padding(
            padding: const EdgeInsets.only(bottom: 8.0),
            child: Text(
              title,
              style: TextStyle(
                fontWeight: FontWeight.bold,
                color: colorScheme.onSurface,
                fontSize: 16,
              ),
            ),
          ),
        ...fields.map<Widget>((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;

          // Initialize sub field with existing value from parent controller JSON.
          // Expect flat map { 'support.field': value }. Fallback to nested traversal.
          String initialValue = '';
          if (controller.text.isNotEmpty) {
            try {
              final decoded = json.decode(controller.text);
              if (decoded is Map<String, dynamic>) {
                if (decoded.containsKey(path)) {
                  initialValue = decoded[path]?.toString() ?? '';
                } else {
                  final parts = path.split('.');
                  dynamic curr = decoded;
                  for (final p in parts) {
                    if (curr is Map && curr.containsKey(p)) {
                      curr = curr[p];
                    } else {
                      curr = null;
                      break;
                    }
                  }
                  if (curr != null) initialValue = curr.toString();
                }
              }
            } catch (_) {}
          }

          void sync(String val) {
            Map<String, dynamic> current = {};
            if (controller.text.isNotEmpty) {
              try {
                final decoded = json.decode(controller.text);
                if (decoded is Map<String, dynamic>) current = decoded;
              } catch (_) {}
            }
            // Store flat path mapping
            if (val.isEmpty) {
              // remove the key when emptied
              current.remove(path);
            } else {
              current[path] = val;
            }
            // If map becomes empty, clear controller to avoid failing validation when optional
            controller.text = current.isEmpty ? '' : json.encode(current);
            onChanged?.call();
          }

          final TextEditingController subController =
              TextEditingController(text: initialValue);

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

          return Padding(
            padding: const EdgeInsets.only(bottom: 12.0),
            child: buildShared(),
          );
        }).toList(),
      ],
    );

    final Widget body = boxed
        ? Container(
            padding: const EdgeInsets.all(12),
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(8),
              border: Border.all(color: colorScheme.outline.withOpacity(0.4)),
            ),
            child: content,
          )
        : content;

    return Directionality(textDirection: TextDirection.ltr, child: body);
  }
}
