import 'dart:async';

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

import 'package:superport_v2/core/common/utils/pagination_utils.dart';
import 'package:superport_v2/core/network/failure.dart';
import 'package:superport_v2/features/masters/product/domain/entities/product.dart';
import 'package:superport_v2/features/masters/product/domain/repositories/product_repository.dart';

/// 제품 검색 결과를 표현하는 제안 모델.
class InventoryProductSuggestion {
  InventoryProductSuggestion({
    required this.id,
    required this.code,
    required this.name,
    required this.vendorName,
    required this.unitName,
  });

  factory InventoryProductSuggestion.fromProduct(Product product) {
    return InventoryProductSuggestion(
      id: product.id ?? 0,
      code: product.productCode,
      name: product.productName,
      vendorName: product.vendor?.vendorName ?? '-',
      unitName: product.uom?.uomName ?? '-',
    );
  }

  final int id;
  final String code;
  final String name;
  final String vendorName;
  final String unitName;
}

/// 제품명 자동완성을 제공하는 입력 필드.
class InventoryProductAutocompleteField extends StatefulWidget {
  const InventoryProductAutocompleteField({
    super.key,
    required this.productController,
    required this.productFocusNode,
    required this.manufacturerController,
    required this.unitController,
    required this.onSuggestionSelected,
    this.initialSuggestion,
    this.onChanged,
  });

  final TextEditingController productController;
  final FocusNode productFocusNode;
  final TextEditingController manufacturerController;
  final TextEditingController unitController;
  final ValueChanged<InventoryProductSuggestion?> onSuggestionSelected;
  final InventoryProductSuggestion? initialSuggestion;
  final VoidCallback? onChanged;

  @override
  State<InventoryProductAutocompleteField> createState() =>
      _InventoryProductAutocompleteFieldState();
}

