import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import '../l10n/l10n.dart';
import '../models/contact.dart';
import '../connector/meshcore_connector.dart';
import '../connector/meshcore_protocol.dart';
import '../theme/mesh_theme.dart';
import '../widgets/debug_frame_viewer.dart';
import '../services/repeater_command_service.dart';
import '../widgets/routing_sheet.dart';
import '../helpers/snack_bar_builder.dart';

class RepeaterCliScreen extends StatefulWidget {
  final Contact repeater;
  final String password;

  const RepeaterCliScreen({
    super.key,
    required this.repeater,
    required this.password,
  });

  @override
  State<RepeaterCliScreen> createState() => _RepeaterCliScreenState();
}

class _RepeaterCliScreenState extends State<RepeaterCliScreen> {
  final TextEditingController _commandController = TextEditingController();
  final FocusNode _commandFocusNode = FocusNode();
  final ScrollController _scrollController = ScrollController();
  final List<Map<String, String>> _commandHistory = [];
  int _historyIndex = -1;
  StreamSubscription<Uint8List>? _frameSubscription;
  RepeaterCommandService? _commandService;

  late final List<Map<String, String>> _quickCommands = [
    {'labelKey': 'advertise', 'command': 'advert'},
    {'labelKey': 'getName', 'command': 'get name'},
    {'labelKey': 'getRadio', 'command': 'get radio'},
    {'labelKey': 'getTx', 'command': 'get tx'},
    {'labelKey': 'discovery', 'command': 'discover.neighbors'},
    {'labelKey': 'neighbors', 'command': 'neighbors'},
    {'labelKey': 'version', 'command': 'ver'},
    {'labelKey': 'clock', 'command': 'clock'},
    {'labelKey': 'clock sync', 'command': 'clock sync'},
  ];

  @override
  void initState() {
    super.initState();
    final connector = Provider.of<MeshCoreConnector>(context, listen: false);
    _commandService = RepeaterCommandService(connector);
    _setupMessageListener();
  }

  @override
  void dispose() {
    _frameSubscription?.cancel();
    _commandService?.dispose();
    _commandController.dispose();
    _commandFocusNode.dispose();
    _scrollController.dispose();
    super.dispose();
  }

  void _setupMessageListener() {
    final connector = Provider.of<MeshCoreConnector>(context, listen: false);
    _frameSubscription = connector.receivedFrames.listen((frame) {
      if (frame.isEmpty) return;
      if (frame[0] == respCodeContactMsgRecv ||
          frame[0] == respCodeContactMsgRecvV3) {
        _handleTextMessageResponse(frame);
      }
    });
  }

  int _resolveRepeaterIndex = -1;

  Contact _resolveRepeater(MeshCoreConnector connector) {
    if (_resolveRepeaterIndex >= 0 &&
        _resolveRepeaterIndex < connector.contacts.length &&
        connector.contacts[_resolveRepeaterIndex].publicKeyHex ==
            widget.repeater.publicKeyHex) {
      return connector.contacts[_resolveRepeaterIndex];
    }
    _resolveRepeaterIndex = connector.contacts.indexWhere(
      (c) => c.publicKeyHex == widget.repeater.publicKeyHex,
    );
    if (_resolveRepeaterIndex == -1) {
      return widget.repeater;
    }
    return connector.contacts[_resolveRepeaterIndex];
  }

  void _handleTextMessageResponse(Uint8List frame) {
    final parsed = parseContactMessageText(frame);
    if (parsed == null) return;
    if (!_matchesRepeaterPrefix(parsed.senderPrefix)) return;
    _commandService?.handleResponse(widget.repeater, parsed.text);
  }

  bool _matchesRepeaterPrefix(Uint8List prefix) {
    final target = widget.repeater.publicKey;
    if (target.length < 6 || prefix.length < 6) return false;
    for (int i = 0; i < 6; i++) {
      if (prefix[i] != target[i]) return false;
    }
    return true;
  }

