import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import '../../../shared/theme/theme_extensions.dart';
// app_theme not required here; using theme extension tokens
import '../../../shared/widgets/sheet_handle.dart';

import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'dart:io' show Platform;
import 'dart:async';
import 'dart:ui';
import 'dart:math' as math;
import '../providers/chat_providers.dart';
import '../../tools/providers/tools_providers.dart';
import '../../prompts/providers/prompts_providers.dart';
import '../../../core/models/tool.dart';
import '../../../core/models/prompt.dart';
import '../../../core/providers/app_providers.dart';
import '../../../core/services/settings_service.dart';
import '../../chat/services/voice_input_service.dart';

import '../../../shared/utils/platform_utils.dart';
import 'package:conduit/l10n/app_localizations.dart';
import '../../../shared/widgets/modal_safe_area.dart';

class _SendMessageIntent extends Intent {
  const _SendMessageIntent();
}

class _InsertNewlineIntent extends Intent {
  const _InsertNewlineIntent();
}

class _SelectNextPromptIntent extends Intent {
  const _SelectNextPromptIntent();
}

class _SelectPreviousPromptIntent extends Intent {
  const _SelectPreviousPromptIntent();
}

class _DismissPromptIntent extends Intent {
  const _DismissPromptIntent();
}

class _PromptCommandMatch {
  const _PromptCommandMatch({
    required this.command,
    required this.start,
    required this.end,
  });

  final String command;
  final int start;
  final int end;
}

class ModernChatInput extends ConsumerStatefulWidget {
  final Function(String) onSendMessage;
  final bool enabled;
  final Function()? onVoiceInput;
  final Function()? onFileAttachment;
  final Function()? onImageAttachment;
  final Function()? onCameraCapture;

  const ModernChatInput({
    super.key,
    required this.onSendMessage,
    this.enabled = true,
    this.onVoiceInput,
    this.onFileAttachment,
    this.onImageAttachment,
    this.onCameraCapture,
  });

  @override
  ConsumerState<ModernChatInput> createState() => _ModernChatInputState();
}

// (Removed legacy _MicButton; inline mic logic now lives in primary button)

