import 'dart:async';

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

import '../domain/entities/approval_approver_candidate.dart';
import '../domain/repositories/approval_approver_repository.dart';

/// 승인자 자동완성 필드.
///
/// - 이름/사번을 입력하면 API에서 승인자 후보를 검색한다.
/// - 항목 선택 시 `idController`에 승인자 ID를 기록한다.
class ApprovalApproverAutocompleteField extends StatefulWidget {
  const ApprovalApproverAutocompleteField({
    super.key,
    required this.idController,
    this.hintText,
    this.onSelected,
  });

  final TextEditingController idController;
  final String? hintText;
  final void Function(ApprovalApproverCandidate?)? onSelected;

  @override
  State<ApprovalApproverAutocompleteField> createState() =>
      _ApprovalApproverAutocompleteFieldState();
}

class _ApprovalApproverAutocompleteFieldState
    extends State<ApprovalApproverAutocompleteField> {
  static const _debounceDuration = Duration(milliseconds: 250);
  static const _pageSize = 15;

  final TextEditingController _textController = TextEditingController();
  final FocusNode _focusNode = FocusNode();
  final List<ApprovalApproverCandidate> _suggestions = [];
  final List<ApprovalApproverCandidate> _initialSuggestions = [];
  ApprovalApproverCandidate? _selected;
  Timer? _debounce;
  bool _isSearching = false;
  bool _isLoadingInitial = false;
  bool _initialLoaded = false;
  bool _isApplyingText = false;
  int _requestId = 0;

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

  @override
  void initState() {
    super.initState();
    _focusNode.addListener(_handleFocusChange);
    _initializeFromId();
    unawaited(_prefetchInitialCandidates());
  }

  @override
  void didUpdateWidget(covariant ApprovalApproverAutocompleteField oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (!identical(widget.idController, oldWidget.idController)) {
      _initializeFromId();
    }
  }

  Future<void> _initializeFromId() async {
    final idText = widget.idController.text.trim();
    final id = int.tryParse(idText);
    if (id == null) {
      setState(() {
        _selected = null;
        _setText('');
      });
      return;
    }
    final repository = _repository;
    if (repository == null) {
      return;
    }
    try {
      final candidate = await repository.fetchById(id);
      if (!mounted) {
        return;
      }
      if (candidate == null) {
        setState(() {
          _selected = null;
          _setText('');
        });
        widget.onSelected?.call(null);
        return;
      }
      setState(() {
        _selected = candidate;
      });
      _ensureCandidateCached(candidate);
      _setText(_displayLabel(candidate));
      widget.onSelected?.call(candidate);
    } catch (_) {
      // 조회 실패 시 기존 상태를 유지하되 텍스트를 비운다.
      if (!mounted) {
        return;
      }
      setState(() {
        _selected = null;
        _setText('');
      });
      widget.onSelected?.call(null);
    }
  }

  Future<void> _prefetchInitialCandidates() async {
    if (_initialLoaded || _isLoadingInitial) {
      return;
    }

    final repository = _repository;
    if (repository == null) {
      return;
    }

    setState(() {
      _isLoadingInitial = true;
    });

    try {
      final results = await repository.listInitial(limit: _pageSize);
      if (!mounted) {
        return;
      }
      setState(() {
        _initialSuggestions.clear();
        _initialSuggestions.addAll(results);
        if (_selected != null &&
            !_initialSuggestions.any((item) => item.id == _selected!.id)) {
          _initialSuggestions.insert(0, _selected!);
        }
        if (_textController.text.trim().isEmpty) {
          _suggestions.clear();
          _suggestions.addAll(_initialSuggestions);
        }
        _isLoadingInitial = false;
        _initialLoaded = true;
      });
    } catch (_) {
      if (!mounted) {
        return;
      }
      setState(() {
        _isLoadingInitial = false;
        _initialLoaded = false;
      });
    }
  }

  void _handleFocusChange() {
    if (_focusNode.hasFocus) {
      if (!_initialLoaded && !_isLoadingInitial) {
        unawaited(_prefetchInitialCandidates());
      } else if (_textController.text.trim().isEmpty &&
          _initialSuggestions.isNotEmpty) {
        setState(() {
          _suggestions.clear();
          _suggestions.addAll(_initialSuggestions);
        });
      }
      return;
    }
    unawaited(_confirmManualEntry(_textController.text));
  }

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

  Future<void> _search(String keyword) async {
    final repository = _repository;
    final trimmed = keyword.trim();
    if (repository == null || trimmed.isEmpty) {
      if (mounted) {
        setState(() {
          _suggestions.clear();
          _isSearching = false;
        });
      }
      return;
    }
    final request = ++_requestId;
    setState(() {
      _isSearching = true;
    });
    try {
      final results = await repository.search(
        keyword: trimmed,
        limit: _pageSize,
      );
      if (!mounted || request != _requestId) {
        return;
      }
      setState(() {
        _suggestions.clear();
        _suggestions.addAll(results);
        _isSearching = false;
      });
    } catch (_) {
      if (!mounted || request != _requestId) {
        return;
      }
      setState(() {
        _suggestions.clear();
        _isSearching = false;
      });
    }
  }

  Future<void> _confirmManualEntry(String value) async {
    final trimmed = value.trim();
    if (trimmed.isEmpty) {
      _clearSelection();
      return;
    }
    final manualId = int.tryParse(trimmed.replaceAll(RegExp(r'[^0-9]'), ''));
    if (manualId == null) {
      return;
    }
    final repository = _repository;
    if (repository == null) {
      return;
    }
    try {
      final candidate = await repository.fetchById(manualId);
      if (!mounted) {
        return;
      }
      if (candidate == null) {
        _clearSelection();
        return;
      }
      _applySelection(candidate);
    } catch (_) {
      if (!mounted) {
        return;
      }
      _clearSelection();
    }
  }

  void _applySelection(ApprovalApproverCandidate candidate) {
    setState(() {
      _selected = candidate;
      widget.idController.text = candidate.id.toString();
      _isSearching = false;
    });
    _ensureCandidateCached(candidate);
    _setText(_displayLabel(candidate));
    widget.onSelected?.call(candidate);
  }

  void _clearSelection({
    bool useInitialSuggestions = false,
    bool resetText = true,
  }) {
    final hadSelection =
        _selected != null || widget.idController.text.isNotEmpty;
    setState(() {
      _selected = null;
      _isSearching = false;
      _suggestions.clear();
      if (useInitialSuggestions && _initialSuggestions.isNotEmpty) {
        _suggestions.addAll(_initialSuggestions);
      }
    });
    if (hadSelection) {
      widget.idController.clear();
    }
    widget.onSelected?.call(null);
    if (resetText || useInitialSuggestions || hadSelection) {
      _setText('');
    }
  }

  String _displayLabel(ApprovalApproverCandidate candidate) {
    final team = candidate.team?.trim();
    if (team == null || team.isEmpty) {
      return '${candidate.name} (${candidate.employeeNo})';
    }
    return '${candidate.name} (${candidate.employeeNo}) · $team';
  }

  void _ensureCandidateCached(ApprovalApproverCandidate candidate) {
    if (!mounted) {
      return;
    }
    final exists = _initialSuggestions.any((item) => item.id == candidate.id);
    if (exists) {
      return;
    }
    setState(() {
      _initialSuggestions.insert(0, candidate);
      if (_textController.text.trim().isEmpty) {
        _suggestions.clear();
        _suggestions.addAll(_initialSuggestions);
      }
    });
  }

  void _setText(String value) {
    _isApplyingText = true;
    _textController.text = value;
    _textController.selection = TextSelection.collapsed(offset: value.length);
    _isApplyingText = false;
  }

  @override
  void dispose() {
    _debounce?.cancel();
    _textController.dispose();
    _focusNode.removeListener(_handleFocusChange);
    _focusNode.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final theme = ShadTheme.of(context);
    return LayoutBuilder(
      builder: (context, constraints) {
        final maxWidth = constraints.maxWidth.isFinite
            ? constraints.maxWidth
            : 360.0;
        return RawAutocomplete<ApprovalApproverCandidate>(
          textEditingController: _textController,
          focusNode: _focusNode,
          optionsBuilder: (textEditingValue) {
            return _suggestions;
          },
          displayStringForOption: _displayLabel,
          onSelected: _applySelection,
          fieldViewBuilder:
              (context, textController, focusNode, onFieldSubmitted) {
                assert(identical(textController, _textController));
                return Stack(
                  alignment: Alignment.centerRight,
                  children: [
                    ShadInput(
                      controller: textController,
                      focusNode: focusNode,
                      placeholder: Text(widget.hintText ?? '승인자 이름 또는 사번 검색'),
                      onChanged: (value) {
                        if (_isApplyingText) {
                          return;
                        }
                        if (value.trim().isEmpty) {
                          _debounce?.cancel();
                          _clearSelection(useInitialSuggestions: true);
                          if (!_initialLoaded && !_isLoadingInitial) {
                            unawaited(_prefetchInitialCandidates());
                          }
                        } else {
                          _scheduleSearch(value);
                        }
                      },
                      onSubmitted: (_) {
                        unawaited(_confirmManualEntry(textController.text));
                        onFieldSubmitted();
                      },
                      onPressedOutside: (_) => focusNode.requestFocus(),
                    ),
                    if (_isSearching ||
                        (_isLoadingInitial &&
                            textController.text.trim().isEmpty))
                      const Padding(
                        padding: EdgeInsets.only(right: 12),
                        child: SizedBox(
                          width: 16,
                          height: 16,
                          child: CircularProgressIndicator(strokeWidth: 2),
                        ),
                      ),
                  ],
                );
              },
          optionsViewBuilder: (context, onSelected, options) {
            final isInitialLoadInProgress =
                _isLoadingInitial && _textController.text.trim().isEmpty;
            if (_isSearching || isInitialLoadInProgress) {
              return _buildDropdownWrapper(
                maxWidth: maxWidth,
                maxHeight: 220,
                theme: theme,
                child: const Center(
                  child: Padding(
                    padding: EdgeInsets.symmetric(vertical: 24),
                    child: CircularProgressIndicator(strokeWidth: 2),
                  ),
                ),
              );
            }
            if (options.isEmpty) {
              final hasKeyword = _textController.text.trim().isNotEmpty;
              final message = hasKeyword
                  ? '일치하는 승인자를 찾지 못했습니다.'
                  : '표시할 승인자가 없습니다.';
              return _buildDropdownWrapper(
                maxWidth: maxWidth,
                maxHeight: 220,
                theme: theme,
                child: Center(
                  child: Padding(
                    padding: const EdgeInsets.symmetric(vertical: 24),
                    child: Text(message, style: theme.textTheme.muted),
                  ),
                ),
              );
            }
            return _buildDropdownWrapper(
              maxWidth: maxWidth,
              maxHeight: 260,
              theme: theme,
              child: ListView.builder(
                padding: const EdgeInsets.symmetric(vertical: 6),
                itemCount: options.length,
                itemBuilder: (context, index) {
                  final option = options.elementAt(index);
                  return InkWell(
                    onTap: () => onSelected(option),
                    child: Padding(
                      padding: const EdgeInsets.symmetric(
                        horizontal: 12,
                        vertical: 10,
                      ),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(option.name, style: theme.textTheme.p),
                          const SizedBox(height: 4),
                          Text(
                            _optionSubtitle(option),
                            style: theme.textTheme.muted.copyWith(fontSize: 12),
                          ),
                        ],
                      ),
                    ),
                  );
                },
              ),
            );
          },
        );
      },
    );
  }

  Widget _buildDropdownWrapper({
    required double maxWidth,
    required double maxHeight,
    required Widget child,
    required ShadThemeData theme,
  }) {
    return Align(
      alignment: AlignmentDirectional.topStart,
      child: ConstrainedBox(
        constraints: BoxConstraints(maxWidth: maxWidth, maxHeight: maxHeight),
        child: Material(
          elevation: 6,
          color: theme.colorScheme.background,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(12),
            side: BorderSide(color: theme.colorScheme.border),
          ),
          child: child,
        ),
      ),
    );
  }

  String _optionSubtitle(ApprovalApproverCandidate candidate) {
    final team = candidate.team?.trim();
    final buffer = StringBuffer('ID ${candidate.id} · ${candidate.employeeNo}');
    if (team != null && team.isNotEmpty) {
      buffer.write(' · $team');
    }
    return buffer.toString();
  }
}
