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/core/common/utils/pagination_utils.dart';
import 'package:superport_v2/core/network/failure.dart';
import 'package:superport_v2/features/masters/customer/domain/entities/customer.dart';
import 'package:superport_v2/features/masters/customer/domain/repositories/customer_repository.dart';

/// 파트너사를 나타내는 선택 옵션 모델.
class InventoryPartnerOption {
  InventoryPartnerOption({
    required this.id,
    required this.name,
    required this.code,
  });

  factory InventoryPartnerOption.fromCustomer(Customer customer) {
    return InventoryPartnerOption(
      id: customer.id ?? 0,
      name: customer.customerName,
      code: customer.customerCode,
    );
  }

  final int id;
  final String name;
  final String code;

  String get label => code.isEmpty ? name : '$name ($code)';
}

/// 파트너사 자동완성 입력 필드.
class InventoryPartnerSelectField extends StatefulWidget {
  const InventoryPartnerSelectField({
    super.key,
    this.initialPartner,
    required this.onChanged,
    this.enabled = true,
    this.placeholder,
  });

  final InventoryPartnerOption? initialPartner;
  final ValueChanged<InventoryPartnerOption?> onChanged;
  final bool enabled;
  final Widget? placeholder;

  @override
  State<InventoryPartnerSelectField> createState() =>
      _InventoryPartnerSelectFieldState();
}

