// ChainSearchPalette — global Cmd+K command bar.
//
// Indexes: nav destinations, installed modules, store entries,
// saved flows. Filters client-side as the operator types.
// Selected result navigates / opens / acts in one keystroke.
//
// Triggered from the shell-level Shortcuts/Actions binding so
// the palette is reachable from every page.

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import '../data/hub.dart';
import '../l10n/app_localizations.dart';
import 'chain_module_sheet.dart';

/// One row in the palette. Carries everything needed to render
/// + dispatch — keep it serializable-shaped so we can persist
/// recent picks later.
class ChainSearchHit {
  final String label;
  final String hint;
  final IconData icon;

  /// Free-form group used for the section header in the
  /// results list ("Pages", "Modules", "Store", "Flows").
  final String group;

  /// Lowercase haystack — name + hint + group. Filtered against
  /// the lowercased query.
  final String haystack;

  /// Action to run when this hit is selected.
  final VoidCallback onSelect;

  ChainSearchHit({
    required this.label,
    required this.hint,
    required this.icon,
    required this.group,
    required this.onSelect,
  }) : haystack = '$label $hint $group'.toLowerCase();
}

class ChainSearchPalette extends StatefulWidget {
  /// Pages the operator can navigate to. Each entry maps to a
  /// callback that selects the right tab back in the shell.
  final List<ChainSearchHit> staticHits;

  const ChainSearchPalette({super.key, required this.staticHits});

  /// Convenience launcher — used from the Cmd+K shortcut.
  static Future<void> show(
    BuildContext context, {
    required List<ChainSearchHit> staticHits,
  }) {
    return showDialog<void>(
      context: context,
      barrierColor: Colors.black54,
      builder: (_) => ChainSearchPalette(staticHits: staticHits),
    );
  }

  @override
  State<ChainSearchPalette> createState() => _FaiSearchPaletteState();
}

class _FaiSearchPaletteState extends State<ChainSearchPalette> {
  final _query = TextEditingController();
  final _focus = FocusNode();
  Future<List<ChainSearchHit>>? _dynamicFuture;
  int _highlight = 0;

