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

class OtpCodeFields extends StatefulWidget {
  const OtpCodeFields({
    super.key,
    this.length = 6,
    this.autofocus = true,
    this.enabled = true,
    this.errorText,
    this.onChanged,
    this.onCompleted,
    this.boxSize = 48,
    this.gap = 10,
  });

  final int length;
  final bool autofocus;
  final bool enabled;
  final String? errorText;
  final ValueChanged<String>? onChanged;
  final ValueChanged<String>? onCompleted;
  final double boxSize;
  final double gap;

  @override
  State<OtpCodeFields> createState() => _OtpCodeFieldsState();
}

class _OtpCodeFieldsState extends State<OtpCodeFields> {
  late final List<TextEditingController> _controllers;
  late final List<FocusNode> _focusNodes;

  String get _code => _controllers.map((c) => c.text.trim()).join();

  @override
  void initState() {
    super.initState();
    _controllers = List.generate(widget.length, (_) => TextEditingController());
    _focusNodes = List.generate(widget.length, (_) => FocusNode());

    if (widget.autofocus) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (!mounted) return;
        _focusNodes.first.requestFocus();
      });
    }
  }

  @override
  void dispose() {
    for (final c in _controllers) {
      c.dispose();
    }
    for (final f in _focusNodes) {
      f.dispose();
    }
    super.dispose();
  }

  void _emit() {
    final code = _code;
    widget.onChanged?.call(code);
    if (code.length == widget.length &&
        !_controllers.any((c) => c.text.isEmpty)) {
      widget.onCompleted?.call(code);
    }
  }

  void _setFromPaste(String value) {
    final digits = value.replaceAll(RegExp(r'\D'), '');
    if (digits.isEmpty) return;

    final clipped = digits.length > widget.length
        ? digits.substring(0, widget.length)
        : digits;

    for (var i = 0; i < widget.length; i++) {
      _controllers[i].text = i < clipped.length ? clipped[i] : '';
    }

    final nextIndex = clipped.length >= widget.length
        ? widget.length - 1
        : clipped.length;

    _focusNodes[nextIndex].requestFocus();
    _emit();
    setState(() {});
  }

  void _onChanged(int index, String value) {
    if (!mounted) return;

    if (value.length > 1) {
      _setFromPaste(value);
      return;
    }

    if (value.isNotEmpty && index < widget.length - 1) {
      _focusNodes[index + 1].requestFocus();
    }

    _emit();
    setState(() {});
  }

  KeyEventResult _onKey(int index, KeyEvent event) {
    if (event is! KeyDownEvent) return KeyEventResult.ignored;

    if (event.logicalKey == LogicalKeyboardKey.backspace) {
      final current = _controllers[index].text;

      if (current.isEmpty && index > 0) {
        _controllers[index - 1].text = '';
        _focusNodes[index - 1].requestFocus();
        _emit();
        setState(() {});
        return KeyEventResult.handled;
      }
    }

    return KeyEventResult.ignored;
  }

  @override
  Widget build(BuildContext context) {
    final borderColor = widget.errorText == null || widget.errorText!.isEmpty
        ? Theme.of(context).dividerColor
        : Theme.of(context).colorScheme.error;

    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        Wrap(
          alignment: WrapAlignment.center,
          spacing: widget.gap,
          children: List.generate(widget.length, (i) {
            return SizedBox(
              width: widget.boxSize,
              height: widget.boxSize,
              child: Focus(
                onKeyEvent: (_, event) => _onKey(i, event),
                child: TextField(
                  enabled: widget.enabled,
                  controller: _controllers[i],
                  focusNode: _focusNodes[i],
                  keyboardType: TextInputType.number,
                  textInputAction: i == widget.length - 1
                      ? TextInputAction.done
                      : TextInputAction.next,
                  textAlign: TextAlign.center,
                  maxLength: 1,
                  style: const TextStyle(
                    fontSize: 18,
                    fontWeight: FontWeight.w700,
                  ),
                  inputFormatters: [
                    FilteringTextInputFormatter.digitsOnly,
                    LengthLimitingTextInputFormatter(1),
                  ],
                  decoration: InputDecoration(
                    counterText: '',
                    contentPadding: EdgeInsets.zero,
                    enabledBorder: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(14),
                      borderSide: BorderSide(color: borderColor),
                    ),
                    focusedBorder: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(14),
                      borderSide: BorderSide(
                        color: Theme.of(context).colorScheme.primary,
                        width: 1.6,
                      ),
                    ),
                    errorBorder: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(14),
                      borderSide: BorderSide(
                        color: Theme.of(context).colorScheme.error,
                        width: 1.2,
                      ),
                    ),
                    focusedErrorBorder: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(14),
                      borderSide: BorderSide(
                        color: Theme.of(context).colorScheme.error,
                        width: 1.6,
                      ),
                    ),
                  ),
                  onChanged: (v) => _onChanged(i, v),
                ),
              ),
            );
          }),
        ),
        if (widget.errorText != null && widget.errorText!.isNotEmpty)
          Padding(
            padding: const EdgeInsets.only(top: 10),
            child: Text(
              widget.errorText!,
              textAlign: TextAlign.center,
              style: TextStyle(
                color: Theme.of(context).colorScheme.error,
                fontSize: 12,
              ),
            ),
          ),
      ],
    );
  }
}