class _InventoryPartnerSelectFieldState
    extends State<InventoryPartnerSelectField> {
  static const Duration _debounceDuration = Duration(milliseconds: 280);

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

  final TextEditingController _controller = TextEditingController();
  final FocusNode _focusNode = FocusNode();
  final List<InventoryPartnerOption> _baseOptions = [];
  final List<InventoryPartnerOption> _suggestions = [];
  InventoryPartnerOption? _selected;
  Timer? _debounce;
  bool _isSearching = false;
  bool _isLoadingInitial = false;
  String? _error;
  int _requestId = 0;
  bool _isApplyingText = false;

  @override
  void initState() {
    super.initState();
    _selected = widget.initialPartner;
    _controller.addListener(_handleTextChanged);
    _focusNode.addListener(_handleFocusChange);
    _loadInitialOptions();
  }

  @override
  void didUpdateWidget(covariant InventoryPartnerSelectField oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.initialPartner != oldWidget.initialPartner) {
      if (widget.initialPartner != null) {
        _setSelection(widget.initialPartner!, notify: false);
      } else {
        _clearSelection(notify: false);
      }
    }
  }

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

  Future<void> _loadInitialOptions() async {
    final repository = _repository;
    if (repository == null) {
      setState(() {
        _error = '파트너 데이터를 불러올 수 없습니다.';
      });
      return;
    }
    setState(() {
      _isLoadingInitial = true;
      _error = null;
    });
    try {
      final partners = await fetchAllPaginatedItems<Customer>(
        request: (page, pageSize) => repository.list(
          page: page,
          pageSize: pageSize,
          isPartner: true,
          isActive: true,
        ),
        pageSize: 40,
      );
      if (!mounted) {
        return;
      }
      final options = partners
          .where((customer) => customer.id != null)
          .map(InventoryPartnerOption.fromCustomer)
          .toList(growable: false);
      setState(() {
        _baseOptions
          ..clear()
          ..addAll(options);
        _suggestions
          ..clear()
          ..addAll(options);
        _isLoadingInitial = false;
      });
      final initial = widget.initialPartner;
      if (initial != null) {
        _setSelection(initial, notify: false);
      }
    } catch (error) {
      if (!mounted) {
        return;
      }
      final failure = Failure.from(error);
      setState(() {
        _error = failure.describe();
        _isLoadingInitial = false;
      });
    }
  }

  void _handleTextChanged() {
    if (_isApplyingText) {
      return;
    }
    final keyword = _controller.text.trim();
    if (keyword.isEmpty) {
      _debounce?.cancel();
      setState(() {
        _isSearching = false;
        _suggestions
          ..clear()
          ..addAll(_baseOptions);
      });
      return;
    }
    if (_selected != null && keyword != _selected!.label) {
      _selected = null;
      widget.onChanged(null);
    }
    _scheduleSearch(keyword);
  }

  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: 20,
        query: keyword,
        isPartner: true,
        isActive: true,
      );
      if (!mounted || request != _requestId) {
        return;
      }
      final items = result.items
          .where((customer) => customer.id != null)
          .map(InventoryPartnerOption.fromCustomer)
          .toList(growable: false);
      setState(() {
        _suggestions
          ..clear()
          ..addAll(items);
        _isSearching = false;
      });
    } catch (_) {
      if (!mounted || request != _requestId) {
        return;
      }
      setState(() {
        _suggestions
          ..clear()
          ..addAll(_baseOptions);
        _isSearching = false;
      });
    }
  }

  void _setSelection(InventoryPartnerOption option, {bool notify = true}) {
    setState(() {
      _selected = option;
      if (!_baseOptions.any((item) => item.id == option.id)) {
        _baseOptions.add(option);
      }
      _applyControllerText(option.label);
    });
    if (notify) {
      widget.onChanged(option);
    }
  }

  void _clearSelection({bool notify = true}) {
    setState(() {
      _selected = null;
      _applyControllerText('');
      _suggestions
        ..clear()
        ..addAll(_baseOptions);
    });
    if (notify) {
      widget.onChanged(null);
    }
  }

  void _applyControllerText(String text) {
    _isApplyingText = true;
    _controller
      ..text = text
      ..selection = TextSelection.collapsed(offset: text.length);
    _isApplyingText = false;
  }

  void _handleFocusChange() {
    if (!_focusNode.hasFocus && _selected != null) {
      _applyControllerText(_selected!.label);
    }
  }

  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) {
    if (_isLoadingInitial) {
      return _buildLoadingInput();
    }
    if (_error != null) {
      return ShadInput(
        readOnly: true,
        enabled: false,
        placeholder: Text(_error!),
      );
    }

    return RawAutocomplete<InventoryPartnerOption>(
      textEditingController: _controller,
      focusNode: _focusNode,
      optionsBuilder: (value) {
        final keyword = value.text.trim();
        if (keyword.isEmpty) {
          return _suggestions.isEmpty ? _baseOptions : _suggestions;
        }
        final lower = keyword.toLowerCase();
        final source = _suggestions.isEmpty ? _baseOptions : _suggestions;
        return source.where((option) {
          return option.name.toLowerCase().contains(lower) ||
              option.code.toLowerCase().contains(lower);
        });
      },
      displayStringForOption: (option) => option.label,
      onSelected: (option) {
        _setSelection(option);
      },
      fieldViewBuilder: (context, controller, focusNode, onSubmitted) {
        assert(identical(controller, _controller));
        final placeholder = widget.placeholder ?? const Text('파트너사를 선택하세요');
        final input = ShadInput(
          controller: controller,
          focusNode: focusNode,
          enabled: widget.enabled,
          readOnly: !widget.enabled,
          placeholder: placeholder,
          onSubmitted: (_) => onSubmitted(),
        );
        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 Align(
            alignment: Alignment.topLeft,
            child: ConstrainedBox(
              constraints: const BoxConstraints(maxHeight: 220, minWidth: 260),
              child: ShadCard(
                padding: const EdgeInsets.symmetric(vertical: 16),
                child: const Center(child: Text('검색 결과가 없습니다.')),
              ),
            ),
          );
        }
        return Align(
          alignment: Alignment.topLeft,
          child: ConstrainedBox(
            constraints: const BoxConstraints(maxHeight: 260, minWidth: 260),
            child: ShadCard(
              padding: EdgeInsets.zero,
              child: ListView.builder(
                shrinkWrap: true,
                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: ShadTheme.of(context).textTheme.p,
                          ),
                          if (option.code.isNotEmpty) ...[
                            const SizedBox(height: 4),
                            Text(
                              option.code,
                              style: ShadTheme.of(
                                context,
                              ).textTheme.muted.copyWith(fontSize: 12),
                            ),
                          ],
                        ],
                      ),
                    ),
                  );
                },
              ),
            ),
          ),
        );
      },
    );
  }
}
