import 'dart:async';

import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:shadcn_ui/shadcn_ui.dart';

import 'package:superport_v2/features/masters/user/domain/entities/user.dart';
import 'package:superport_v2/features/masters/user/domain/repositories/user_repository.dart';

/// 작성자(직원) 검색 결과 모델.
class InventoryEmployeeSuggestion {
  InventoryEmployeeSuggestion({
    required this.id,
    required this.employeeNo,
    required this.name,
  });

  factory InventoryEmployeeSuggestion.fromUser(UserAccount account) {
    return InventoryEmployeeSuggestion(
      id: account.id ?? 0,
      employeeNo: account.employeeNo,
      name: account.employeeName,
    );
  }

  final int id;
  final String employeeNo;
  final String name;
}

/// 작성자를 자동완성으로 선택하는 입력 필드.
class InventoryEmployeeAutocompleteField extends StatefulWidget {
  const InventoryEmployeeAutocompleteField({
    super.key,
    required this.controller,
    this.initialSuggestion,
    required this.onSuggestionSelected,
    this.onChanged,
    this.enabled = true,
    this.placeholder = '작성자 이름 또는 사번 검색',
  });

  final TextEditingController controller;
  final InventoryEmployeeSuggestion? initialSuggestion;
  final ValueChanged<InventoryEmployeeSuggestion?> onSuggestionSelected;
  final VoidCallback? onChanged;
  final bool enabled;
  final String placeholder;

  @override
  State<InventoryEmployeeAutocompleteField> createState() =>
      _InventoryEmployeeAutocompleteFieldState();
}