  @override
  void initState() {
    super.initState();
    _dynamicFuture = _loadDynamic();
    // Autofocus is handled by the TextField, but the focus
    // node also catches arrow keys for keyboard navigation.
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (mounted) _focus.requestFocus();
    });
  }

  @override
  void dispose() {
    _query.dispose();
    _focus.dispose();
    super.dispose();
  }

  /// Pull modules / store / flows in parallel so the palette
  /// is searchable as soon as the operator finishes typing the
  /// first character.
  Future<List<ChainSearchHit>> _loadDynamic() async {
    final svc = HubService.instance;
    final l = AppLocalizations.of(context)!;
    final results = await Future.wait([
      svc.listModules().catchError((_) => <ModuleSummary>[]),
      svc.searchStore(limit: 200).catchError((_) => <StoreItem>[]),
      svc.listFlows().catchError((_) => <SavedFlow>[]),
    ]);
    final modules = results[0] as List<ModuleSummary>;
    final store = results[1] as List<StoreItem>;
    final flows = results[2] as List<SavedFlow>;

    final hits = <ChainSearchHit>[];
    for (final m in modules) {
      hits.add(
        ChainSearchHit(
          label: m.name,
          hint: l.searchInstalledModuleHint(m.version),
          icon: Icons.extension,
          group: l.searchGroupModules,
          onSelect: () {
            Navigator.pop(context);
            ChainModuleSheet.show(context, m.name);
          },
        ),
      );
    }
    final storeGroup = l.searchGroupStore;
    final pagesGroup = l.searchGroupPages;
    for (final s in store) {
      hits.add(
        ChainSearchHit(
          label: s.name,
          hint: s.taglineEn.isEmpty
              ? l.searchStoreHintWithCategory(
                  s.category.isEmpty ? l.searchStoreUncategorized : s.category,
                )
              : l.searchStoreHintWithTagline(s.taglineEn),
          icon: Icons.storefront_outlined,
          group: storeGroup,
          // Selecting a store hit just closes the palette and
          // focuses the Store tab; deep-linking to the detail
          // sheet would require thread-through plumbing we don't
          // have yet.
          onSelect: () {
            Navigator.pop(context);
            for (final h in widget.staticHits) {
              if (h.label == storeGroup && h.group == pagesGroup) {
                h.onSelect();
                return;
              }
            }
          },
        ),
      );
    }
    final flowsGroup = l.searchGroupFlows;
    for (final f in flows) {
      hits.add(
        ChainSearchHit(
          label: f.name,
          hint: l.searchSavedFlowHint,
          icon: Icons.account_tree_outlined,
          group: flowsGroup,
          onSelect: () {
            Navigator.pop(context);
            for (final h in widget.staticHits) {
              if (h.label == flowsGroup && h.group == pagesGroup) {
                h.onSelect();
                return;
              }
            }
          },
        ),
      );
    }
    return hits;
  }

  /// Filtered + grouped result list. Empty query returns the
  /// pages first then everything else, capped to 50 rows so the
  /// dialog stays scrollable.
  List<ChainSearchHit> _resultsFor(List<ChainSearchHit> dynamic_, String raw) {
    final query = raw.trim().toLowerCase();
    final all = [...widget.staticHits, ...dynamic_];
    if (query.isEmpty) return all.take(50).toList();
    final scored = <(int, ChainSearchHit)>[];
    for (final h in all) {
      final i = h.haystack.indexOf(query);
      if (i < 0) continue;
      // Prefer label-prefix matches, then any substring.
      final isLabelPrefix = h.label.toLowerCase().startsWith(query);
      final score = (isLabelPrefix ? 0 : 1000) + i;
      scored.add((score, h));
    }
    scored.sort((a, b) => a.$1.compareTo(b.$1));
    return [for (final s in scored) s.$2].take(50).toList();
  }

  KeyEventResult _onKeyEvent(FocusNode node, KeyEvent event, int hitCount) {
    if (event is! KeyDownEvent && event is! KeyRepeatEvent) {
      return KeyEventResult.ignored;
    }
    if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
      setState(() {
        _highlight = (_highlight + 1).clamp(0, hitCount - 1);
      });
      return KeyEventResult.handled;
    }
    if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
      setState(() {
        _highlight = (_highlight - 1).clamp(0, hitCount - 1);
      });
      return KeyEventResult.handled;
    }
    if (event.logicalKey == LogicalKeyboardKey.escape) {
      Navigator.pop(context);
      return KeyEventResult.handled;
    }
    return KeyEventResult.ignored;
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final l = AppLocalizations.of(context)!;
    return Dialog(
      alignment: Alignment.topCenter,
      insetPadding: const EdgeInsets.only(top: 80, left: 24, right: 24),
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
      child: ConstrainedBox(
        constraints: const BoxConstraints(maxWidth: 640, maxHeight: 540),
        child: FutureBuilder<List<ChainSearchHit>>(
          future: _dynamicFuture,
          builder: (context, snap) {
            final dyn = snap.data ?? const <ChainSearchHit>[];
            return ValueListenableBuilder<TextEditingValue>(
              valueListenable: _query,
              builder: (_, value, _) {
                final hits = _resultsFor(dyn, value.text);
                if (_highlight >= hits.length) {
                  _highlight = hits.isEmpty ? 0 : hits.length - 1;
                }
                return Column(
                  mainAxisSize: MainAxisSize.min,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Padding(
                      padding: const EdgeInsets.fromLTRB(16, 12, 16, 0),
                      child: Focus(
                        focusNode: _focus,
                        onKeyEvent: (n, e) => _onKeyEvent(n, e, hits.length),
                        child: TextField(
                          controller: _query,
                          autofocus: true,
                          decoration: InputDecoration(
                            prefixIcon: const Icon(Icons.search, size: 20),
                            hintText: l.searchHint,
                            border: InputBorder.none,
                            suffixIcon:
                                snap.connectionState == ConnectionState.waiting
                                ? const SizedBox(
                                    width: 14,
                                    height: 14,
                                    child: CircularProgressIndicator(
                                      strokeWidth: 2,
                                    ),
                                  )
                                : null,
                          ),
                          style: theme.textTheme.titleMedium,
                          onSubmitted: (_) {
                            if (hits.isNotEmpty) {
                              hits[_highlight].onSelect();
                            }
                          },
                        ),
                      ),
                    ),
                    const Divider(height: 1),
                    Flexible(
                      child: hits.isEmpty
                          ? Padding(
                              padding: const EdgeInsets.all(24),
                              child: Text(
                                snap.connectionState == ConnectionState.waiting
                                    ? l.searchIndexing
                                    : l.searchNoMatches,
                                style: theme.textTheme.bodySmall?.copyWith(
                                  color: theme.colorScheme.onSurfaceVariant,
                                ),
                              ),
                            )
                          : ListView.builder(
                              padding: const EdgeInsets.symmetric(vertical: 4),
                              itemCount: hits.length,
                              itemBuilder: (context, i) {
                                final h = hits[i];
                                final highlighted = i == _highlight;
                                final showHeader =
                                    i == 0 || hits[i - 1].group != h.group;
                                return Column(
                                  crossAxisAlignment: CrossAxisAlignment.start,
                                  children: [
                                    if (showHeader)
                                      Padding(
                                        padding: const EdgeInsets.fromLTRB(
                                          16,
                                          8,
                                          16,
                                          4,
                                        ),
                                        child: Text(
                                          h.group.toUpperCase(),
                                          style: theme.textTheme.labelSmall
                                              ?.copyWith(
                                                color: theme
                                                    .colorScheme
                                                    .onSurfaceVariant,
                                                letterSpacing: 0.6,
                                                fontSize: 10,
                                              ),
                                        ),
                                      ),
                                    InkWell(
                                      onTap: h.onSelect,
                                      onHover: (hovered) {
                                        if (hovered) {
                                          setState(() => _highlight = i);
                                        }
                                      },
                                      child: Container(
                                        color: highlighted
                                            ? theme
                                                  .colorScheme
                                                  .surfaceContainerHigh
                                            : null,
                                        padding: const EdgeInsets.symmetric(
                                          horizontal: 16,
                                          vertical: 8,
                                        ),
                                        child: Row(
                                          children: [
                                            Icon(
                                              h.icon,
                                              size: 18,
                                              color: theme.colorScheme.primary,
                                            ),
                                            const SizedBox(width: 12),
                                            Expanded(
                                              child: Column(
                                                crossAxisAlignment:
                                                    CrossAxisAlignment.start,
                                                children: [
                                                  Text(
                                                    h.label,
                                                    style: theme
                                                        .textTheme
                                                        .bodyMedium
                                                        ?.copyWith(
                                                          fontWeight:
                                                              FontWeight.w500,
                                                        ),
                                                  ),
                                                  if (h.hint.isNotEmpty)
                                                    Text(
                                                      h.hint,
                                                      style: theme
                                                          .textTheme
                                                          .bodySmall
                                                          ?.copyWith(
                                                            color: theme
                                                                .colorScheme
                                                                .onSurfaceVariant,
                                                          ),
                                                      maxLines: 1,
                                                      overflow:
                                                          TextOverflow.ellipsis,
                                                    ),
                                                ],
                                              ),
                                            ),
                                            if (highlighted)
                                              Icon(
                                                Icons.keyboard_return,
                                                size: 14,
                                                color: theme
                                                    .colorScheme
                                                    .onSurfaceVariant,
                                              ),
                                          ],
                                        ),
                                      ),
                                    ),
                                  ],
                                );
                              },
                            ),
                    ),
                    const Divider(height: 1),
                    Padding(
                      padding: const EdgeInsets.symmetric(
                        horizontal: 16,
                        vertical: 6,
                      ),
                      child: Row(
                        children: [
                          _Hint(text: l.searchHintNavigate),
                          const SizedBox(width: 12),
                          _Hint(text: l.searchHintOpen),
                          const SizedBox(width: 12),
                          _Hint(text: l.searchHintClose),
                          const Spacer(),
                          Text(
                            l.storeNResults(hits.length),
                            style: theme.textTheme.bodySmall?.copyWith(
                              color: theme.colorScheme.onSurfaceVariant,
                            ),
                          ),
                        ],
                      ),
                    ),
                  ],
                );
              },
            );
          },
        ),
      ),
    );
  }
}

class _Hint extends StatelessWidget {
  final String text;
  const _Hint({required this.text});

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Text(
      text,
      style: theme.textTheme.bodySmall?.copyWith(
        color: theme.colorScheme.onSurfaceVariant,
        fontSize: 10,
      ),
    );
  }
}