  void _sendCommand({bool showDebug = false}) async {
    final command = _commandController.text.trim();
    if (command.isEmpty) return;

    setState(() {
      _commandHistory.add({
        'type': 'command',
        'text': command,
        'timestamp': DateTime.now().toString(),
      });
    });

    if (showDebug && mounted) {
      final frame = buildSendCliCommandFrame(
        widget.repeater.publicKey,
        command,
      );
      DebugFrameViewer.showFrameDebug(
        context,
        frame,
        context.l10n.repeater_cliCommandFrameTitle,
      );
    }

    try {
      if (_commandService != null) {
        final connector = Provider.of<MeshCoreConnector>(
          context,
          listen: false,
        );
        final repeater = _resolveRepeater(connector);
        final response = await _commandService!.sendCommand(
          repeater,
          command,
          retries: 1,
        );
        if (mounted) {
          setState(() {
            _commandHistory.add({
              'type': 'response',
              'text': response,
              'timestamp': DateTime.now().toString(),
            });
          });
        }
      }
    } catch (e) {
      if (mounted) {
        setState(() {
          _commandHistory.add({
            'type': 'response',
            'text': context.l10n.repeater_cliCommandError(e.toString()),
            'timestamp': DateTime.now().toString(),
          });
        });
      }
    }

    _commandController.clear();
    _historyIndex = -1;
    _commandFocusNode.requestFocus();

    Future.delayed(const Duration(milliseconds: 100), () {
      if (_scrollController.hasClients) {
        _scrollController.animateTo(
          _scrollController.position.maxScrollExtent,
          duration: const Duration(milliseconds: 200),
          curve: Curves.easeOut,
        );
      }
    });
  }

  void _useQuickCommand(String command) {
    _commandController.text = command;
    _sendCommand();
  }

  void _navigateHistory(bool up) {
    final commands = _commandHistory
        .where((entry) => entry['type'] == 'command')
        .toList()
        .reversed
        .toList();

    if (commands.isEmpty) return;

    if (up) {
      if (_historyIndex < commands.length - 1) {
        _historyIndex++;
      }
    } else {
      if (_historyIndex > 0) {
        _historyIndex--;
      } else {
        _historyIndex = -1;
        _commandController.clear();
        return;
      }
    }

    if (_historyIndex >= 0 && _historyIndex < commands.length) {
      _commandController.text = commands[_historyIndex]['text'] ?? '';
      _commandController.selection = TextSelection.fromPosition(
        TextPosition(offset: _commandController.text.length),
      );
    }
  }

  void _clearHistory() {
    setState(() {
      _commandHistory.clear();
      _historyIndex = -1;
    });
  }

  String _quickCommandLabel(String key) {
    final l10n = context.l10n;
    switch (key) {
      case 'getName':
        return l10n.repeater_cliQuickGetName;
      case 'getRadio':
        return l10n.repeater_cliQuickGetRadio;
      case 'getTx':
        return l10n.repeater_cliQuickGetTx;
      case 'neighbors':
        return l10n.repeater_cliQuickNeighbors;
      case 'version':
        return l10n.repeater_cliQuickVersion;
      case 'advertise':
        return l10n.repeater_cliQuickAdvertise;
      case 'clock':
        return l10n.repeater_cliQuickClock;
      case 'clock sync':
        return l10n.repeater_cliQuickClockSync;
      case 'discovery':
        return l10n.repeater_cliQuickDiscovery;
      default:
        return key;
    }
  }

