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

/// 창고 선택 옵션 모델.
class InventoryWarehouseOption {
  InventoryWarehouseOption({
    required this.id,
    required this.code,
    required this.name,
  });

  factory InventoryWarehouseOption.fromWarehouse(Warehouse warehouse) {
    return InventoryWarehouseOption(
      id: warehouse.id ?? 0,
      code: warehouse.warehouseCode,
      name: warehouse.warehouseName,
    );
  }

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

  @override
  String toString() => '$name ($code)';
}

/// 창고 목록을 불러와 `ShadSelect`로 노출하는 위젯.
///
/// - 서버에서 활성화된 창고를 조회해 옵션으로 구성한다.
/// - `includeAllOption`을 사용하면 '전체' 선택지를 함께 제공한다.
class InventoryWarehouseSelectField extends StatefulWidget {
  const InventoryWarehouseSelectField({
    super.key,
    this.initialWarehouseId,
    required this.onChanged,
    this.enabled = true,
    this.placeholder,
    this.includeAllOption = false,
    this.allLabel = '전체 창고',
  });

  final int? initialWarehouseId;
  final ValueChanged<InventoryWarehouseOption?> onChanged;
  final bool enabled;
  final Widget? placeholder;
  final bool includeAllOption;
  final String allLabel;

  @override
  State<InventoryWarehouseSelectField> createState() =>
      _InventoryWarehouseSelectFieldState();
}

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

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

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

  InventoryWarehouseOption get _allOption =>
      InventoryWarehouseOption(id: -1, code: 'ALL', name: widget.allLabel);

  @override
  void initState() {
    super.initState();
    _controller.addListener(_handleTextChanged);
    _loadInitialOptions();
  }

  @override
  void didUpdateWidget(covariant InventoryWarehouseSelectField oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.initialWarehouseId != oldWidget.initialWarehouseId) {
      _applyInitialSelection(allowFetch: true);
    }
  }

  @override
  void dispose() {
    _controller.removeListener(_handleTextChanged);
    _controller.dispose();
    _focusNode.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 warehouses = await fetchAllPaginatedItems<Warehouse>(
        request: (page, pageSize) => repository.list(
          page: page,
          pageSize: pageSize,
          isActive: true,
          includeZipcode: false,
        ),
      );
      final options = warehouses
          .map(InventoryWarehouseOption.fromWarehouse)
          .toList(growable: false);
      setState(() {
        _initialOptions
          ..clear()
          ..addAll(options);
        _suggestions
          ..clear()
          ..addAll(_availableOptions(options));
        _isLoadingInitial = false;
      });
      await _applyInitialSelection(options: options, allowFetch: true);
    } catch (error) {
      setState(() {
        final failure = Failure.from(error);
        _error = failure.describe();
        _isLoadingInitial = false;
      });
    }
  }

  List<InventoryWarehouseOption> _availableOptions(
    List<InventoryWarehouseOption> base,
  ) {
    if (!widget.includeAllOption) {
      return List<InventoryWarehouseOption>.from(base);
    }
    return <InventoryWarehouseOption>[_allOption, ...base];
  }

  Future<void> _applyInitialSelection({
    List<InventoryWarehouseOption>? options,
    bool allowFetch = false,
  }) async {
    final id = widget.initialWarehouseId;
    if (id == null) {
      if (_selected != null) {
        _setSelection(null, notify: true);
      }
      return;
    }
    final source = options ?? _initialOptions;
    final match = _findById(source, id);
    if (match != null) {
      _setSelection(match, notify: true);
      return;
    }
    if (!allowFetch) {
      return;
    }
    final repository = _repository;
    if (repository == null) {
      return;
    }
    try {
      final result = await repository.list(
        page: 1,
        pageSize: 20,
        query: id.toString(),
        isActive: true,
        includeZipcode: false,
      );
      final fetched = result.items
          .map(InventoryWarehouseOption.fromWarehouse)
          .toList(growable: false);
      final option = _findById(fetched, id);
      if (option == null) {
        return;
      }
      if (!_initialOptions.any((item) => item.id == option.id)) {
        _initialOptions.add(option);
      }
      setState(() {
        _suggestions
          ..clear()
          ..addAll(_availableOptions(_initialOptions));
      });
      _setSelection(option, notify: true);
    } catch (_) {
      // 초기 선정을 찾지 못하면 기본값 유지.
    }
  }

  InventoryWarehouseOption? _findById(
    List<InventoryWarehouseOption> options,
    int id,
  ) {
    for (final option in options) {
      if (option.id == id) {
        return option;
      }
    }
    return null;
  }

  void _handleTextChanged() {
    if (_isApplyingText) {
      return;
    }
    final keyword = _controller.text.trim();
    final selectedLabel = _selectedLabel;
    final matchesSelectedLabel =
        selectedLabel != null && keyword == selectedLabel;
    if (matchesSelectedLabel) {
      _debounce?.cancel();
      setState(() {
        _isSearching = false;
        _suggestions
          ..clear()
          ..addAll(_availableOptions(_initialOptions));
      });
      return;
    }
    if (keyword.isEmpty) {
      _debounce?.cancel();
      setState(() {
        _isSearching = false;
        _suggestions
          ..clear()
          ..addAll(_availableOptions(_initialOptions));
      });
      return;
    }

    final label = _selected == null
        ? null
        : _selected!.id == -1
        ? widget.allLabel
        : _displayLabel(_selected!);
    if (label != null && keyword != label) {
      _selected = null;
      widget.onChanged(null);
    }
    _scheduleSearch(keyword);
  }

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

  Future<void> _performSearch(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,
        isActive: true,
        includeZipcode: false,
      );
      if (!mounted || request != _requestId) {
        return;
      }
      final items = result.items
          .map(InventoryWarehouseOption.fromWarehouse)
          .toList(growable: false);
      setState(() {
        _suggestions
          ..clear()
          ..addAll(items);
        _isSearching = false;
      });
    } catch (_) {
      if (!mounted || request != _requestId) {
        return;
      }
      setState(() {
        _suggestions.clear();
        _isSearching = false;
      });
    }
  }

  void _setSelection(InventoryWarehouseOption? option, {bool notify = true}) {
    setState(() {
      if (option != null &&
          option.id != -1 &&
          !_initialOptions.any((item) => item.id == option.id)) {
        _initialOptions.add(option);
      }
      _selected = option;
      if (option == null) {
        _applyControllerText('');
      } else if (option.id == -1) {
        _applyControllerText(widget.allLabel);
      } else {
        _applyControllerText(_displayLabel(option));
      }
    });
    if (!notify) {
      return;
    }
    if (option == null || option.id == -1) {
      widget.onChanged(null);
    } else {
      widget.onChanged(option);
    }
  }

  String _displayLabel(InventoryWarehouseOption option) {
    return '${option.name} (${option.code})';
  }

  String? get _selectedLabel {
    final selected = _selected;
    if (selected == null) {
      return null;
    }
    return selected.id == -1 ? widget.allLabel : _displayLabel(selected);
  }

  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<InventoryWarehouseOption>(
      textEditingController: _controller,
      focusNode: _focusNode,
      optionsBuilder: (textEditingValue) {
        final keyword = textEditingValue.text.trim();
        final selectedLabel = _selectedLabel;
        final shouldShowAll =
            keyword.isEmpty ||
            (selectedLabel != null && keyword == selectedLabel);
        if (shouldShowAll) {
          return _availableOptions(_initialOptions);
        }
        return _suggestions;
      },
      displayStringForOption: (option) =>
          option.id == -1 ? widget.allLabel : _displayLabel(option),
      onSelected: (option) {
        if (!mounted) {
          return;
        }
        if (option.id == -1) {
          _setSelection(_allOption);
        } else {
          _setSelection(option);
        }
      },
      fieldViewBuilder: (context, textController, focusNode, onFieldSubmitted) {
        assert(identical(textController, _controller));
        final placeholder = widget.placeholder ?? const Text('창고 선택');
        return Stack(
          alignment: Alignment.centerRight,
          children: [
            ShadInput(
              controller: textController,
              focusNode: focusNode,
              enabled: widget.enabled,
              readOnly: !widget.enabled,
              placeholder: placeholder,
              onPressedOutside: (event) {
                // 포커스를 유지하지 않으면 드롭다운이 즉시 닫히므로 재요청한다.
                focusNode.requestFocus();
              },
            ),
            if (_isSearching)
              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: Alignment.topLeft,
              child: ConstrainedBox(
                constraints: const BoxConstraints(
                  maxHeight: 200,
                  minWidth: 260,
                ),
                child: ShadCard(
                  padding: const EdgeInsets.symmetric(vertical: 12),
                  child: const Center(child: Text('검색 결과가 없습니다.')),
                ),
              ),
            ),
          );
        }
        return Listener(
          onPointerDown: (_) {
            if (!_focusNode.hasPrimaryFocus) {
              _focusNode.requestFocus();
            }
          },
          child: Align(
            alignment: Alignment.topLeft,
            child: ConstrainedBox(
              constraints: const BoxConstraints(maxHeight: 240, minWidth: 260),
              child: ShadCard(
                padding: EdgeInsets.zero,
                child: ListView.builder(
                  shrinkWrap: true,
                  itemCount: options.length,
                  itemBuilder: (context, index) {
                    final option = options.elementAt(index);
                    final isAll = option.id == -1;
                    final label = isAll
                        ? widget.allLabel
                        : _displayLabel(option);
                    return InkWell(
                      onTap: () => onSelected(option),
                      child: Padding(
                        padding: const EdgeInsets.symmetric(
                          horizontal: 12,
                          vertical: 10,
                        ),
                        child: Text(label),
                      ),
                    );
                  },
                ),
              ),
            ),
          ),
        );
      },
    );
  }

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