class _InventoryEmployeeAutocompleteFieldState
    extends State<InventoryEmployeeAutocompleteField> {
  static const Duration _debounceDuration = Duration(milliseconds: 250);

  final List<InventoryEmployeeSuggestion> _suggestions = [];
  InventoryEmployeeSuggestion? _selected;
  Timer? _debounce;
  int _requestId = 0;
  bool _isSearching = false;
  late final FocusNode _focusNode;

  UserRepository? get _repository =>
      GetIt.I.isRegistered<UserRepository>() ? GetIt.I<UserRepository>() : null;

  @override
  void initState() {
    super.initState();
    _selected = widget.initialSuggestion;
    _focusNode = FocusNode();
    widget.controller.addListener(_handleChanged);
    if (_selected != null && widget.controller.text.isEmpty) {
      widget.controller.text = _displayLabel(_selected!);
    }
  }

  @override
  void didUpdateWidget(covariant InventoryEmployeeAutocompleteField oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (!identical(oldWidget.controller, widget.controller)) {
      oldWidget.controller.removeListener(_handleChanged);
      widget.controller.addListener(_handleChanged);
    }
    if (widget.initialSuggestion != oldWidget.initialSuggestion &&
        widget.initialSuggestion != null) {
      _selected = widget.initialSuggestion;
      widget.controller.text = _displayLabel(widget.initialSuggestion!);
    }
  }

  void _handleChanged() {
    final text = widget.controller.text.trim();
    if (text.isEmpty) {
      _clearSelection();
      return;
    }
    _scheduleSearch(text);
    widget.onChanged?.call();
  }

  void _scheduleSearch(String keyword) {
    _debounce?.cancel();
    _debounce = Timer(_debounceDuration, () => _search(keyword));
  }

  Future<void> _search(String keyword) async {
    final repository = _repository;
    if (repository == null) {
      return;
    }
    final request = ++_requestId;
    setState(() {
      _isSearching = true;
    });
    try {
      final result = await repository.list(
        page: 1,
        pageSize: 15,
        query: keyword,
        isActive: true,
      );
      if (!mounted || request != _requestId) {
        return;
      }
      setState(() {
        _suggestions
          ..clear()
          ..addAll(result.items.map(InventoryEmployeeSuggestion.fromUser));
        _isSearching = false;
      });
    } catch (_) {
      if (!mounted || request != _requestId) {
        return;
      }
      setState(() {
        _suggestions.clear();
        _isSearching = false;
      });
    }
  }

  void _applySuggestion(InventoryEmployeeSuggestion suggestion) {
    setState(() {
      _selected = suggestion;
    });
    widget.controller.text = _displayLabel(suggestion);
    widget.onSuggestionSelected(suggestion);
    widget.onChanged?.call();
  }

  void _clearSelection() {
    if (_selected == null) {
      return;
    }
    setState(() {
      _selected = null;
      _suggestions.clear();
      _isSearching = false;
    });
    widget.onSuggestionSelected(null);
    widget.controller.clear();
  }

  String _displayLabel(InventoryEmployeeSuggestion suggestion) {
    return '${suggestion.name} (${suggestion.employeeNo})';
  }

  @override
  void dispose() {
    widget.controller.removeListener(_handleChanged);
    _debounce?.cancel();
    _focusNode.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final theme = ShadTheme.of(context);
    return RawAutocomplete<InventoryEmployeeSuggestion>(
      textEditingController: widget.controller,
      focusNode: _focusNode,
      optionsBuilder: (textEditingValue) {
        if (textEditingValue.text.trim().isEmpty) {
          return const Iterable<InventoryEmployeeSuggestion>.empty();
        }
        return _suggestions;
      },
      displayStringForOption: _displayLabel,
      onSelected: _applySuggestion,
      fieldViewBuilder: (context, textController, focusNode, onFieldSubmitted) {
        final input = ShadInput(
          controller: textController,
          focusNode: focusNode,
          enabled: widget.enabled,
          placeholder: Text(widget.placeholder),
          onChanged: (_) => widget.onChanged?.call(),
          onSubmitted: (_) => onFieldSubmitted(),
          onPressedOutside: (event) {
            // 드롭다운 선택 중 포커스를 유지해 리스트가 즉시 닫히는 문제를 방지한다.
            focusNode.requestFocus();
          },
        );
        if (!_isSearching) {
          return input;
        }
        return Stack(
          alignment: Alignment.centerRight,
          children: [
            input,
            const Padding(
              padding: EdgeInsets.only(right: 12),
              child: SizedBox(
                width: 16,
                height: 16,
                child: CircularProgressIndicator(strokeWidth: 2),
              ),
            ),
          ],
        );
      },
      optionsViewBuilder: (context, onSelected, options) {
        if (options.isEmpty) {
          return Listener(
            onPointerDown: (_) {
              // 포커스가 사라지면 항목 선택 전에 닫히므로 즉시 복구한다.
              if (!_focusNode.hasPrimaryFocus) {
                _focusNode.requestFocus();
              }
            },
            child: Align(
              alignment: AlignmentDirectional.topStart,
              child: Material(
                elevation: 6,
                color: theme.colorScheme.background,
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(12),
                  side: BorderSide(color: theme.colorScheme.border),
                ),
                child: Padding(
                  padding: const EdgeInsets.symmetric(vertical: 16),
                  child: Center(
                    child: Text('일치하는 직원이 없습니다.', style: theme.textTheme.muted),
                  ),
                ),
              ),
            ),
          );
        }
        return Listener(
          onPointerDown: (_) {
            if (!_focusNode.hasPrimaryFocus) {
              _focusNode.requestFocus();
            }
          },
          child: Align(
            alignment: AlignmentDirectional.topStart,
            child: ConstrainedBox(
              constraints: const BoxConstraints(maxHeight: 240, maxWidth: 360),
              child: Material(
                elevation: 6,
                color: theme.colorScheme.background,
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(12),
                  side: BorderSide(color: theme.colorScheme.border),
                ),
                child: ListView.builder(
                  padding: const EdgeInsets.symmetric(vertical: 6),
                  itemCount: options.length,
                  itemBuilder: (context, index) {
                    final suggestion = options.elementAt(index);
                    return InkWell(
                      onTap: () => onSelected(suggestion),
                      child: Padding(
                        padding: const EdgeInsets.symmetric(
                          horizontal: 12,
                          vertical: 10,
                        ),
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text(suggestion.name, style: theme.textTheme.p),
                            const SizedBox(height: 4),
                            Text(
                              'ID ${suggestion.id} · ${suggestion.employeeNo}',
                              style: theme.textTheme.muted.copyWith(
                                fontSize: 12,
                              ),
                            ),
                          ],
                        ),
                      ),
                    );
                  },
                ),
              ),
            ),
          ),
        );
      },
    );
  }
}