/// 제품 자동완성 위젯의 내부 상태를 관리한다.
class _InventoryProductAutocompleteFieldState
    extends State<InventoryProductAutocompleteField> {
  static const Duration _debounceDuration = Duration(milliseconds: 280);

  final List<InventoryProductSuggestion> _initialOptions =
      <InventoryProductSuggestion>[];
  final List<InventoryProductSuggestion> _suggestions =
      <InventoryProductSuggestion>[];
  InventoryProductSuggestion? _selected;
  Timer? _debounce;
  int _requestCounter = 0;
  bool _isSearching = false;
  bool _isLoadingInitial = false;
  String? _error;
  bool _isApplyingText = false;

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

  @override
  void initState() {
    super.initState();
    _selected = widget.initialSuggestion;
    if (_selected != null) {
      _applySuggestionSilently(_selected!);
    }
    widget.productController.addListener(_handleTextChanged);
    _loadInitialOptions();
  }

  @override
  void didUpdateWidget(covariant InventoryProductAutocompleteField oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (!identical(oldWidget.productController, widget.productController)) {
      oldWidget.productController.removeListener(_handleTextChanged);
      widget.productController.addListener(_handleTextChanged);
      _scheduleSearch(widget.productController.text.trim());
    }
    if (widget.initialSuggestion != oldWidget.initialSuggestion) {
      if (widget.initialSuggestion != null) {
        _selected = widget.initialSuggestion;
        _applySuggestionSilently(widget.initialSuggestion!);
      } else {
        _selected = null;
      }
    }
  }

  Future<void> _loadInitialOptions() async {
    final repository = _repository;
    if (repository == null) {
      setState(() {
        _error = '제품 데이터 소스를 찾을 수 없습니다.';
        _suggestions.clear();
        _initialOptions.clear();
      });
      return;
    }
    setState(() {
      _isLoadingInitial = true;
      _error = null;
    });
    try {
      final products = await fetchAllPaginatedItems<Product>(
        request: (page, pageSize) =>
            repository.list(page: page, pageSize: pageSize, isActive: true),
      );
      if (!mounted) {
        return;
      }
      final options = products
          .map(InventoryProductSuggestion.fromProduct)
          .toList(growable: false);
      setState(() {
        _initialOptions
          ..clear()
          ..addAll(options);
        if (_selected != null &&
            !_initialOptions.any((item) => item.id == _selected!.id)) {
          _initialOptions.add(_selected!);
        }
        _suggestions
          ..clear()
          ..addAll(_initialOptions);
        _isLoadingInitial = false;
      });
    } catch (error) {
      if (!mounted) {
        return;
      }
      setState(() {
        final failure = Failure.from(error);
        _error = failure.describe();
        _initialOptions.clear();
        _suggestions.clear();
        _isLoadingInitial = false;
      });
    }
  }

  void _cacheSuggestion(InventoryProductSuggestion suggestion) {
    if (!_initialOptions.any((item) => item.id == suggestion.id)) {
      _initialOptions.add(suggestion);
    }
    if (!_suggestions.any((item) => item.id == suggestion.id)) {
      _suggestions.add(suggestion);
    }
  }

  void _syncExternalControllers(InventoryProductSuggestion suggestion) {
    if (widget.manufacturerController.text != suggestion.vendorName) {
      widget.manufacturerController.text = suggestion.vendorName;
    }
    if (widget.unitController.text != suggestion.unitName) {
      widget.unitController.text = suggestion.unitName;
    }
  }

  void _applySuggestionSilently(InventoryProductSuggestion suggestion) {
    _selected = suggestion;
    _cacheSuggestion(suggestion);
    _updateProductFieldValue(suggestion.name);
    _syncExternalControllers(suggestion);
  }

  /// 입력 변화에 따라 제안 검색을 예약한다.
  void _handleTextChanged() {
    if (_isApplyingText) {
      return;
    }
    final text = widget.productController.text.trim();
    if (text.isEmpty) {
      _debounce?.cancel();
      _clearSelection();
      return;
    }
    if (_selected != null && text != _selected!.name) {
      _selected = null;
      widget.onSuggestionSelected(null);
    }
    _scheduleSearch(text);
    widget.onChanged?.call();
  }

  void _scheduleSearch(String keyword) {
    _debounce?.cancel();
    if (keyword.isEmpty) {
      setState(() {
        _isSearching = false;
        _suggestions.clear();
      });
      return;
    }
    _debounce = Timer(_debounceDuration, () => _fetchSuggestions(keyword));
  }

  Future<void> _fetchSuggestions(String keyword) async {
    final repository = _repository;
    if (repository == null) {
      return;
    }
    final currentRequest = ++_requestCounter;
    setState(() {
      _isSearching = true;
    });
    try {
      final result = await repository.list(
        page: 1,
        pageSize: 12,
        query: keyword,
        isActive: true,
      );
      if (!mounted || currentRequest != _requestCounter) {
        return;
      }
      final fetched = result.items
          .map(InventoryProductSuggestion.fromProduct)
          .toList(growable: false);
      setState(() {
        for (final option in fetched) {
          if (!_initialOptions.any((item) => item.id == option.id)) {
            _initialOptions.add(option);
          }
        }
        _suggestions
          ..clear()
          ..addAll(fetched);
        _isSearching = false;
      });
    } catch (_) {
      if (!mounted || currentRequest != _requestCounter) {
        return;
      }
      setState(() {
        _suggestions
          ..clear()
          ..addAll(_initialOptions);
        _isSearching = false;
      });
    }
  }

  /// 선택된 제안 정보를 외부 필드에 반영한다.
  void _applySuggestion(
    InventoryProductSuggestion suggestion, {
    bool updateProductField = true,
    bool notify = true,
  }) {
    _debounce?.cancel();
    setState(() {
      _selected = suggestion;
      _cacheSuggestion(suggestion);
    });
    if (notify) {
      widget.onSuggestionSelected(suggestion);
    }
    if (updateProductField) {
      _updateProductFieldValue(suggestion.name);
    }
    _syncExternalControllers(suggestion);
    if (kDebugMode) {
      debugPrint(
        '[InventoryProductAutocomplete] selected "${suggestion.name}" '
        '-> text="${widget.productController.text}"',
      );
    }
    widget.onChanged?.call();
  }

  void _clearSelection({bool notify = true}) {
    if (_selected == null &&
        widget.manufacturerController.text.isEmpty &&
        widget.unitController.text.isEmpty &&
        widget.productController.text.isEmpty) {
      setState(() {
        _suggestions
          ..clear()
          ..addAll(_initialOptions);
        _isSearching = false;
      });
      return;
    }
    _debounce?.cancel();
    setState(() {
      _selected = null;
      _suggestions
        ..clear()
        ..addAll(_initialOptions);
      _isSearching = false;
    });
    if (notify) {
      widget.onSuggestionSelected(null);
    }
    widget.manufacturerController.clear();
    widget.unitController.clear();
    if (widget.productController.text.isEmpty) {
      _updateProductFieldValue('');
    }
    widget.onChanged?.call();
  }

  /// 제품 입력 필드에 텍스트와 커서를 안전하게 반영한다.
  void _updateProductFieldValue(String text) {
    final controller = widget.productController;
    _isApplyingText = true;
    controller.value = controller.value.copyWith(
      text: text,
      selection: TextSelection.collapsed(offset: text.length),
      composing: TextRange.empty,
    );
    _isApplyingText = false;
  }

  @override
  void dispose() {
    widget.productController.removeListener(_handleTextChanged);
    _debounce?.cancel();
    super.dispose();
  }

  Widget _buildLoadingInput() {
    return Stack(
      alignment: Alignment.centerLeft,
      children: const [
        ShadInput(readOnly: true),
        Padding(
          padding: EdgeInsets.only(left: 12),
          child: SizedBox(
            width: 16,
            height: 16,
            child: CircularProgressIndicator(strokeWidth: 2),
          ),
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    final theme = ShadTheme.of(context);
    if (_isLoadingInitial) {
      return _buildLoadingInput();
    }
    if (_error != null) {
      return ShadInput(
        controller: widget.productController,
        focusNode: widget.productFocusNode,
        enabled: false,
        readOnly: true,
        placeholder: Text(_error!),
      );
    }
    return LayoutBuilder(
      builder: (context, constraints) {
        return RawAutocomplete<InventoryProductSuggestion>(
          textEditingController: widget.productController,
          focusNode: widget.productFocusNode,
          optionsBuilder: (textEditingValue) {
            final keyword = textEditingValue.text.trim();
            final base = _suggestions.isEmpty ? _initialOptions : _suggestions;
            if (keyword.isEmpty) {
              return base;
            }
            final lowerKeyword = keyword.toLowerCase();
            return base.where((option) {
              return option.name.toLowerCase().contains(lowerKeyword) ||
                  option.code.toLowerCase().contains(lowerKeyword) ||
                  option.vendorName.toLowerCase().contains(lowerKeyword);
            });
          },
          displayStringForOption: (option) => option.name,
          onSelected: (option) {
            _applySuggestion(option);
          },
          fieldViewBuilder:
              (context, textController, focusNode, onFieldSubmitted) {
                final input = ShadInput(
                  controller: textController,
                  focusNode: focusNode,
                  placeholder: const Text('제품명 검색'),
                  onChanged: (_) => widget.onChanged?.call(),
                  onSubmitted: (_) => onFieldSubmitted(),
                );
                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 (!widget.productFocusNode.hasPrimaryFocus) {
                    widget.productFocusNode.requestFocus();
                  }
                },
                child: Align(
                  alignment: AlignmentDirectional.topStart,
                  child: ConstrainedBox(
                    constraints: BoxConstraints(
                      maxWidth: constraints.maxWidth,
                      maxHeight: 220,
                    ),
                    child: Material(
                      elevation: 6,
                      color: theme.colorScheme.background,
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(12),
                        side: BorderSide(color: theme.colorScheme.border),
                      ),
                      child: Center(
                        child: Padding(
                          padding: const EdgeInsets.symmetric(vertical: 20),
                          child: Text(
                            '검색 결과가 없습니다.',
                            style: theme.textTheme.muted,
                          ),
                        ),
                      ),
                    ),
                  ),
                ),
              );
            }
            return Listener(
              onPointerDown: (_) {
                if (!widget.productFocusNode.hasPrimaryFocus) {
                  widget.productFocusNode.requestFocus();
                }
              },
              child: Align(
                alignment: AlignmentDirectional.topStart,
                child: ConstrainedBox(
                  constraints: BoxConstraints(
                    maxWidth: constraints.maxWidth,
                    maxHeight: 260,
                  ),
                  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 option = options.elementAt(index);
                        return GestureDetector(
                          behavior: HitTestBehavior.opaque,
                          onTapDown: (_) => 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(
                                  '${option.code} · ${option.vendorName} · ${option.unitName}',
                                  style: theme.textTheme.muted.copyWith(
                                    fontSize: 12,
                                  ),
                                ),
                              ],
                            ),
                          ),
                        );
                      },
                    ),
                  ),
                ),
              ),
            );
          },
        );
      },
    );
  }
}