  @override
  Widget build(BuildContext context) {
    final l10n = context.l10n;
    final scheme = Theme.of(context).colorScheme;
    final connector = context.watch<MeshCoreConnector>();
    final repeater = _resolveRepeater(connector);
    final isFloodMode = repeater.pathOverride == -1;

    return Scaffold(
      backgroundColor: MeshPalette.bg,
      appBar: AppBar(
        backgroundColor: MeshPalette.bg1,
        title: Text(l10n.repeater_cliTitle),
        centerTitle: true,
        actions: [
          IconButton(
            icon: Icon(isFloodMode ? Icons.waves : Icons.route),
            tooltip: l10n.repeater_routingMode,
            onPressed: () =>
                ContactRoutingSheet.show(context, contact: repeater),
          ),
          IconButton(
            icon: const Icon(Icons.help_outline),
            tooltip: l10n.repeater_commandHelp,
            onPressed: () => _showCommandHelp(context),
          ),
          IconButton(
            icon: const Icon(Icons.clear_all),
            tooltip: l10n.repeater_clearHistory,
            onPressed: _commandHistory.isEmpty ? null : _clearHistory,
          ),
          PopupMenuButton<String>(
            icon: const Icon(Icons.more_vert),
            onSelected: (value) {
              if (value == 'debug') {
                if (_commandController.text.trim().isNotEmpty) {
                  _sendCommand(showDebug: true);
                } else {
                  showDismissibleSnackBar(
                    context,
                    content: Text(l10n.repeater_enterCommandFirst),
                  );
                }
              }
            },
            itemBuilder: (context) => [
              PopupMenuItem(
                value: 'debug',
                child: Row(
                  children: [
                    const Icon(Icons.bug_report),
                    const SizedBox(width: 8),
                    Text(l10n.repeater_debugNextCommand),
                  ],
                ),
              ),
            ],
          ),
        ],
      ),
      body: Column(
        children: [
          // Quick commands bar
          Container(
            color: MeshPalette.bg1,
            padding: const EdgeInsets.fromLTRB(8, 6, 8, 6),
            child: SingleChildScrollView(
              scrollDirection: Axis.horizontal,
              child: Row(
                children: _quickCommands.map((cmd) {
                  final label = _quickCommandLabel(cmd['labelKey']!);
                  return Padding(
                    padding: const EdgeInsets.only(right: 6),
                    child: ActionChip(
                      label: Text(
                        label,
                        style: MeshTheme.mono(
                          fontSize: 11,
                          fontWeight: FontWeight.w600,
                          color: MeshPalette.blue,
                        ),
                      ),
                      backgroundColor: MeshPalette.blueBg,
                      side: const BorderSide(color: MeshPalette.blueLine),
                      visualDensity: VisualDensity.compact,
                      onPressed: () => _useQuickCommand(cmd['command']!),
                    ),
                  );
                }).toList(),
              ),
            ),
          ),
          Divider(height: 1, color: MeshPalette.line),

          // Output area
          Expanded(
            child: _commandHistory.isEmpty
                ? _buildEmptyState()
                : _buildCommandHistory(),
          ),

          Divider(height: 1, color: MeshPalette.line),

          // Command input
          Container(
            color: MeshPalette.bg1,
            padding: const EdgeInsets.fromLTRB(8, 8, 8, 8),
            child: SafeArea(
              child: Row(
                children: [
                  IconButton(
                    icon: Icon(
                      Icons.arrow_upward,
                      size: 18,
                      color: scheme.onSurfaceVariant,
                    ),
                    tooltip: l10n.repeater_previousCommand,
                    onPressed: () => _navigateHistory(true),
                    visualDensity: VisualDensity.compact,
                  ),
                  IconButton(
                    icon: Icon(
                      Icons.arrow_downward,
                      size: 18,
                      color: scheme.onSurfaceVariant,
                    ),
                    tooltip: l10n.repeater_nextCommand,
                    onPressed: () => _navigateHistory(false),
                    visualDensity: VisualDensity.compact,
                  ),
                  const SizedBox(width: 4),
                  Expanded(
                    child: TextField(
                      controller: _commandController,
                      focusNode: _commandFocusNode,
                      style: MeshTheme.mono(
                        fontSize: 13,
                        color: MeshPalette.ink,
                      ),
                      decoration: InputDecoration(
                        hintText: context.l10n.repeater_enterCommandHint,
                        hintStyle: MeshTheme.mono(
                          fontSize: 13,
                          color: MeshPalette.ink4,
                        ),
                        prefixText: '> ',
                        prefixStyle: MeshTheme.mono(
                          fontSize: 13,
                          color: MeshPalette.blue,
                          fontWeight: FontWeight.w700,
                        ),
                        filled: true,
                        fillColor: MeshPalette.bg2,
                        contentPadding: const EdgeInsets.symmetric(
                          horizontal: 12,
                          vertical: 10,
                        ),
                        border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(MeshRadii.pill),
                          borderSide: const BorderSide(
                            color: MeshPalette.line2,
                          ),
                        ),
                        enabledBorder: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(MeshRadii.pill),
                          borderSide: const BorderSide(
                            color: MeshPalette.line2,
                          ),
                        ),
                        focusedBorder: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(MeshRadii.pill),
                          borderSide: const BorderSide(
                            color: MeshPalette.blue,
                            width: 1.5,
                          ),
                        ),
                      ),
                      textInputAction: TextInputAction.send,
                      onSubmitted: (_) => _sendCommand(),
                    ),
                  ),
                  const SizedBox(width: 6),
                  Material(
                    color: MeshPalette.blue.withValues(alpha: 0.15),
                    shape: const CircleBorder(
                      side: BorderSide(color: MeshPalette.blueLine),
                    ),
                    child: InkWell(
                      customBorder: const CircleBorder(),
                      onTap: () {
                        HapticFeedback.lightImpact();
                        _sendCommand();
                      },
                      child: const Padding(
                        padding: EdgeInsets.all(10),
                        child: Icon(
                          Icons.send,
                          size: 18,
                          color: MeshPalette.blue,
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildEmptyState() {
    final l10n = context.l10n;
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          const Icon(Icons.terminal, size: 48, color: MeshPalette.ink4),
          const SizedBox(height: 12),
          Text(
            l10n.repeater_noCommandsSent,
            style: MeshTheme.mono(fontSize: 13, color: MeshPalette.ink3),
          ),
          const SizedBox(height: 4),
          Text(
            l10n.repeater_typeCommandOrUseQuick,
            style: const TextStyle(fontSize: 12, color: MeshPalette.ink4),
          ),
        ],
      ),
    );
  }

  Widget _buildCommandHistory() {
    return ListView.builder(
      controller: _scrollController,
      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
      itemCount: _commandHistory.length,
      itemBuilder: (context, index) {
        final entry = _commandHistory[index];
        final isCommand = entry['type'] == 'command';

        return Padding(
          padding: const EdgeInsets.only(bottom: 2),
          child: Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              // Gutter prefix
              SizedBox(
                width: 20,
                child: Text(
                  isCommand ? '>' : ' ',
                  style: MeshTheme.mono(
                    fontSize: 12,
                    fontWeight: FontWeight.w700,
                    color: isCommand ? MeshPalette.blue : MeshPalette.ink3,
                  ),
                ),
              ),
              const SizedBox(width: 6),
              Expanded(
                child: SelectableText(
                  entry['text']!,
                  style: MeshTheme.mono(
                    fontSize: 12.5,
                    color: isCommand ? MeshPalette.blue : MeshPalette.ink,
                  ),
                ),
              ),
            ],
          ),
        );
      },
    );
  }

  void _applyHelpCommand(String command) {
    _commandController.text = command;
    _commandController.selection = TextSelection.fromPosition(
      TextPosition(offset: command.length),
    );
    Navigator.pop(context);
    Future.microtask(() {
      if (mounted) {
        _commandFocusNode.requestFocus();
      }
    });
  }

  void _showCommandHelp(BuildContext context) {
    final l10n = context.l10n;
    final generalCommands = [
      _CommandHelpEntry(
        command: 'advert',
        description: l10n.repeater_cliHelpAdvert,
      ),
      _CommandHelpEntry(
        command: 'reboot',
        description: l10n.repeater_cliHelpReboot,
      ),
      _CommandHelpEntry(
        command: 'clock',
        description: l10n.repeater_cliHelpClock,
      ),
      _CommandHelpEntry(
        command: 'password {new-password}',
        description: l10n.repeater_cliHelpPassword,
      ),
      _CommandHelpEntry(
        command: 'ver',
        description: l10n.repeater_cliHelpVersion,
      ),
      _CommandHelpEntry(
        command: 'clear stats',
        description: l10n.repeater_cliHelpClearStats,
      ),
      _CommandHelpEntry(
        command: 'poweroff',
        description: l10n.repeater_cliHelpPowerOff,
      ),
      _CommandHelpEntry(
        command: 'shutdown',
        description: l10n.repeater_cliHelpPowerOff,
      ),
      _CommandHelpEntry(
        command: 'clkreboot',
        description: l10n.repeater_cliHelpClkReboot,
      ),
      _CommandHelpEntry(
        command: 'advert.zerohop',
        description: l10n.repeater_cliHelpAdvertZeroHop,
      ),
      _CommandHelpEntry(
        command: 'start ota',
        description: l10n.repeater_cliHelpStartOta,
      ),
      _CommandHelpEntry(
        command: 'time {epoch-seconds}',
        description: l10n.repeater_cliHelpTime,
      ),
      _CommandHelpEntry(
        command: 'board',
        description: l10n.repeater_cliHelpBoard,
      ),
      _CommandHelpEntry(
        command: 'discover.neighbors',
        description: l10n.repeater_cliHelpDiscoverNeighbors,
      ),
      _CommandHelpEntry(
        command: 'powersaving',
        description: l10n.repeater_cliHelpPowersaving,
      ),
      _CommandHelpEntry(
        command: 'powersaving {on|off}',
        description: l10n.repeater_cliHelpPowersavingOnOff,
      ),
      _CommandHelpEntry(
        command: 'erase',
        description: l10n.repeater_cliHelpErase,
      ),
      _CommandHelpEntry(
        command: 'stats-packets',
        description: l10n.repeater_cliHelpStatsPackets,
      ),
      _CommandHelpEntry(
        command: 'stats-radio',
        description: l10n.repeater_cliHelpStatsRadio,
      ),
      _CommandHelpEntry(
        command: 'stats-core',
        description: l10n.repeater_cliHelpStatsCore,
      ),
    ];

    final settingsCommands = [
      _CommandHelpEntry(
        command: 'set af {air-time-factor}',
        description: l10n.repeater_cliHelpSetAf,
      ),
      _CommandHelpEntry(
        command: 'set tx {tx-power-dbm}',
        description: l10n.repeater_cliHelpSetTx,
      ),
      _CommandHelpEntry(
        command: 'set repeat {on|off}',
        description: l10n.repeater_cliHelpSetRepeat,
      ),
      _CommandHelpEntry(
        command: 'set allow.read.only {on|off}',
        description: l10n.repeater_cliHelpSetAllowReadOnly,
      ),
      _CommandHelpEntry(
        command: 'set flood.max {max-hops}',
        description: l10n.repeater_cliHelpSetFloodMax,
      ),
      _CommandHelpEntry(
        command: 'set int.thresh {db}',
        description: l10n.repeater_cliHelpSetIntThresh,
      ),
      _CommandHelpEntry(
        command: 'set agc.reset.interval {seconds}',
        description: l10n.repeater_cliHelpSetAgcResetInterval,
      ),
      _CommandHelpEntry(
        command: 'set multi.acks {0|1}',
        description: l10n.repeater_cliHelpSetMultiAcks,
      ),
      _CommandHelpEntry(
        command: 'set advert.interval {minutes}',
        description: l10n.repeater_cliHelpSetAdvertInterval,
      ),
      _CommandHelpEntry(
        command: 'set flood.advert.interval {hours}',
        description: l10n.repeater_cliHelpSetFloodAdvertInterval,
      ),
      _CommandHelpEntry(
        command: 'set guest.password {guess-password}',
        description: l10n.repeater_cliHelpSetGuestPassword,
      ),
      _CommandHelpEntry(
        command: 'set name {name}',
        description: l10n.repeater_cliHelpSetName,
      ),
      _CommandHelpEntry(
        command: 'set lat {latitude}',
        description: l10n.repeater_cliHelpSetLat,
      ),
      _CommandHelpEntry(
        command: 'set lon {longitude}',
        description: l10n.repeater_cliHelpSetLon,
      ),
      _CommandHelpEntry(
        command: 'set radio {freq},{bw},{sf},{cr}',
        description: l10n.repeater_cliHelpSetRadio,
      ),
      _CommandHelpEntry(
        command: 'set rxdelay {base}',
        description: l10n.repeater_cliHelpSetRxDelay,
      ),
      _CommandHelpEntry(
        command: 'set txdelay {factor}',
        description: l10n.repeater_cliHelpSetTxDelay,
      ),
      _CommandHelpEntry(
        command: 'set direct.txdelay {factor}',
        description: l10n.repeater_cliHelpSetDirectTxDelay,
      ),
      _CommandHelpEntry(
        command: 'set bridge.enabled {on|off}',
        description: l10n.repeater_cliHelpSetBridgeEnabled,
      ),
      _CommandHelpEntry(
        command: 'set bridge.delay {0-10000}',
        description: l10n.repeater_cliHelpSetBridgeDelay,
      ),
      _CommandHelpEntry(
        command: 'set bridge.source {rx|tx}',
        description: l10n.repeater_cliHelpSetBridgeSource,
      ),
      _CommandHelpEntry(
        command: 'set bridge.baud {speed}',
        description: l10n.repeater_cliHelpSetBridgeBaud,
      ),
      _CommandHelpEntry(
        command: 'set bridge.secret {shared-secret}',
        description: l10n.repeater_cliHelpSetBridgeSecret,
      ),
      _CommandHelpEntry(
        command: 'set adc.multiplier {factor}',
        description: l10n.repeater_cliHelpSetAdcMultiplier,
      ),
      _CommandHelpEntry(
        command: 'tempradio {freq},{bw},{sf},{cr},{minutes}',
        description: l10n.repeater_cliHelpTempRadio,
      ),
      _CommandHelpEntry(
        command: 'setperm {pubkey-hex} {permissions}',
        description: l10n.repeater_cliHelpSetPerm,
      ),
      _CommandHelpEntry(
        command: 'set dutycycle {1-100}',
        description: l10n.repeater_cliHelpSetDutyCycle,
      ),
      _CommandHelpEntry(
        command: 'set prv.key {hex}',
        description: l10n.repeater_cliHelpSetPrvKey,
      ),
      _CommandHelpEntry(
        command: 'set radio.rxgain {on|off}',
        description: l10n.repeater_cliHelpSetRadioRxGain,
      ),
      _CommandHelpEntry(
        command: 'set owner.info {text}',
        description: l10n.repeater_cliHelpSetOwnerInfo,
      ),
      _CommandHelpEntry(
        command: 'set path.hash.mode {0|1|2}',
        description: l10n.repeater_cliHelpSetPathHashMode,
      ),
      _CommandHelpEntry(
        command: 'set loop.detect {off|minimal|moderate|strict}',
        description: l10n.repeater_cliHelpSetLoopDetect,
      ),
      _CommandHelpEntry(
        command: 'set freq {mhz}',
        description: l10n.repeater_cliHelpSetFreq,
      ),
      _CommandHelpEntry(
        command: 'set bridge.channel {1-14}',
        description: l10n.repeater_cliHelpSetBridgeChannel,
      ),
    ];

    final bridgeCommands = [
      _CommandHelpEntry(
        command: 'get bridge.type',
        description: l10n.repeater_cliHelpGetBridgeType,
      ),
    ];

    final loggingCommands = [
      _CommandHelpEntry(
        command: 'log start',
        description: l10n.repeater_cliHelpLogStart,
      ),
      _CommandHelpEntry(
        command: 'log stop',
        description: l10n.repeater_cliHelpLogStop,
      ),
      _CommandHelpEntry(
        command: 'log erase',
        description: l10n.repeater_cliHelpLogErase,
      ),
    ];

    final neighborCommands = [
      _CommandHelpEntry(
        command: 'neighbors',
        description: l10n.repeater_cliHelpNeighbors,
      ),
      _CommandHelpEntry(
        command: 'neighbor.remove {pubkey-prefix}',
        description: l10n.repeater_cliHelpNeighborRemove,
      ),
    ];

    final regionCommands = [
      _CommandHelpEntry(
        command: 'region',
        description: l10n.repeater_cliHelpRegion,
      ),
      _CommandHelpEntry(
        command: 'region load',
        description: l10n.repeater_cliHelpRegionLoad,
      ),
      _CommandHelpEntry(
        command: 'region get {* | name-prefix}',
        description: l10n.repeater_cliHelpRegionGet,
      ),
      _CommandHelpEntry(
        command: 'region put {name} {* | parent-name-prefix}',
        description: l10n.repeater_cliHelpRegionPut,
      ),
      _CommandHelpEntry(
        command: 'region remove {name}',
        description: l10n.repeater_cliHelpRegionRemove,
      ),
      _CommandHelpEntry(
        command: 'region allowf {* | name-prefix}',
        description: l10n.repeater_cliHelpRegionAllowf,
      ),
      _CommandHelpEntry(
        command: 'region denyf {* | name-prefix}',
        description: l10n.repeater_cliHelpRegionDenyf,
      ),
      _CommandHelpEntry(
        command: 'region home',
        description: l10n.repeater_cliHelpRegionHome,
      ),
      _CommandHelpEntry(
        command: 'region home {* | name-prefix}',
        description: l10n.repeater_cliHelpRegionHomeSet,
      ),
      _CommandHelpEntry(
        command: 'region save',
        description: l10n.repeater_cliHelpRegionSave,
      ),
      _CommandHelpEntry(
        command: 'region default',
        description: l10n.repeater_cliHelpRegionDefault,
      ),
      _CommandHelpEntry(
        command: 'region default {* | name-prefix | <null>}',
        description: l10n.repeater_cliHelpRegionDefaultSet,
      ),
      _CommandHelpEntry(
        command: 'region list allowed',
        description: l10n.repeater_cliHelpRegionListAllowed,
      ),
      _CommandHelpEntry(
        command: 'region list denied',
        description: l10n.repeater_cliHelpRegionListDenied,
      ),
    ];

    final getCommands = [
      _CommandHelpEntry(
        command: 'get name',
        description: l10n.repeater_cliHelpGetName,
      ),
      _CommandHelpEntry(
        command: 'get role',
        description: l10n.repeater_cliHelpGetRole,
      ),
      _CommandHelpEntry(
        command: 'get public.key',
        description: l10n.repeater_cliHelpGetPublicKey,
      ),
      _CommandHelpEntry(
        command: 'get prv.key',
        description: l10n.repeater_cliHelpGetPrvKey,
      ),
      _CommandHelpEntry(
        command: 'get repeat',
        description: l10n.repeater_cliHelpGetRepeat,
      ),
      _CommandHelpEntry(
        command: 'get tx',
        description: l10n.repeater_cliHelpGetTx,
      ),
      _CommandHelpEntry(
        command: 'get freq',
        description: l10n.repeater_cliHelpGetFreq,
      ),
      _CommandHelpEntry(
        command: 'get radio',
        description: l10n.repeater_cliHelpGetRadio,
      ),
      _CommandHelpEntry(
        command: 'get radio.rxgain',
        description: l10n.repeater_cliHelpGetRadioRxGain,
      ),
      _CommandHelpEntry(
        command: 'get af',
        description: l10n.repeater_cliHelpGetAf,
      ),
      _CommandHelpEntry(
        command: 'get dutycycle',
        description: l10n.repeater_cliHelpGetDutyCycle,
      ),
      _CommandHelpEntry(
        command: 'get int.thresh',
        description: l10n.repeater_cliHelpGetIntThresh,
      ),
      _CommandHelpEntry(
        command: 'get agc.reset.interval',
        description: l10n.repeater_cliHelpGetAgcResetInterval,
      ),
      _CommandHelpEntry(
        command: 'get multi.acks',
        description: l10n.repeater_cliHelpGetMultiAcks,
      ),
      _CommandHelpEntry(
        command: 'get allow.read.only',
        description: l10n.repeater_cliHelpGetAllowReadOnly,
      ),
      _CommandHelpEntry(
        command: 'get advert.interval',
        description: l10n.repeater_cliHelpGetAdvertInterval,
      ),
      _CommandHelpEntry(
        command: 'get flood.advert.interval',
        description: l10n.repeater_cliHelpGetFloodAdvertInterval,
      ),
      _CommandHelpEntry(
        command: 'get guest.password',
        description: l10n.repeater_cliHelpGetGuestPassword,
      ),
      _CommandHelpEntry(
        command: 'get lat',
        description: l10n.repeater_cliHelpGetLat,
      ),
      _CommandHelpEntry(
        command: 'get lon',
        description: l10n.repeater_cliHelpGetLon,
      ),
      _CommandHelpEntry(
        command: 'get rxdelay',
        description: l10n.repeater_cliHelpGetRxDelay,
      ),
      _CommandHelpEntry(
        command: 'get txdelay',
        description: l10n.repeater_cliHelpGetTxDelay,
      ),
      _CommandHelpEntry(
        command: 'get direct.txdelay',
        description: l10n.repeater_cliHelpGetDirectTxDelay,
      ),
      _CommandHelpEntry(
        command: 'get flood.max',
        description: l10n.repeater_cliHelpGetFloodMax,
      ),
      _CommandHelpEntry(
        command: 'get owner.info',
        description: l10n.repeater_cliHelpGetOwnerInfo,
      ),
      _CommandHelpEntry(
        command: 'get path.hash.mode',
        description: l10n.repeater_cliHelpGetPathHashMode,
      ),
      _CommandHelpEntry(
        command: 'get loop.detect',
        description: l10n.repeater_cliHelpGetLoopDetect,
      ),
      _CommandHelpEntry(
        command: 'get acl',
        description: l10n.repeater_cliHelpGetAcl,
      ),
      _CommandHelpEntry(
        command: 'get bridge.enabled',
        description: l10n.repeater_cliHelpGetBridgeEnabled,
      ),
      _CommandHelpEntry(
        command: 'get bridge.delay',
        description: l10n.repeater_cliHelpGetBridgeDelay,
      ),
      _CommandHelpEntry(
        command: 'get bridge.source',
        description: l10n.repeater_cliHelpGetBridgeSource,
      ),
      _CommandHelpEntry(
        command: 'get bridge.baud',
        description: l10n.repeater_cliHelpGetBridgeBaud,
      ),
      _CommandHelpEntry(
        command: 'get bridge.channel',
        description: l10n.repeater_cliHelpGetBridgeChannel,
      ),
      _CommandHelpEntry(
        command: 'get bridge.secret',
        description: l10n.repeater_cliHelpGetBridgeSecret,
      ),
      _CommandHelpEntry(
        command: 'get bootloader.ver',
        description: l10n.repeater_cliHelpGetBootloaderVer,
      ),
      _CommandHelpEntry(
        command: 'get adc.multiplier',
        description: l10n.repeater_cliHelpGetAdcMultiplier,
      ),
    ];

    final powerMgmtCommands = [
      _CommandHelpEntry(
        command: 'get pwrmgt.support',
        description: l10n.repeater_cliHelpGetPwrMgtSupport,
      ),
      _CommandHelpEntry(
        command: 'get pwrmgt.source',
        description: l10n.repeater_cliHelpGetPwrMgtSource,
      ),
      _CommandHelpEntry(
        command: 'get pwrmgt.bootreason',
        description: l10n.repeater_cliHelpGetPwrMgtBootReason,
      ),
      _CommandHelpEntry(
        command: 'get pwrmgt.bootmv',
        description: l10n.repeater_cliHelpGetPwrMgtBootMv,
      ),
    ];

    final sensorCommands = [
      _CommandHelpEntry(
        command: 'sensor get {key}',
        description: l10n.repeater_cliHelpSensorGet,
      ),
      _CommandHelpEntry(
        command: 'sensor set {key} {value}',
        description: l10n.repeater_cliHelpSensorSet,
      ),
      _CommandHelpEntry(
        command: 'sensor list [start]',
        description: l10n.repeater_cliHelpSensorList,
      ),
    ];

    final gpsCommands = [
      _CommandHelpEntry(command: 'gps', description: l10n.repeater_cliHelpGps),
      _CommandHelpEntry(
        command: 'gps {on|off}',
        description: l10n.repeater_cliHelpGpsOnOff,
      ),
      _CommandHelpEntry(
        command: 'gps sync',
        description: l10n.repeater_cliHelpGpsSync,
      ),
      _CommandHelpEntry(
        command: 'gps setloc',
        description: l10n.repeater_cliHelpGpsSetLoc,
      ),
      _CommandHelpEntry(
        command: 'gps advert',
        description: l10n.repeater_cliHelpGpsAdvert,
      ),
      _CommandHelpEntry(
        command: 'gps advert {none|share|prefs}',
        description: l10n.repeater_cliHelpGpsAdvertSet,
      ),
    ];

    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text(l10n.repeater_commandsListTitle),
        content: SingleChildScrollView(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                l10n.repeater_commandsListNote,
                style: const TextStyle(fontSize: 13),
              ),
              const SizedBox(height: 16),
              _buildHelpSection(
                context,
                l10n.repeater_general,
                generalCommands,
              ),
              const SizedBox(height: 16),
              _buildHelpSection(
                context,
                l10n.repeater_getCategory,
                getCommands,
              ),
              const SizedBox(height: 16),
              _buildHelpSection(
                context,
                l10n.repeater_settingsCategory,
                settingsCommands,
              ),
              const SizedBox(height: 16),
              _buildHelpSection(
                context,
                l10n.repeater_powerMgmt,
                powerMgmtCommands,
              ),
              const SizedBox(height: 16),
              _buildHelpSection(context, l10n.repeater_sensors, sensorCommands),
              const SizedBox(height: 16),
              _buildHelpSection(context, l10n.repeater_bridge, bridgeCommands),
              const SizedBox(height: 16),
              _buildHelpSection(
                context,
                l10n.repeater_logging,
                loggingCommands,
              ),
              const SizedBox(height: 16),
              _buildHelpSection(
                context,
                l10n.repeater_neighborsRepeaterOnly,
                neighborCommands,
              ),
              const SizedBox(height: 16),
              _buildHelpSection(
                context,
                l10n.repeater_regionManagementRepeaterOnly,
                regionCommands,
                note: l10n.repeater_regionNote,
              ),
              const SizedBox(height: 16),
              _buildHelpSection(
                context,
                l10n.repeater_gpsManagement,
                gpsCommands,
                note: l10n.repeater_gpsNote,
              ),
            ],
          ),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: Text(l10n.common_close),
          ),
        ],
      ),
    );
  }

  Widget _buildHelpSection(
    BuildContext context,
    String title,
    List<_CommandHelpEntry> commands, {
    String? note,
  }) {
    final scheme = Theme.of(context).colorScheme;
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          title,
          style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
        ),
        if (note != null) ...[
          const SizedBox(height: 4),
          Text(
            note,
            style: TextStyle(fontSize: 11, color: scheme.onSurfaceVariant),
          ),
        ],
        const SizedBox(height: 8),
        ...commands.map((entry) => _buildHelpCommandCard(context, entry)),
      ],
    );
  }

  Widget _buildHelpCommandCard(BuildContext context, _CommandHelpEntry entry) {
    final scheme = Theme.of(context).colorScheme;
    return Card(
      elevation: 0,
      margin: const EdgeInsets.only(bottom: 6),
      color: scheme.surfaceContainerHighest,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(MeshRadii.sm),
        side: BorderSide(color: scheme.outlineVariant),
      ),
      child: InkWell(
        borderRadius: BorderRadius.circular(MeshRadii.sm),
        onTap: () => _applyHelpCommand(entry.command),
        child: Padding(
          padding: const EdgeInsets.all(10),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                entry.command,
                style: MeshTheme.mono(
                  fontSize: 12,
                  fontWeight: FontWeight.w600,
                  color: MeshPalette.blue,
                ),
              ),
              const SizedBox(height: 4),
              Text(
                entry.description,
                style: TextStyle(fontSize: 12, color: scheme.onSurfaceVariant),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class _CommandHelpEntry {
  final String command;
  final String description;
  const _CommandHelpEntry({required this.command, required this.description});
}
