// Voice bar widget (SDD-097). Replaces the legacy `_AudioControls`
// block that used to live in `main.dart`. Driven by the
// `BridgeEvent::VoiceState` stream the bridge publishes from the
// core's transmit-mode selector + release-tail timer.

import 'package:flutter/material.dart';

import '../l10n/generated/app_localizations.dart';
import 'ptt_capability_badge.dart';
import 'voice_compact.dart';
import 'voice_level_meter.dart';
import 'voice_platform.dart';
import 'voice_status_summary.dart';
import '../src/rust/api.dart' as rust;

/// Voice bar — surfaces the live voice state, mode badge, hard-mute
/// toggle, level meter, and a leave-channel affordance.
class VoiceBar extends StatelessWidget {
  /// Construct a voice bar.
  const VoiceBar({
    super.key,
    required this.inChannel,
    required this.transmitMode,
    required this.hardMute,
    required this.outputMuted,
    required this.releaseTailMs,
    required this.channelName,
    required this.audioStats,
    required this.pttLevel,
    required this.pttBackendId,
    required this.pttBoundInputClass,
    required this.pttBoundKeyLabel,
    required this.onConfigure,
    required this.onPttHeldChanged,
    this.talkPowerBlocked = false,
    this.inputLevel,
  });

  final bool inChannel;
  final rust.BridgeTransmitMode transmitMode;
  final bool hardMute;
  final bool outputMuted;
  final bool talkPowerBlocked;

  /// Configured release-tail in milliseconds (0..=500). Surfaced as
  /// a hint underneath the mode badge.
  final int releaseTailMs;

  /// Channel display name to show in the channel-name pill. Empty
  /// string suppresses the pill (typically when `!inChannel`).
  final String channelName;

  /// Audio statistics (TX/RX frames + ptt_active) to drive the
  /// level meter. Pass `null` to render an idle meter.
  final rust.BridgeAudioStats? audioStats;

  /// Real-time input level from the 30 Hz stream (dBFS).
  /// When non-null, takes precedence over `audioStats.inputLevel`.
  final double? inputLevel;

  /// PTT capability badge inputs — passed through to
  /// [`PttCapabilityBadge`].
  final String pttLevel;

  /// Backend id, e.g. `focused`, `windows-raw-input`.
  final String pttBackendId;

  /// Coarse bound input class.
  final String pttBoundInputClass;

  /// Platform-neutral key label captured by the binding dialog.
  final String pttBoundKeyLabel;

  /// Open the voice settings dialog. This is the SINGLE entry point
  /// for transmit-mode selection, PTT key binding, and release-tail
  /// configuration. The capability badge below is information-only
  /// and intentionally does NOT have its own configure affordance.
  final VoidCallback onConfigure;

  /// Drive the press/release edges of the on-screen PTT button on
  /// touch-only mobile platforms (iOS / iPadOS / Android). On
  /// desktop platforms this callback is wired but never invoked
  /// because the on-screen button is only rendered on mobile.
  /// The callee should map `true` to `setPtt(active: true)` and
  /// `false` to `setPtt(active: false)`; the Rust release-tail
  /// timer handles the trailing tail (SDD-096).
  final ValueChanged<bool> onPttHeldChanged;