class _ModernChatInputState extends ConsumerState<ModernChatInput>
    with TickerProviderStateMixin {
  static const double _composerRadius = AppBorderRadius.card;

  final TextEditingController _controller = TextEditingController();
  final FocusNode _focusNode = FocusNode();
  bool _isRecording = false;
  // final String _voiceInputText = '';
  bool _hasText = false; // track locally without rebuilding on each keystroke
  StreamSubscription<String>? _voiceStreamSubscription;
  late VoiceInputService _voiceService;
  StreamSubscription<int>?
  _intensitySub; // removed usage; will be cleaned fully
  StreamSubscription<String>? _textSub;
  String _baseTextAtStart = '';
  bool _isDeactivated = false;
  int _lastHandledFocusTick = 0;
  bool _showPromptOverlay = false;
  String _currentPromptCommand = '';
  TextRange? _currentPromptRange;
  int _promptSelectionIndex = 0;

  @override
  void initState() {
    super.initState();
    _voiceService = ref.read(voiceInputServiceProvider);

    // Apply any prefilled text on first frame (focus handled via inputFocusTrigger)
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (!mounted || _isDeactivated) return;
      final text = ref.read(prefilledInputTextProvider);
      if (text != null && text.isNotEmpty) {
        _controller.text = text;
        _controller.selection = TextSelection.collapsed(offset: text.length);
        // Clear after applying so it doesn't re-apply on rebuilds
        ref.read(prefilledInputTextProvider.notifier).clear();
      }
    });

    // Removed ref.listen here; it must be used from build in this Riverpod version

    // Listen for text and selection changes in the composer
    _controller.addListener(_handleComposerChanged);

    // Publish focus changes to listeners
    _focusNode.addListener(() {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (!mounted || _isDeactivated) return;
        final hasFocus = _focusNode.hasFocus;
        // Publish composer focus state
        try {
          ref.read(composerHasFocusProvider.notifier).set(hasFocus);
        } catch (_) {}
      });
    });

    // Do not auto-focus on mount; only focus on explicit user intent
  }

  @override
  void dispose() {
    // Note: Avoid using ref in dispose as per Riverpod best practices
    // The focus state will be naturally cleared when the widget is disposed
    _controller.removeListener(_handleComposerChanged);
    _controller.dispose();
    _focusNode.dispose();
    _voiceStreamSubscription?.cancel();
    _intensitySub?.cancel();
    _textSub?.cancel();
    _voiceService.stopListening();
    super.dispose();
  }

  void _ensureFocusedIfEnabled() {
    if (!widget.enabled) return;
    if (!_focusNode.hasFocus) {
      // Use FocusNode directly to avoid depending on Inherited widgets
      _focusNode.requestFocus();
    }
  }

  @override
  void deactivate() {
    _isDeactivated = true;
    super.deactivate();
  }

  @override
  void activate() {
    super.activate();
    _isDeactivated = false;
  }

  @override
  void didUpdateWidget(covariant ModernChatInput oldWidget) {
    super.didUpdateWidget(oldWidget);
    // Avoid auto-focusing when becoming enabled; wait for user intent
    if (!widget.enabled && oldWidget.enabled) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (!mounted || _isDeactivated) return;
        if (_focusNode.hasFocus) {
          _focusNode.unfocus();
        }
      });
    }
  }

  void _sendMessage() {
    final text = _controller.text.trim();
    if (text.isEmpty || !widget.enabled) return;

    PlatformUtils.lightHaptic();
    widget.onSendMessage(text);
    _controller.clear();
    // Keep focus and keyboard open; do not collapse automatically
  }

  void _insertNewline() {
    final text = _controller.text;
    TextSelection sel = _controller.selection;
    final int start = sel.isValid ? sel.start : text.length;
    final int end = sel.isValid ? sel.end : text.length;
    final String before = text.substring(0, start);
    final String after = text.substring(end);
    final String updated = '$before\n$after';
    _controller.value = TextEditingValue(
      text: updated,
      selection: TextSelection.collapsed(offset: before.length + 1),
      composing: TextRange.empty,
    );
    // Ensure field stays focused
    _ensureFocusedIfEnabled();
  }

  static final RegExp _promptCommandBoundary = RegExp(r'\s');

  void _handleComposerChanged() {
    if (!mounted || _isDeactivated) return;

    final String text = _controller.text;
    final TextSelection selection = _controller.selection;
    final bool hasText = text.trim().isNotEmpty;
    final _PromptCommandMatch? match = _resolvePromptCommand(
      text,
      selection,
      widget.enabled,
    );
    final bool shouldShow = match != null;
    final bool wasShowing = _showPromptOverlay;
    final String previousCommand = _currentPromptCommand;

    bool needsUpdate = hasText != _hasText || shouldShow != _showPromptOverlay;

    if (!needsUpdate) {
      if (match != null) {
        final TextRange? range = _currentPromptRange;
        needsUpdate =
            previousCommand != match.command ||
            range == null ||
            range.start != match.start ||
            range.end != match.end;
      } else {
        needsUpdate =
            _currentPromptCommand.isNotEmpty || _currentPromptRange != null;
      }
    }

    if (!needsUpdate) return;

    setState(() {
      _hasText = hasText;
      if (match != null) {
        if (previousCommand != match.command) {
          _promptSelectionIndex = 0;
        }
        _currentPromptCommand = match.command;
        _currentPromptRange = TextRange(start: match.start, end: match.end);
        _showPromptOverlay = true;
      } else {
        _currentPromptCommand = '';
        _currentPromptRange = null;
        _promptSelectionIndex = 0;
        _showPromptOverlay = false;
      }
    });

    if (!wasShowing && shouldShow) {
      // Trigger prompt fetch lazily when overlay first appears
      ref.read(promptsListProvider.future);
    }
  }

  _PromptCommandMatch? _resolvePromptCommand(
    String text,
    TextSelection selection,
    bool enabled,
  ) {
    if (!enabled) return null;
    if (!selection.isValid || !selection.isCollapsed) return null;

    final int cursor = selection.start;
    if (cursor < 0 || cursor > text.length) return null;
    if (cursor == 0) return null;

    int start = cursor;
    while (start > 0) {
      final String previous = text.substring(start - 1, start);
      if (_promptCommandBoundary.hasMatch(previous)) {
        break;
      }
      start--;
    }

    final String candidate = text.substring(start, cursor);
    if (candidate.isEmpty || !candidate.startsWith('/')) {
      return null;
    }

    return _PromptCommandMatch(command: candidate, start: start, end: cursor);
  }

  List<Prompt> _filterPrompts(List<Prompt> prompts) {
    if (prompts.isEmpty) return const <Prompt>[];
    final String query = _currentPromptCommand.toLowerCase();

    final List<Prompt> filtered =
        prompts
            .where(
              (prompt) =>
                  prompt.command.toLowerCase().contains(query.trim()) &&
                  prompt.content.isNotEmpty,
            )
            .toList()
          ..sort((a, b) {
            final int titleCompare = a.title.toLowerCase().compareTo(
              b.title.toLowerCase(),
            );
            if (titleCompare != 0) return titleCompare;
            return a.command.toLowerCase().compareTo(b.command.toLowerCase());
          });

    return filtered;
  }

  void _movePromptSelection(int delta) {
    final AsyncValue<List<Prompt>> promptsAsync = ref.read(promptsListProvider);
    final List<Prompt>? prompts = promptsAsync.value;
    if (prompts == null || prompts.isEmpty) return;

    final List<Prompt> filtered = _filterPrompts(prompts);
    if (filtered.isEmpty) return;

    int newIndex = _promptSelectionIndex + delta;
    if (newIndex < 0) {
      newIndex = 0;
    } else if (newIndex >= filtered.length) {
      newIndex = filtered.length - 1;
    }
    if (newIndex == _promptSelectionIndex) return;

    setState(() {
      _promptSelectionIndex = newIndex;
    });
  }

  void _confirmPromptSelection() {
    final AsyncValue<List<Prompt>> promptsAsync = ref.read(promptsListProvider);
    final List<Prompt>? prompts = promptsAsync.value;
    if (prompts == null || prompts.isEmpty) return;

    final List<Prompt> filtered = _filterPrompts(prompts);
    if (filtered.isEmpty) return;

    int index = _promptSelectionIndex;
    if (index < 0) {
      index = 0;
    } else if (index >= filtered.length) {
      index = filtered.length - 1;
    }
    _applyPrompt(filtered[index]);
  }

  void _applyPrompt(Prompt prompt) {
    final TextRange? range = _currentPromptRange;
    if (range == null) return;

    final String text = _controller.text;
    final String before = text.substring(0, range.start);
    final String after = text.substring(range.end);
    final String content = prompt.content;
    final int caret = before.length + content.length;

    _controller.value = TextEditingValue(
      text: '$before$content$after',
      selection: TextSelection.collapsed(offset: caret),
      composing: TextRange.empty,
    );

    _ensureFocusedIfEnabled();

    setState(() {
      _showPromptOverlay = false;
      _currentPromptCommand = '';
      _currentPromptRange = null;
      _promptSelectionIndex = 0;
    });
  }

  void _hidePromptOverlay() {
    if (!_showPromptOverlay) return;
    setState(() {
      _showPromptOverlay = false;
      _currentPromptCommand = '';
      _currentPromptRange = null;
      _promptSelectionIndex = 0;
    });
  }

  Widget _buildPromptOverlay(BuildContext context) {
    final Brightness brightness = Theme.of(context).brightness;
    final overlayColor = context.conduitTheme.cardBackground;
    final borderColor = context.conduitTheme.cardBorder.withValues(
      alpha: brightness == Brightness.dark ? 0.6 : 0.4,
    );

    final AsyncValue<List<Prompt>> promptsAsync = ref.watch(
      promptsListProvider,
    );

    return Container(
      decoration: BoxDecoration(
        color: overlayColor,
        borderRadius: BorderRadius.circular(AppBorderRadius.card),
        border: Border.all(color: borderColor, width: BorderWidth.thin),
        boxShadow: [
          BoxShadow(
            color: context.conduitTheme.cardShadow.withValues(
              alpha: brightness == Brightness.dark ? 0.28 : 0.16,
            ),
            blurRadius: 22,
            offset: const Offset(0, 8),
            spreadRadius: -4,
          ),
        ],
      ),
      child: promptsAsync.when(
        data: (prompts) {
          final List<Prompt> filtered = _filterPrompts(prompts);
          if (filtered.isEmpty) {
            return _buildPromptOverlayPlaceholder(
              context,
              Icon(
                Icons.inbox_outlined,
                size: IconSize.medium,
                color: context.conduitTheme.textSecondary.withValues(
                  alpha: Alpha.medium,
                ),
              ),
              AppLocalizations.of(context)!.noResults,
            );
          }

          int activeIndex = _promptSelectionIndex;
          if (activeIndex < 0) {
            activeIndex = 0;
          } else if (activeIndex >= filtered.length) {
            activeIndex = filtered.length - 1;
          }

          return ConstrainedBox(
            constraints: const BoxConstraints(maxHeight: 240),
            child: ListView.separated(
              padding: const EdgeInsets.symmetric(vertical: Spacing.xs),
              shrinkWrap: true,
              physics: const ClampingScrollPhysics(),
              itemCount: filtered.length,
              separatorBuilder: (context, index) =>
                  const SizedBox(height: Spacing.xxs),
              itemBuilder: (context, index) {
                final prompt = filtered[index];
                final bool isSelected = index == activeIndex;
                final Color highlight = isSelected
                    ? context.conduitTheme.navigationSelectedBackground
                          .withValues(alpha: 0.4)
                    : Colors.transparent;

                return Material(
                  color: Colors.transparent,
                  child: InkWell(
                    borderRadius: BorderRadius.circular(AppBorderRadius.card),
                    onTap: () => _applyPrompt(prompt),
                    child: Container(
                      decoration: BoxDecoration(
                        color: highlight,
                        borderRadius: BorderRadius.circular(
                          AppBorderRadius.card,
                        ),
                      ),
                      padding: const EdgeInsets.symmetric(
                        horizontal: Spacing.sm,
                        vertical: Spacing.xs,
                      ),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(
                            prompt.command,
                            style: Theme.of(context).textTheme.bodyMedium
                                ?.copyWith(
                                  fontWeight: FontWeight.w600,
                                  color: context.conduitTheme.textPrimary,
                                ),
                          ),
                          if (prompt.title.trim().isNotEmpty)
                            Padding(
                              padding: const EdgeInsets.only(top: Spacing.xxs),
                              child: Text(
                                prompt.title,
                                style: Theme.of(context).textTheme.bodySmall
                                    ?.copyWith(
                                      color: context.conduitTheme.textSecondary,
                                    ),
                              ),
                            ),
                        ],
                      ),
                    ),
                  ),
                );
              },
            ),
          );
        },
        loading: () => _buildPromptOverlayPlaceholder(
          context,
          SizedBox(
            width: IconSize.large,
            height: IconSize.large,
            child: CircularProgressIndicator(
              strokeWidth: BorderWidth.regular,
              valueColor: AlwaysStoppedAnimation<Color>(
                context.conduitTheme.loadingIndicator,
              ),
            ),
          ),
          null,
        ),
        error: (error, stackTrace) => _buildPromptOverlayPlaceholder(
          context,
          Icon(
            Icons.error_outline,
            size: IconSize.medium,
            color: context.conduitTheme.error,
          ),
          null,
        ),
      ),
    );
  }

  Widget _buildPromptOverlayPlaceholder(
    BuildContext context,
    Widget leading,
    String? message,
  ) {
    return Padding(
      padding: const EdgeInsets.symmetric(
        horizontal: Spacing.sm,
        vertical: Spacing.md,
      ),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          leading,
          if (message != null) ...[
            const SizedBox(width: Spacing.sm),
            Flexible(
              child: Text(
                message,
                style: Theme.of(context).textTheme.bodySmall?.copyWith(
                  color: context.conduitTheme.textSecondary,
                ),
              ),
            ),
          ],
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    ref.listen<String?>(prefilledInputTextProvider, (previous, next) {
      final incoming = next?.trim();
      if (incoming == null || incoming.isEmpty) return;
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (!mounted || _isDeactivated) return;
        _controller.text = incoming;
        _controller.selection = TextSelection.collapsed(
          offset: incoming.length,
        );
        try {
          ref.read(prefilledInputTextProvider.notifier).clear();
        } catch (_) {}
      });
    });

    final messages = ref.watch(chatMessagesProvider);
    final isGenerating =
        messages.isNotEmpty &&
        messages.last.role == 'assistant' &&
        messages.last.isStreaming;
    final stopGeneration = ref.read(stopGenerationProvider);

    final webSearchEnabled = ref.watch(webSearchEnabledProvider);
    final imageGenEnabled = ref.watch(imageGenerationEnabledProvider);
    final imageGenAvailable = ref.watch(imageGenerationAvailableProvider);
    final selectedQuickPills = ref.watch(
      appSettingsProvider.select((s) => s.quickPills),
    );
    final sendOnEnter = ref.watch(
      appSettingsProvider.select((s) => s.sendOnEnter),
    );
    final toolsAsync = ref.watch(toolsListProvider);
    final List<Tool> availableTools = toolsAsync.maybeWhen<List<Tool>>(
      data: (t) => t,
      orElse: () => const <Tool>[],
    );
    final bool showWebPill = selectedQuickPills.contains('web');
    final bool showImagePillPref = selectedQuickPills.contains('image');
    final voiceAvailableAsync = ref.watch(voiceInputAvailableProvider);
    final bool voiceAvailable = voiceAvailableAsync.maybeWhen(
      data: (v) => v,
      orElse: () => false,
    );
    final selectedToolIds = ref.watch(selectedToolIdsProvider);

    final focusTick = ref.watch(inputFocusTriggerProvider);
    if (focusTick != _lastHandledFocusTick) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (!mounted || _isDeactivated) return;
        _ensureFocusedIfEnabled();
        _lastHandledFocusTick = focusTick;
      });
    }

    final Brightness brightness = Theme.of(context).brightness;
    final bool isActive = _focusNode.hasFocus || _hasText;
    final Color composerSurface = context.conduitTheme.inputBackground;
    final Color composerBackground = brightness == Brightness.dark
        ? composerSurface.withValues(alpha: 0.78)
        : context.conduitTheme.surfaceContainerHighest;
    final Color placeholderBase = context.conduitTheme.inputPlaceholder;
    final Color placeholderFocused = context.conduitTheme.inputText.withValues(
      alpha: 0.64,
    );
    final Color outlineColor = Color.lerp(
      context.conduitTheme.inputBorder,
      context.conduitTheme.inputBorderFocused,
      isActive ? 1.0 : 0.0,
    )!.withValues(alpha: brightness == Brightness.dark ? 0.55 : 0.45);
    final Color shellShadowColor = context.conduitTheme.cardShadow.withValues(
      alpha: brightness == Brightness.dark
          ? 0.22 + (isActive ? 0.08 : 0.0)
          : 0.12 + (isActive ? 0.06 : 0.0),
    );

    final List<Widget> quickPills = <Widget>[];

    for (final id in selectedQuickPills) {
      if (id == 'web' && showWebPill) {
        final String label = AppLocalizations.of(context)!.web;
        final IconData icon = Platform.isIOS
            ? CupertinoIcons.search
            : Icons.search;
        void handleTap() {
          final notifier = ref.read(webSearchEnabledProvider.notifier);
          notifier.set(!webSearchEnabled);
        }

        quickPills.add(
          _buildPillButton(
            icon: icon,
            label: label,
            isActive: webSearchEnabled,
            onTap: widget.enabled && !_isRecording ? handleTap : null,
          ),
        );
      } else if (id == 'image' && showImagePillPref && imageGenAvailable) {
        final String label = AppLocalizations.of(context)!.imageGen;
        final IconData icon = Platform.isIOS
            ? CupertinoIcons.photo
            : Icons.image;
        void handleTap() {
          final notifier = ref.read(imageGenerationEnabledProvider.notifier);
          notifier.set(!imageGenEnabled);
        }

        quickPills.add(
          _buildPillButton(
            icon: icon,
            label: label,
            isActive: imageGenEnabled,
            onTap: widget.enabled && !_isRecording ? handleTap : null,
          ),
        );
      } else {
        Tool? tool;
        for (final t in availableTools) {
          if (t.id == id) {
            tool = t;
            break;
          }
        }
        if (tool != null) {
          final bool isSelected = selectedToolIds.contains(id);
          final String label = tool.name;
          final IconData icon = Platform.isIOS
              ? CupertinoIcons.wrench
              : Icons.build;

          void handleTap() {
            final current = List<String>.from(selectedToolIds);
            if (current.contains(id)) {
              current.remove(id);
            } else {
              current.add(id);
            }
            ref.read(selectedToolIdsProvider.notifier).set(current);
          }

          quickPills.add(
            _buildPillButton(
              icon: icon,
              label: label,
              isActive: isSelected,
              onTap: widget.enabled && !_isRecording ? handleTap : null,
            ),
          );
        }
      }
    }

    Widget shell = AnimatedContainer(
      duration: const Duration(milliseconds: 180),
      curve: Curves.easeOutCubic,
      decoration: BoxDecoration(
        color: composerBackground,
        borderRadius: BorderRadius.circular(_composerRadius),
        border: Border.all(color: outlineColor, width: BorderWidth.thin),
        boxShadow: [
          BoxShadow(
            color: shellShadowColor,
            blurRadius: 12 + (isActive ? 4 : 0),
            spreadRadius: -2,
            offset: const Offset(0, -2),
          ),
        ],
      ),
      width: double.infinity,
      child: Padding(
        padding: EdgeInsets.only(
          bottom: MediaQuery.of(context).viewPadding.bottom,
        ),
        child: ConstrainedBox(
          constraints: BoxConstraints(
            maxHeight: MediaQuery.of(context).size.height * 0.4,
          ),
          child: AnimatedSize(
            duration: const Duration(milliseconds: 160),
            curve: Curves.easeOutCubic,
            alignment: Alignment.topCenter,
            child: SingleChildScrollView(
              physics: const ClampingScrollPhysics(),
              child: RepaintBoundary(
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    if (_showPromptOverlay)
                      Padding(
                        padding: const EdgeInsets.fromLTRB(
                          Spacing.sm,
                          0,
                          Spacing.sm,
                          Spacing.xs,
                        ),
                        child: _buildPromptOverlay(context),
                      ),
                    Padding(
                      padding: const EdgeInsets.fromLTRB(
                        Spacing.sm,
                        Spacing.xs,
                        Spacing.sm,
                        Spacing.xs,
                      ),
                      child: Container(
                        padding: const EdgeInsets.fromLTRB(
                          Spacing.sm,
                          Spacing.xs,
                          Spacing.sm,
                          Spacing.xs,
                        ),
                        decoration: BoxDecoration(
                          color: Colors.transparent,
                          borderRadius: BorderRadius.circular(_composerRadius),
                        ),
                        child: Row(
                          crossAxisAlignment: CrossAxisAlignment.center,
                          children: [
                            Expanded(
                              child: GestureDetector(
                                behavior: HitTestBehavior.opaque,
                                onTap: () {
                                  if (!widget.enabled) return;
                                  _ensureFocusedIfEnabled();
                                },
                                child: Semantics(
                                  textField: true,
                                  label: AppLocalizations.of(
                                    context,
                                  )!.messageInputLabel,
                                  hint: AppLocalizations.of(
                                    context,
                                  )!.messageInputHint,
                                  child: Shortcuts(
                                    shortcuts: () {
                                      final map = <LogicalKeySet, Intent>{
                                        LogicalKeySet(
                                          LogicalKeyboardKey.meta,
                                          LogicalKeyboardKey.enter,
                                        ): const _SendMessageIntent(),
                                        LogicalKeySet(
                                          LogicalKeyboardKey.control,
                                          LogicalKeyboardKey.enter,
                                        ): const _SendMessageIntent(),
                                      };
                                      if (sendOnEnter) {
                                        map[LogicalKeySet(
                                              LogicalKeyboardKey.enter,
                                            )] =
                                            const _SendMessageIntent();
                                        map[LogicalKeySet(
                                              LogicalKeyboardKey.shift,
                                              LogicalKeyboardKey.enter,
                                            )] =
                                            const _InsertNewlineIntent();
                                      }
                                      if (_showPromptOverlay) {
                                        map[LogicalKeySet(
                                              LogicalKeyboardKey.arrowDown,
                                            )] =
                                            const _SelectNextPromptIntent();
                                        map[LogicalKeySet(
                                              LogicalKeyboardKey.arrowUp,
                                            )] =
                                            const _SelectPreviousPromptIntent();
                                        map[LogicalKeySet(
                                              LogicalKeyboardKey.escape,
                                            )] =
                                            const _DismissPromptIntent();
                                      }
                                      return map;
                                    }(),
                                    child: Actions(
                                      actions: <Type, Action<Intent>>{
                                        _SendMessageIntent:
                                            CallbackAction<_SendMessageIntent>(
                                              onInvoke: (intent) {
                                                if (_showPromptOverlay) {
                                                  _confirmPromptSelection();
                                                  return null;
                                                }
                                                _sendMessage();
                                                return null;
                                              },
                                            ),
                                        _InsertNewlineIntent:
                                            CallbackAction<
                                              _InsertNewlineIntent
                                            >(
                                              onInvoke: (intent) {
                                                _insertNewline();
                                                return null;
                                              },
                                            ),
                                        _SelectNextPromptIntent:
                                            CallbackAction<
                                              _SelectNextPromptIntent
                                            >(
                                              onInvoke: (intent) {
                                                _movePromptSelection(1);
                                                return null;
                                              },
                                            ),
                                        _SelectPreviousPromptIntent:
                                            CallbackAction<
                                              _SelectPreviousPromptIntent
                                            >(
                                              onInvoke: (intent) {
                                                _movePromptSelection(-1);
                                                return null;
                                              },
                                            ),
                                        _DismissPromptIntent:
                                            CallbackAction<
                                              _DismissPromptIntent
                                            >(
                                              onInvoke: (intent) {
                                                _hidePromptOverlay();
                                                return null;
                                              },
                                            ),
                                      },
                                      child: TweenAnimationBuilder<double>(
                                        tween: Tween<double>(
                                          begin: 0.0,
                                          end: isActive ? 1.0 : 0.0,
                                        ),
                                        duration: const Duration(
                                          milliseconds: 180,
                                        ),
                                        curve: Curves.easeOutCubic,
                                        builder: (context, factor, child) {
                                          final Color animatedPlaceholder =
                                              Color.lerp(
                                                placeholderBase,
                                                placeholderFocused,
                                                factor,
                                              )!;
                                          final Color animatedTextColor =
                                              Color.lerp(
                                                context.conduitTheme.inputText
                                                    .withValues(alpha: 0.88),
                                                context.conduitTheme.inputText,
                                                factor,
                                              )!;

                                          final FontWeight recordingWeight =
                                              _isRecording
                                              ? FontWeight.w500
                                              : FontWeight.w400;
                                          final TextStyle baseChatStyle =
                                              AppTypography.chatMessageStyle;

                                          return TextField(
                                            controller: _controller,
                                            focusNode: _focusNode,
                                            enabled: widget.enabled,
                                            autofocus: false,
                                            minLines: 1,
                                            maxLines: null,
                                            keyboardType:
                                                TextInputType.multiline,
                                            textCapitalization:
                                                TextCapitalization.sentences,
                                            textInputAction: sendOnEnter
                                                ? TextInputAction.send
                                                : TextInputAction.newline,
                                            autofillHints: const <String>[],
                                            showCursor: true,
                                            scrollPadding:
                                                const EdgeInsets.only(
                                                  bottom: 80,
                                                ),
                                            keyboardAppearance: brightness,
                                            cursorColor: animatedTextColor,
                                            style: baseChatStyle.copyWith(
                                              color: animatedTextColor,
                                              fontStyle: _isRecording
                                                  ? FontStyle.italic
                                                  : FontStyle.normal,
                                              fontWeight: recordingWeight,
                                            ),
                                            decoration: InputDecoration(
                                              hintText: AppLocalizations.of(
                                                context,
                                              )!.messageHintText,
                                              hintStyle: baseChatStyle.copyWith(
                                                color: animatedPlaceholder,
                                                fontWeight: recordingWeight,
                                                fontStyle: _isRecording
                                                    ? FontStyle.italic
                                                    : FontStyle.normal,
                                              ),
                                              filled: false,
                                              border: InputBorder.none,
                                              enabledBorder: InputBorder.none,
                                              focusedBorder: InputBorder.none,
                                              errorBorder: InputBorder.none,
                                              disabledBorder: InputBorder.none,
                                              contentPadding:
                                                  const EdgeInsets.symmetric(
                                                    horizontal: Spacing.sm,
                                                    vertical: Spacing.xs,
                                                  ),
                                              isDense: true,
                                              alignLabelWithHint: true,
                                            ),
                                            onSubmitted: (_) {
                                              if (sendOnEnter) {
                                                _sendMessage();
                                              }
                                            },
                                            onTap: () {
                                              if (!widget.enabled) return;
                                              _ensureFocusedIfEnabled();
                                            },
                                          );
                                        },
                                      ),
                                    ),
                                  ),
                                ),
                              ),
                            ),
                          ],
                        ),
                      ),
                    ),
                    Padding(
                      padding: const EdgeInsets.fromLTRB(
                        Spacing.inputPadding - Spacing.xs,
                        0,
                        Spacing.lg + Spacing.xs,
                        0,
                      ),
                      child: Row(
                        children: [
                          _buildOverflowButton(
                            tooltip: AppLocalizations.of(context)!.more,
                            webSearchActive: webSearchEnabled,
                            imageGenerationActive: imageGenEnabled,
                            toolsActive: selectedToolIds.isNotEmpty,
                          ),
                          const SizedBox(width: Spacing.xs),
                          Expanded(
                            child: quickPills.isEmpty
                                ? const SizedBox.shrink()
                                : ClipRect(
                                    child: SingleChildScrollView(
                                      scrollDirection: Axis.horizontal,
                                      physics: const BouncingScrollPhysics(),
                                      child: Row(
                                        mainAxisSize: MainAxisSize.min,
                                        children: _withHorizontalSpacing(
                                          quickPills,
                                          Spacing.xxs,
                                        ),
                                      ),
                                    ),
                                  ),
                          ),
                          const SizedBox(width: Spacing.sm),
                          Row(
                            mainAxisSize: MainAxisSize.min,
                            children: [
                              _buildPrimaryButton(
                                _hasText,
                                isGenerating,
                                stopGeneration,
                                voiceAvailable,
                              ),
                            ],
                          ),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ),
        ),
      ),
    );

    if (brightness == Brightness.dark) {
      shell = ClipRRect(
        borderRadius: BorderRadius.circular(_composerRadius),
        child: BackdropFilter(
          filter: ImageFilter.blur(sigmaX: 12, sigmaY: 12),
          child: shell,
        ),
      );
    }

    return Container(
      color: Colors.transparent,
      padding: EdgeInsets.zero,
      child: Column(mainAxisSize: MainAxisSize.min, children: [shell]),
    );
  }

  // (Removed legacy _buildVoiceButton; mic functionality moved to primary button)

  List<Widget> _withHorizontalSpacing(List<Widget> children, double gap) {
    if (children.length <= 1) {
      return List<Widget>.from(children);
    }
    final result = <Widget>[];
    for (var i = 0; i < children.length; i++) {
      result.add(children[i]);
      if (i != children.length - 1) {
        result.add(SizedBox(width: gap));
      }
    }
    return result;
  }

  Widget _buildOverflowButton({
    required String tooltip,
    required bool webSearchActive,
    required bool imageGenerationActive,
    required bool toolsActive,
  }) {
    final bool enabled = widget.enabled && !_isRecording;

    IconData icon;
    Color? activeColor;
    if (webSearchActive) {
      icon = Platform.isIOS ? CupertinoIcons.search : Icons.search;
      activeColor = context.conduitTheme.buttonPrimary;
    } else if (imageGenerationActive) {
      icon = Platform.isIOS ? CupertinoIcons.photo : Icons.image;
      activeColor = context.conduitTheme.buttonPrimary;
    } else if (toolsActive) {
      icon = Platform.isIOS ? CupertinoIcons.wrench : Icons.build;
      activeColor = context.conduitTheme.buttonPrimary;
    } else {
      icon = Platform.isIOS ? CupertinoIcons.add : Icons.add;
      activeColor = null;
    }

    const double iconSize = IconSize.large;

    final Color iconColor = !enabled
        ? context.conduitTheme.textPrimary.withValues(alpha: Alpha.disabled)
        : (activeColor ??
              context.conduitTheme.textPrimary.withValues(alpha: Alpha.strong));

    return Tooltip(
      message: tooltip,
      child: Opacity(
        opacity: enabled ? 1.0 : Alpha.disabled,
        child: SizedBox(
          width: TouchTarget.minimum,
          height: TouchTarget.minimum,
          child: Material(
            color: Colors.transparent,
            child: InkWell(
              borderRadius: BorderRadius.circular(AppBorderRadius.round),
              onTap: enabled
                  ? () {
                      HapticFeedback.selectionClick();
                      _showOverflowSheet();
                    }
                  : null,
              child: Center(
                child: Icon(icon, size: iconSize, color: iconColor),
              ),
            ),
          ),
        ),
      ),
    );
  }

  Widget _buildPrimaryButton(
    bool hasText,
    bool isGenerating,
    void Function() stopGeneration,
    bool voiceAvailable,
  ) {
    // Compact 44px touch target, circular radius, md icon size
    const double buttonSize = TouchTarget.minimum; // 44.0
    const double radius = AppBorderRadius.round; // big to ensure circle

    final enabled = !isGenerating && hasText && widget.enabled;

    // Generating -> STOP variant
    if (isGenerating) {
      return Tooltip(
        message: AppLocalizations.of(context)!.stopGenerating,
        child: Material(
          color: Colors.transparent,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(radius),
            side: BorderSide(
              color: context.conduitTheme.error,
              width: BorderWidth.regular,
            ),
          ),
          child: InkWell(
            borderRadius: BorderRadius.circular(radius),
            onTap: () {
              HapticFeedback.lightImpact();
              stopGeneration();
            },
            child: Container(
              width: buttonSize,
              height: buttonSize,
              decoration: BoxDecoration(
                color: context.conduitTheme.error.withValues(
                  alpha: Alpha.buttonPressed,
                ),
                borderRadius: BorderRadius.circular(radius),
                boxShadow: ConduitShadows.button,
              ),
              child: Stack(
                alignment: Alignment.center,
                children: [
                  SizedBox(
                    width: buttonSize - 18,
                    height: buttonSize - 18,
                    child: CircularProgressIndicator(
                      strokeWidth: BorderWidth.medium,
                      valueColor: AlwaysStoppedAnimation<Color>(
                        context.conduitTheme.error,
                      ),
                    ),
                  ),
                  Icon(
                    Platform.isIOS ? CupertinoIcons.stop_fill : Icons.stop,
                    size: IconSize.medium,
                    color: context.conduitTheme.error,
                  ),
                ],
              ),
            ),
          ),
        ),
      );
    }

    // If there's text, render SEND variant; otherwise render MIC variant
    if (hasText) {
      return Tooltip(
        message: enabled
            ? AppLocalizations.of(context)!.sendMessage
            : AppLocalizations.of(context)!.send,
        child: Opacity(
          opacity: enabled ? Alpha.primary : Alpha.disabled,
          child: IgnorePointer(
            ignoring: !enabled,
            child: Material(
              color: Colors.transparent,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(radius),
                side: BorderSide(
                  color: enabled
                      ? context.conduitTheme.buttonPrimary
                      : context.conduitTheme.cardBorder.withValues(
                          alpha: Alpha.medium,
                        ),
                  width: BorderWidth.regular,
                ),
              ),
              child: InkWell(
                borderRadius: BorderRadius.circular(radius),
                onTap: enabled
                    ? () {
                        PlatformUtils.lightHaptic();
                        _sendMessage();
                      }
                    : null,
                child: Container(
                  width: buttonSize,
                  height: buttonSize,
                  decoration: BoxDecoration(
                    color: enabled
                        ? context.conduitTheme.buttonPrimary
                        : context.conduitTheme.cardBackground,
                    borderRadius: BorderRadius.circular(radius),
                    boxShadow: ConduitShadows.button,
                  ),
                  child: Icon(
                    Platform.isIOS
                        ? CupertinoIcons.arrow_up
                        : Icons.arrow_upward,
                    size: IconSize.medium,
                    color: enabled
                        ? context.conduitTheme.buttonPrimaryText
                        : context.conduitTheme.textPrimary.withValues(
                            alpha: Alpha.disabled,
                          ),
                  ),
                ),
              ),
            ),
          ),
        ),
      );
    }

    // MIC variant when no text is present
    final bool enabledMic = widget.enabled && voiceAvailable;
    return Tooltip(
      message: AppLocalizations.of(context)!.voiceInput,
      child: Opacity(
        opacity: enabledMic ? Alpha.primary : Alpha.disabled,
        child: IgnorePointer(
          ignoring: !enabledMic,
          child: Material(
            color: Colors.transparent,
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(radius),
              side: BorderSide(
                color: _isRecording
                    ? context.conduitTheme.buttonPrimary
                    : context.conduitTheme.cardBorder.withValues(
                        alpha: enabledMic ? Alpha.strong : Alpha.medium,
                      ),
                width: BorderWidth.regular,
              ),
            ),
            child: InkWell(
              borderRadius: BorderRadius.circular(radius),
              onTap: enabledMic
                  ? () {
                      HapticFeedback.selectionClick();
                      _toggleVoice();
                    }
                  : null,
              child: Container(
                width: buttonSize,
                height: buttonSize,
                decoration: BoxDecoration(
                  color: _isRecording
                      ? context.conduitTheme.buttonPrimary.withValues(
                          alpha: Alpha.buttonPressed,
                        )
                      : context.conduitTheme.cardBackground,
                  borderRadius: BorderRadius.circular(radius),
                  boxShadow: ConduitShadows.button,
                ),
                child: Icon(
                  Platform.isIOS ? CupertinoIcons.mic_fill : Icons.mic,
                  size: IconSize.medium,
                  color: _isRecording
                      ? context.conduitTheme.buttonPrimary
                      : (enabledMic
                            ? context.conduitTheme.textPrimary
                            : context.conduitTheme.textPrimary.withValues(
                                alpha: Alpha.disabled,
                              )),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }

  Widget _buildPillButton({
    required IconData icon,
    required String label,
    required bool isActive,
    VoidCallback? onTap,
  }) {
    final bool enabled = onTap != null;
    final Brightness brightness = Theme.of(context).brightness;
    final Color baseBackground = context.conduitTheme.cardBackground;
    final Color background = isActive
        ? context.conduitTheme.buttonPrimary.withValues(alpha: 0.16)
        : baseBackground.withValues(
            alpha: brightness == Brightness.dark ? 0.18 : 0.12,
          );
    final Color outline = isActive
        ? context.conduitTheme.buttonPrimary.withValues(alpha: 0.8)
        : context.conduitTheme.cardBorder.withValues(alpha: 0.6);
    final Color contentColor = isActive
        ? context.conduitTheme.buttonPrimary
        : context.conduitTheme.textPrimary.withValues(
            alpha: enabled ? Alpha.strong : Alpha.disabled,
          );

    return Material(
      color: Colors.transparent,
      child: InkWell(
        borderRadius: BorderRadius.circular(AppBorderRadius.input),
        onTap: onTap == null
            ? null
            : () {
                HapticFeedback.selectionClick();
                onTap();
              },
        child: Container(
          padding: const EdgeInsets.symmetric(
            horizontal: Spacing.sm,
            vertical: Spacing.xs,
          ),
          decoration: BoxDecoration(
            color: background,
            borderRadius: BorderRadius.circular(AppBorderRadius.input),
            border: Border.all(color: outline, width: BorderWidth.thin),
          ),
          child: Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              Icon(icon, size: IconSize.medium, color: contentColor),
              const SizedBox(width: Spacing.xs),
              Text(
                label,
                maxLines: 1,
                overflow: TextOverflow.ellipsis,
                style: AppTypography.labelStyle.copyWith(color: contentColor),
              ),
            ],
          ),
        ),
      ),
    );
  }

  void _showOverflowSheet() {
    HapticFeedback.selectionClick();
    final prevCanRequest = _focusNode.canRequestFocus;
    final wasFocused = _focusNode.hasFocus;
    _focusNode.canRequestFocus = false;
    try {
      FocusScope.of(context).unfocus();
      SystemChannels.textInput.invokeMethod('TextInput.hide');
    } catch (_) {}

    showModalBottomSheet(
      context: context,
      backgroundColor: Colors.transparent,
      isScrollControlled: true,
      builder: (modalContext) => Consumer(
        builder: (innerContext, modalRef, _) {
          final l10n = AppLocalizations.of(innerContext)!;
          final theme = innerContext.conduitTheme;

          final attachments = <Widget>[
            _buildOverflowAction(
              icon: Platform.isIOS ? CupertinoIcons.doc : Icons.attach_file,
              label: l10n.file,
              onTap: widget.onFileAttachment == null
                  ? null
                  : () {
                      HapticFeedback.lightImpact();
                      widget.onFileAttachment!.call();
                    },
            ),
            _buildOverflowAction(
              icon: Platform.isIOS ? CupertinoIcons.photo : Icons.image,
              label: l10n.photo,
              onTap: widget.onImageAttachment == null
                  ? null
                  : () {
                      HapticFeedback.lightImpact();
                      widget.onImageAttachment!.call();
                    },
            ),
            _buildOverflowAction(
              icon: Platform.isIOS ? CupertinoIcons.camera : Icons.camera_alt,
              label: l10n.camera,
              onTap: widget.onCameraCapture == null
                  ? null
                  : () {
                      HapticFeedback.lightImpact();
                      widget.onCameraCapture!.call();
                    },
            ),
          ];

          final featureTiles = <Widget>[];
          final webSearchAvailable = modalRef.watch(webSearchAvailableProvider);
          final webSearchEnabled = modalRef.watch(webSearchEnabledProvider);
          if (webSearchAvailable) {
            featureTiles.add(
              _buildFeatureToggleTile(
                icon: Platform.isIOS ? CupertinoIcons.search : Icons.search,
                title: l10n.webSearch,
                subtitle: l10n.webSearchDescription,
                value: webSearchEnabled,
                onChanged: (next) {
                  modalRef.read(webSearchEnabledProvider.notifier).set(next);
                },
              ),
            );
          }

          final imageGenAvailable = modalRef.watch(
            imageGenerationAvailableProvider,
          );
          final imageGenEnabled = modalRef.watch(
            imageGenerationEnabledProvider,
          );
          if (imageGenAvailable) {
            featureTiles.add(
              _buildFeatureToggleTile(
                icon: Platform.isIOS ? CupertinoIcons.photo : Icons.image,
                title: l10n.imageGeneration,
                subtitle: l10n.imageGenerationDescription,
                value: imageGenEnabled,
                onChanged: (next) {
                  modalRef
                      .read(imageGenerationEnabledProvider.notifier)
                      .set(next);
                },
              ),
            );
          }

          final selectedToolIds = modalRef.watch(selectedToolIdsProvider);
          final toolsAsync = modalRef.watch(toolsListProvider);
          final Widget toolsSection = toolsAsync.when(
            data: (tools) {
              if (tools.isEmpty) {
                return _buildInfoCard('No tools available');
              }
              final tiles = tools.map((tool) {
                final isSelected = selectedToolIds.contains(tool.id);
                return _buildToolTile(
                  tool: tool,
                  selected: isSelected,
                  onToggle: () {
                    final current = List<String>.from(
                      modalRef.read(selectedToolIdsProvider),
                    );
                    if (isSelected) {
                      current.remove(tool.id);
                    } else {
                      current.add(tool.id);
                    }
                    modalRef
                        .read(selectedToolIdsProvider.notifier)
                        .set(current);
                  },
                );
              }).toList();
              return Column(children: _withVerticalSpacing(tiles, Spacing.xxs));
            },
            loading: () => Center(
              child: SizedBox(
                width: 24,
                height: 24,
                child: CircularProgressIndicator(strokeWidth: BorderWidth.thin),
              ),
            ),
            error: (error, stack) => _buildInfoCard('Failed to load tools'),
          );

          final bodyChildren = <Widget>[
            const SheetHandle(),
            const SizedBox(height: Spacing.sm),
            Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                Row(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    for (var i = 0; i < attachments.length; i++) ...[
                      if (i != 0) const SizedBox(width: Spacing.sm),
                      Expanded(child: attachments[i]),
                    ],
                  ],
                ),
              ],
            ),
          ];

          if (featureTiles.isNotEmpty) {
            bodyChildren
              ..add(const SizedBox(height: Spacing.sm))
              ..addAll(_withVerticalSpacing(featureTiles, Spacing.xxs));
          }

          bodyChildren
            ..add(const SizedBox(height: Spacing.sm))
            ..add(_buildSectionLabel(l10n.tools))
            ..add(toolsSection);

          // Measure content height and cap the sheet's max size to avoid extra blank space
          final GlobalKey sheetContentKey = GlobalKey();
          double? measuredContentHeight;

          return StatefulBuilder(
            builder: (context, setModalState) {
              // Schedule a post-frame measurement of the content height
              WidgetsBinding.instance.addPostFrameCallback((_) {
                final ctx = sheetContentKey.currentContext;
                if (ctx != null) {
                  final renderObject = ctx.findRenderObject();
                  if (renderObject is RenderBox) {
                    final double h = renderObject.size.height;
                    if (h > 0 && h != measuredContentHeight) {
                      measuredContentHeight = h;
                      setModalState(() {});
                    }
                  }
                }
              });

              final media = MediaQuery.of(modalContext);
              final double availableHeight =
                  media.size.height - media.padding.top;

              double computedMax = 0.9;
              if (measuredContentHeight != null && availableHeight > 0) {
                computedMax = (measuredContentHeight! / availableHeight).clamp(
                  0.1,
                  0.9,
                );
              }
              final double computedMin = math.min(0.2, computedMax);
              final double computedInitial = math.min(0.34, computedMax);

              return Stack(
                children: [
                  Positioned.fill(
                    child: GestureDetector(
                      behavior: HitTestBehavior.opaque,
                      onTap: () => Navigator.of(modalContext).maybePop(),
                      child: const SizedBox.shrink(),
                    ),
                  ),
                  DraggableScrollableSheet(
                    expand: false,
                    initialChildSize: computedInitial,
                    minChildSize: computedMin,
                    maxChildSize: computedMax,
                    snap: true,
                    snapSizes: [computedMax],
                    builder: (sheetContext, scrollController) {
                      return Container(
                        decoration: BoxDecoration(
                          color: theme.surfaceBackground,
                          borderRadius: const BorderRadius.vertical(
                            top: Radius.circular(AppBorderRadius.bottomSheet),
                          ),
                          border: Border.all(
                            color: theme.dividerColor,
                            width: BorderWidth.thin,
                          ),
                          boxShadow: ConduitShadows.modal,
                        ),
                        child: ModalSheetSafeArea(
                          padding: const EdgeInsets.fromLTRB(
                            Spacing.md,
                            Spacing.xs,
                            Spacing.md,
                            Spacing.md,
                          ),
                          child: SingleChildScrollView(
                            controller: scrollController,
                            padding: EdgeInsets.zero,
                            child: Column(
                              key: sheetContentKey,
                              crossAxisAlignment: CrossAxisAlignment.stretch,
                              children: bodyChildren,
                            ),
                          ),
                        ),
                      );
                    },
                  ),
                ],
              );
            },
          );
        },
      ),
    ).whenComplete(() {
      if (mounted) {
        _focusNode.canRequestFocus = prevCanRequest;
        if (wasFocused && widget.enabled) {
          WidgetsBinding.instance.addPostFrameCallback((_) {
            if (!mounted) return;
            _ensureFocusedIfEnabled();
          });
        }
      }
    });
  }

  List<Widget> _withVerticalSpacing(List<Widget> children, double gap) {
    if (children.length <= 1) {
      return List<Widget>.from(children);
    }
    final spaced = <Widget>[];
    for (var i = 0; i < children.length; i++) {
      spaced.add(children[i]);
      if (i != children.length - 1) {
        spaced.add(SizedBox(height: gap));
      }
    }
    return spaced;
  }

  Widget _buildSectionLabel(String text) {
    return Padding(
      padding: const EdgeInsets.only(bottom: Spacing.xxs),
      child: Text(
        text,
        style: AppTypography.labelStyle.copyWith(
          color: context.conduitTheme.textSecondary.withValues(
            alpha: Alpha.strong,
          ),
          fontWeight: FontWeight.w600,
        ),
      ),
    );
  }

  Widget _buildFeatureToggleTile({
    required IconData icon,
    required String title,
    String? subtitle,
    required bool value,
    required ValueChanged<bool> onChanged,
  }) {
    final theme = context.conduitTheme;
    final brightness = Theme.of(context).brightness;
    final description = subtitle?.trim() ?? '';

    final Color background = value
        ? theme.buttonPrimary.withValues(
            alpha: brightness == Brightness.dark ? 0.28 : 0.16,
          )
        : theme.surfaceContainer.withValues(
            alpha: brightness == Brightness.dark ? 0.32 : 0.12,
          );
    final Color borderColor = value
        ? theme.buttonPrimary.withValues(alpha: 0.7)
        : theme.cardBorder.withValues(alpha: 0.55);

    return Semantics(
      button: true,
      toggled: value,
      label: title,
      hint: description.isEmpty ? null : description,
      child: Material(
        color: Colors.transparent,
        child: InkWell(
          borderRadius: BorderRadius.circular(AppBorderRadius.input),
          onTap: () {
            HapticFeedback.selectionClick();
            onChanged(!value);
          },
          child: AnimatedContainer(
            duration: const Duration(milliseconds: 180),
            curve: Curves.easeOutCubic,
            margin: const EdgeInsets.symmetric(vertical: Spacing.xxs),
            padding: const EdgeInsets.all(Spacing.sm),
            decoration: BoxDecoration(
              color: background,
              borderRadius: BorderRadius.circular(AppBorderRadius.input),
              border: Border.all(color: borderColor, width: BorderWidth.thin),
              boxShadow: value ? ConduitShadows.low : const [],
            ),
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                _buildToolGlyph(icon: icon, selected: value, theme: theme),
                const SizedBox(width: Spacing.xs),
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        title,
                        style: AppTypography.bodySmallStyle.copyWith(
                          color: theme.textPrimary,
                          fontWeight: value ? FontWeight.w600 : FontWeight.w500,
                        ),
                        maxLines: 1,
                        overflow: TextOverflow.ellipsis,
                      ),
                      if (description.isNotEmpty) ...[
                        const SizedBox(height: Spacing.xs),
                        Text(
                          description,
                          maxLines: 2,
                          overflow: TextOverflow.ellipsis,
                          style: AppTypography.captionStyle.copyWith(
                            color: theme.textSecondary.withValues(
                              alpha: Alpha.strong,
                            ),
                          ),
                        ),
                      ],
                    ],
                  ),
                ),
                const SizedBox(width: Spacing.xs),
                _buildTogglePill(isOn: value, theme: theme),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Widget _buildToolTile({
    required Tool tool,
    required bool selected,
    required VoidCallback onToggle,
  }) {
    final theme = context.conduitTheme;
    final brightness = Theme.of(context).brightness;
    final description = _toolDescriptionFor(tool);
    final Color background = selected
        ? theme.buttonPrimary.withValues(
            alpha: brightness == Brightness.dark ? 0.28 : 0.16,
          )
        : theme.surfaceContainer.withValues(
            alpha: brightness == Brightness.dark ? 0.32 : 0.12,
          );
    final Color borderColor = selected
        ? theme.buttonPrimary.withValues(alpha: 0.7)
        : theme.cardBorder.withValues(alpha: 0.55);

    return Semantics(
      button: true,
      toggled: selected,
      label: tool.name,
      hint: description.isEmpty ? null : description,
      child: Material(
        color: Colors.transparent,
        child: InkWell(
          borderRadius: BorderRadius.circular(AppBorderRadius.input),
          onTap: () {
            HapticFeedback.selectionClick();
            onToggle();
          },
          child: AnimatedContainer(
            duration: const Duration(milliseconds: 180),
            curve: Curves.easeOutCubic,
            margin: const EdgeInsets.symmetric(vertical: Spacing.xxs),
            padding: const EdgeInsets.all(Spacing.sm),
            decoration: BoxDecoration(
              color: background,
              borderRadius: BorderRadius.circular(AppBorderRadius.input),
              border: Border.all(color: borderColor, width: BorderWidth.thin),
              boxShadow: selected ? ConduitShadows.low : const [],
            ),
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                _buildToolGlyph(
                  icon: _toolIconFor(tool),
                  selected: selected,
                  theme: theme,
                ),
                const SizedBox(width: Spacing.xs),
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        tool.name,
                        style: AppTypography.bodySmallStyle.copyWith(
                          color: theme.textPrimary,
                          fontWeight: selected
                              ? FontWeight.w600
                              : FontWeight.w500,
                        ),
                        maxLines: 1,
                        overflow: TextOverflow.ellipsis,
                      ),
                      if (description.isNotEmpty) ...[
                        const SizedBox(height: Spacing.xs),
                        Text(
                          description,
                          maxLines: 2,
                          overflow: TextOverflow.ellipsis,
                          style: AppTypography.captionStyle.copyWith(
                            color: theme.textSecondary.withValues(
                              alpha: Alpha.strong,
                            ),
                          ),
                        ),
                      ],
                    ],
                  ),
                ),
                const SizedBox(width: Spacing.xs),
                _buildTogglePill(isOn: selected, theme: theme),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Widget _buildToolGlyph({
    required IconData icon,
    required bool selected,
    required ConduitThemeExtension theme,
  }) {
    final Color accentStart = theme.buttonPrimary.withValues(
      alpha: selected ? Alpha.active : Alpha.hover,
    );
    final Color accentEnd = theme.buttonPrimary.withValues(
      alpha: selected ? Alpha.highlight : Alpha.focus,
    );
    final Color iconColor = selected
        ? theme.buttonPrimaryText
        : theme.iconPrimary.withValues(alpha: Alpha.strong);

    return Container(
      width: 36,
      height: 36,
      decoration: BoxDecoration(
        shape: BoxShape.circle,
        gradient: LinearGradient(
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
          colors: [accentStart, accentEnd],
        ),
      ),
      child: Icon(icon, color: iconColor, size: IconSize.modal),
    );
  }

  String _toolDescriptionFor(Tool tool) {
    final metaDescription = _extractMetaDescription(tool.meta);
    if (metaDescription != null && metaDescription.isNotEmpty) {
      return metaDescription;
    }

    final custom = tool.description?.trim();
    if (custom != null && custom.isNotEmpty) {
      return custom;
    }

    final name = tool.name.toLowerCase();
    if (name.contains('search') || name.contains('browse')) {
      return 'Search the web for fresh context to improve answers.';
    }
    if (name.contains('image') ||
        name.contains('vision') ||
        name.contains('media')) {
      return 'Understand or generate imagery alongside your conversation.';
    }
    if (name.contains('code') ||
        name.contains('python') ||
        name.contains('notebook')) {
      return 'Execute code snippets and return computed results inline.';
    }
    if (name.contains('calc') || name.contains('math')) {
      return 'Perform precise math and calculations on demand.';
    }
    if (name.contains('file') || name.contains('document')) {
      return 'Access and summarize your uploaded files during chat.';
    }
    if (name.contains('api') || name.contains('request')) {
      return 'Trigger API requests and bring external data into the chat.';
    }
    return 'Enhance responses with specialized capabilities from this tool.';
  }

  String? _extractMetaDescription(Map<String, dynamic>? meta) {
    if (meta == null || meta.isEmpty) return null;
    final value = meta['description'];
    if (value is String) {
      final trimmed = value.trim();
      if (trimmed.isNotEmpty) return trimmed;
    }
    return null;
  }

  Widget _buildTogglePill({
    required bool isOn,
    required ConduitThemeExtension theme,
  }) {
    final Color trackColor = isOn
        ? theme.buttonPrimary.withValues(alpha: 0.9)
        : theme.cardBorder.withValues(alpha: 0.5);
    final Color thumbColor = isOn
        ? theme.buttonPrimaryText
        : theme.surfaceBackground.withValues(alpha: 0.9);

    return AnimatedContainer(
      duration: const Duration(milliseconds: 180),
      curve: Curves.easeOutCubic,
      width: 42,
      height: 22,
      padding: const EdgeInsets.symmetric(horizontal: 2),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(AppBorderRadius.round),
        color: trackColor,
      ),
      alignment: isOn ? Alignment.centerRight : Alignment.centerLeft,
      child: AnimatedContainer(
        duration: const Duration(milliseconds: 180),
        curve: Curves.easeOutCubic,
        width: 18,
        height: 18,
        decoration: BoxDecoration(
          shape: BoxShape.circle,
          color: thumbColor,
          boxShadow: [
            BoxShadow(
              color: theme.buttonPrimary.withValues(alpha: 0.25),
              blurRadius: 6,
              offset: const Offset(0, 2),
            ),
          ],
        ),
      ),
    );
  }

  IconData _toolIconFor(Tool tool) {
    final name = tool.name.toLowerCase();
    if (name.contains('image') || name.contains('vision')) {
      return Platform.isIOS ? CupertinoIcons.photo : Icons.image;
    }
    if (name.contains('code') || name.contains('python')) {
      return Platform.isIOS
          ? CupertinoIcons.chevron_left_slash_chevron_right
          : Icons.code;
    }
    if (name.contains('calculator') || name.contains('math')) {
      return Icons.calculate;
    }
    if (name.contains('file') || name.contains('document')) {
      return Platform.isIOS ? CupertinoIcons.doc : Icons.description;
    }
    if (name.contains('api') || name.contains('request')) {
      return Icons.cloud;
    }
    if (name.contains('search')) {
      return Platform.isIOS ? CupertinoIcons.search : Icons.search;
    }
    return Platform.isIOS ? CupertinoIcons.square_grid_2x2 : Icons.extension;
  }

  Widget _buildInfoCard(String message) {
    final theme = context.conduitTheme;
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.all(Spacing.md),
      decoration: BoxDecoration(
        color: theme.cardBackground,
        borderRadius: BorderRadius.circular(AppBorderRadius.input),
        border: Border.all(
          color: theme.cardBorder.withValues(alpha: 0.6),
          width: BorderWidth.thin,
        ),
      ),
      child: Text(
        message,
        style: AppTypography.bodyMediumStyle.copyWith(
          color: theme.textSecondary,
        ),
      ),
    );
  }

  Widget _buildOverflowAction({
    required IconData icon,
    required String label,
    VoidCallback? onTap,
  }) {
    final theme = context.conduitTheme;
    final brightness = Theme.of(context).brightness;
    final VoidCallback? callback = onTap;
    final bool enabled = callback != null;
    final Color iconColor = enabled ? theme.buttonPrimary : theme.iconDisabled;
    final Color textColor = enabled
        ? theme.textPrimary
        : theme.textPrimary.withValues(alpha: Alpha.disabled);
    final Color background = theme.surfaceContainer.withValues(
      alpha: brightness == Brightness.dark ? 0.45 : 0.92,
    );
    final Color borderColor = theme.cardBorder.withValues(
      alpha: enabled ? 0.5 : 0.25,
    );
    final Color accent = theme.buttonPrimary.withValues(
      alpha: enabled ? Alpha.selected : Alpha.hover,
    );

    return Opacity(
      opacity: enabled ? 1.0 : Alpha.disabled,
      child: Material(
        color: Colors.transparent,
        child: InkWell(
          borderRadius: BorderRadius.circular(AppBorderRadius.card),
          onTap: callback == null
              ? null
              : () {
                  Navigator.of(context).pop();
                  Future.microtask(callback);
                },
          child: AnimatedContainer(
            duration: const Duration(milliseconds: 180),
            curve: Curves.easeOutCubic,
            padding: const EdgeInsets.symmetric(
              horizontal: Spacing.xs,
              vertical: Spacing.sm,
            ),
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(AppBorderRadius.card),
              border: Border.all(color: borderColor, width: BorderWidth.thin),
              color: background,
            ),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                Container(
                  width: 36,
                  height: 36,
                  decoration: BoxDecoration(
                    shape: BoxShape.circle,
                    gradient: LinearGradient(
                      begin: Alignment.topLeft,
                      end: Alignment.bottomRight,
                      colors: [
                        accent,
                        theme.buttonPrimary.withValues(
                          alpha: enabled ? Alpha.highlight : Alpha.hover,
                        ),
                      ],
                    ),
                  ),
                  child: Icon(icon, color: iconColor, size: IconSize.modal),
                ),
                const SizedBox(height: Spacing.xs),
                Text(
                  label,
                  textAlign: TextAlign.center,
                  style: AppTypography.captionStyle.copyWith(
                    fontWeight: FontWeight.w600,
                    color: textColor,
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  // --- Inline Voice Input ---
  Future<void> _toggleVoice() async {
    if (_isRecording) {
      await _stopVoice();
    } else {
      await _startVoice();
    }
  }

  Future<void> _startVoice() async {
    if (!widget.enabled) return;
    try {
      final ok = await _voiceService.initialize();
      if (!mounted) return;
      if (!ok) {
        _showVoiceUnavailable(
          AppLocalizations.of(context)?.errorMessage ??
              'Voice input unavailable',
        );
        return;
      }
      // Centralized permission + start
      final stream = await _voiceService.beginListening();
      if (!mounted) return;
      setState(() {
        _isRecording = true;
        _baseTextAtStart = _controller.text;
      });
      _intensitySub?.cancel();
      // intensity stream no longer used for UI; stop listening
      _textSub?.cancel();
      _textSub = stream.listen(
        (text) async {
          final updated = _baseTextAtStart.isEmpty
              ? text
              : '${_baseTextAtStart.trimRight()} $text';
          _controller.value = TextEditingValue(
            text: updated,
            selection: TextSelection.collapsed(offset: updated.length),
          );
        },
        onDone: () {
          if (!mounted) return;
          setState(() => _isRecording = false);
          _intensitySub?.cancel();
          _intensitySub = null;
        },
        onError: (_) {
          if (!mounted) return;
          setState(() => _isRecording = false);
          _intensitySub?.cancel();
          _intensitySub = null;
        },
      );
      _ensureFocusedIfEnabled();
    } catch (_) {
      _showVoiceUnavailable(
        AppLocalizations.of(context)?.errorMessage ??
            'Failed to start voice input',
      );
      if (!mounted) return;
      setState(() => _isRecording = false);
    }
  }

  Future<void> _stopVoice() async {
    _intensitySub?.cancel();
    _intensitySub = null;
    await _voiceService.stopListening();
    if (!mounted) return;
    setState(() => _isRecording = false);
    HapticFeedback.selectionClick();
  }

  // Server transcription removed; only on-device STT updates the input text

  void _showVoiceUnavailable(String message) {
    if (!mounted) return;
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text(message),
        behavior: SnackBarBehavior.floating,
        duration: const Duration(seconds: 2),
      ),
    );
  }
}