  @override
  Widget build(BuildContext context) {
    final l10n = AppL10n.of(context);
    final theme = Theme.of(context);
    final stats = audioStats;
    final levelActive = stats?.pttActive ?? false;
    final isPtt = transmitMode == rust.BridgeTransmitMode.ptt;
    final summary = voiceStatusSummary(
      l10n: l10n,
      transmitMode: transmitMode,
      releaseTailMs: releaseTailMs,
      pttBoundKeyLabel: pttBoundKeyLabel,
      isTouchOnly: isTouchOnlyPttHost,
      inputMuted: hardMute,
      outputMuted: outputMuted,
      pttActive: stats?.pttActive ?? false,
      talkPower: talkPowerBlocked ? 0 : null,
      neededTalkPower: talkPowerBlocked ? 1 : null,
      talkPowerGranted: false,
    );

    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 0),
      child: Container(
        decoration: BoxDecoration(
          color: summary.talkPowerBlocked
              ? Colors.amber.withValues(alpha: 0.18)
              : summary.muted
              ? theme.colorScheme.errorContainer.withValues(alpha: 0.35)
              : theme.colorScheme.surfaceContainerHigh,
          borderRadius: BorderRadius.circular(12),
          border: Border.all(
            color: summary.talkPowerBlocked
                ? Colors.amber.shade700
                : summary.muted
                ? theme.colorScheme.error
                : theme.colorScheme.outlineVariant,
            width: summary.talkPowerBlocked || summary.muted ? 1.5 : 0.5,
          ),
        ),
        child: Padding(
          padding: const EdgeInsets.all(12),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              // Row 1: mode badge
              Row(
                children: [
                  Icon(
                    transmitMode == rust.BridgeTransmitMode.ptt
                        ? Icons.radio_button_checked
                        : Icons.podcasts,
                    size: 16,
                    color: theme.colorScheme.onSurfaceVariant,
                  ),
                  const SizedBox(width: 6),
                  Expanded(
                    child: Text(
                      summary.modeLabel,
                      style: theme.textTheme.bodyMedium?.copyWith(
                        color: theme.colorScheme.onSurfaceVariant,
                      ),
                    ),
                  ),
                  TextButton.icon(
                    icon: const Icon(Icons.tune, size: 16),
                    label: Text(l10n.voiceSettingsTitle),
                    onPressed: onConfigure,
                  ),
                ],
              ),
              // Status row: talk power / mic / speaker state.
              if (inChannel) ...[
                const SizedBox(height: 2),
                Text(
                  summary.talkPowerBlocked
                      ? 'Insufficient talk power'
                      : summary.statusText,
                  style: theme.textTheme.bodySmall?.copyWith(
                    color: summary.talkPowerBlocked
                        ? Colors.amber.shade700
                        : summary.muted
                        ? theme.colorScheme.error
                        : theme.colorScheme.onSurfaceVariant,
                  ),
                ),
              ],
              // Row 3: PTT-only secondary content.
              //
              // On hardware-keyboard hosts (Windows / macOS / Linux /
              // Web) this is a one-line bound-key + release-tail hint
              // sitting right under the mode badge.
              //
              // On touch-only hosts (iOS / iPadOS / Android) the
              // on-screen Push to Talk button is rendered AT THE
              // BOTTOM of the Voice Bar (see below) so it sits
              // closest to the user's thumb when the Voice Bar is
              // pinned to the bottom of a narrow-layout screen. The
              // release-tail value is folded into the small print
              // under the button rather than shown here.
              if (isPtt && !isTouchOnlyPttHost)
                Padding(
                  padding: const EdgeInsets.only(left: 22, top: 2),
                  child: Text(
                    '${l10n.voiceModePtt}: '
                    '${pttBoundKeyLabel.isEmpty ? "—" : pttBoundKeyLabel}'
                    '   ·   '
                    '${l10n.voiceReleaseTailLabel}: $releaseTailMs${l10n.voiceReleaseTailHint}',
                    style: theme.textTheme.bodySmall?.copyWith(
                      color: theme.colorScheme.onSurfaceVariant,
                    ),
                  ),
                ),
              const SizedBox(height: 6),
              // Row 4: level meter
              VoiceLevelMeter(active: levelActive, level: inputLevel ?? stats?.inputLevel),
              const SizedBox(height: 4),
              if (stats != null)
                Text(
                  l10n.audioStatsLine(
                    stats.framesSent,
                    stats.framesReceived,
                    stats.pttActive ? l10n.voiceMicOn : l10n.voiceMicOff,
                  ),
                  style: theme.textTheme.bodySmall,
                ),
              const SizedBox(height: 6),
              // PTT capability badge — only relevant when PTT mode is
              // active. Hidden for Continuous / Voice Activity since
              // there's no key binding to surface a capability for.
              // The badge is information-only; the user reaches the
              // bind-key flow through the Voice Bar's settings gear
              // (single configuration entry point — see the comment
              // on `onConfigure`).
              //
              // Still shown on touch-only mobile hosts because iOS P0
              // acceptance requires an explicit `L0Focused` badge and
              // explanation that global hotkeys are not available in
              // the iOS sandbox.
              if (isPtt)
                PttCapabilityBadge(
                  level: pttLevel,
                  backendId: pttBackendId,
                  boundInputClass: pttBoundInputClass,
                ),
              // On touch-only mobile hosts the Push to Talk button is
              // the LAST element of the Voice Bar so it lands closest
              // to the user's thumb when the Voice Bar is pinned to
              // the bottom of a narrow-layout screen. The release-
              // tail value sits above the button so the user sees
              // how long their voice continues after they let go.
              if (isPtt && isTouchOnlyPttHost) ...[
                const SizedBox(height: 4),
                Center(
                  child: Text(
                    '${l10n.voiceReleaseTailLabel}: $releaseTailMs${l10n.voiceReleaseTailHint}',
                    style: theme.textTheme.bodySmall?.copyWith(
                      color: theme.colorScheme.onSurfaceVariant,
                    ),
                  ),
                ),
                const SizedBox(height: 8),
                VoicePttButton(
                  active: levelActive,
                  onHeldChanged: onPttHeldChanged,
                  height: 64,
                  borderRadius: 12,
                  iconSize: 24,
                  iconGap: 10,
                  blurRadius: 12,
                  spreadRadius: 0,
                  listenForPan: true,
                  labelLetterSpacing: null,
                ),
              ],
              // Leave-voice button intentionally absent: TeamSpeak's
              // model is "user is always in some channel", not
              // Discord's join/leave-voice. To stop being heard /
              // hearing others, mute mic and/or speaker via the
              // icons at the top of the bar. To physically move,
              // tap a different channel in the tree below.
            ],
          ),
        ),
      ),
    );
  }
}
