part of '../settings_modal.dart';

class _VoiceSection extends StatefulWidget {
  final PyramidTheme pt;
  const _VoiceSection({required this.pt});

  @override
  State<_VoiceSection> createState() => _VoiceSectionState();
}

class _VoiceSectionState extends State<_VoiceSection> {
  List<MediaDeviceInfo> _audioInputs = [];
  List<MediaDeviceInfo> _audioOutputs = [];
  List<MediaDeviceInfo> _videoInputs = [];
  String? _selectedMic;
  String? _selectedSpeaker;
  String? _selectedCamera;
  double _outputVolume = 1.0;
  bool _loading = true;
  String? _loadError;

  static const _kMic = 'voice_mic_device';
  static const _kSpeaker = 'voice_speaker_device';
  static const _kCamera = 'voice_camera_device';
  static const _kOutputVolume = 'voice_output_volume';

  @override
  void initState() {
    super.initState();
    _init();
  }

  Future<void> _init() async {
    final prefs = await SharedPreferences.getInstance();
    List<MediaDeviceInfo> devices = [];
    String? loadError;
    try {
      devices = await navigator.mediaDevices.enumerateDevices();
    } catch (_) {
      devices = [];
    }
    // Windows (und teils Android) liefert eine LEERE Liste ohne Fehler,
    // solange in diesem Prozess noch nie ein Media-Stream geöffnet wurde.
    // Deshalb nicht nur bei Exception, sondern auch bei leerer Liste einmal
    // kurz getUserMedia anfordern und erneut enumerieren — und zwar SOLANGE
    // der Stream noch offen ist (nach stop() melden manche Systeme wieder 0).
    final hasAudioInput =
        devices.any((d) => d.kind?.toLowerCase() == 'audioinput');
    if (devices.isEmpty || !hasAudioInput) {
      try {
        final stream = await navigator.mediaDevices
            .getUserMedia({'audio': true, 'video': false});
        try {
          devices = await navigator.mediaDevices.enumerateDevices();
        } finally {
          for (final t in stream.getTracks()) {
            t.stop();
          }
          await stream.dispose();
        }
        if (devices.isEmpty) {
          // Bekanntes libwebrtc-Problem auf manchen Windows-Systemen: das
          // Audio-Device-Module meldet 0 Geräte, obwohl Aufnahme/Wiedergabe
          // über das Standardgerät einwandfrei funktionieren.
          loadError =
              'Die Audiotreiber melden keine Geräteliste an WebRTC '
              '(bekanntes libwebrtc-Problem auf manchen Windows-Systemen). '
              'Anrufe funktionieren trotzdem — es wird das Windows-Standardgerät verwendet.';
        }
      } catch (e2) {
        loadError = 'getUserMedia: ${e2.toString().split('\n').first}';
      }
    }
    if (!mounted) return;
    setState(() {
      _audioInputs = devices.where((d) => d.kind?.toLowerCase() == 'audioinput').toList();
      _audioOutputs = devices.where((d) => d.kind?.toLowerCase() == 'audiooutput').toList();
      _videoInputs = devices.where((d) => d.kind?.toLowerCase() == 'videoinput').toList();
      _selectedMic = prefs.getString(_kMic);
      _selectedSpeaker = prefs.getString(_kSpeaker);
      _selectedCamera = prefs.getString(_kCamera);
      _outputVolume = prefs.getDouble(_kOutputVolume) ?? 1.0;
      _loadError = loadError;
      _loading = false;
    });
  }

  Future<void> _setOutputVolume(double v) async {
    setState(() => _outputVolume = v);
    final prefs = await SharedPreferences.getInstance();
    await prefs.setDouble(_kOutputVolume, v);
    // Live auf laufende Calls anwenden.
    LiveKitCallManager.instance.setOutputVolume(v).catchError((_) {});
    try {
      PyramidVoipManager.instance.applyOutputVolume(v).catchError((_) {});
    } catch (_) {} // Instanz existiert evtl. noch nicht
  }

  Future<void> _save(String key, String? value) async {
    final prefs = await SharedPreferences.getInstance();
    if (value == null || value.isEmpty) {
      await prefs.remove(key);
    } else {
      await prefs.setString(key, value);
    }
  }

  Widget _deviceDropdown({
    required List<MediaDeviceInfo> devices,
    required String? selected,
    required String placeholder,
    required IconData icon,
    required ValueChanged<String?> onChanged,
  }) {
    final pt = widget.pt;
    if (devices.isEmpty) {
      return Container(
        padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
        decoration: BoxDecoration(
          color: pt.bg3,
          borderRadius: BorderRadius.circular(6),
          border: Border.all(color: pt.border),
        ),
        child: Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            Icon(icon, size: 13, color: pt.fgDim),
            const SizedBox(width: 6),
            Text('Kein Gerät', style: TextStyle(color: pt.fgDim, fontSize: 12)),
          ],
        ),
      );
    }

    final validSelected = devices.any((d) => d.deviceId == selected) ? selected : null;

    return ConstrainedBox(
      constraints: const BoxConstraints(maxWidth: 220),
      child: Container(
        padding: const EdgeInsets.symmetric(horizontal: 10),
        decoration: BoxDecoration(
          color: pt.bg3,
          borderRadius: BorderRadius.circular(6),
          border: Border.all(color: pt.border),
        ),
        child: DropdownButtonHideUnderline(
          child: DropdownButton<String?>(
            value: validSelected,
            hint: Row(children: [
              Icon(icon, size: 13, color: pt.fgDim),
              const SizedBox(width: 6),
              Text(placeholder, style: TextStyle(color: pt.fgDim, fontSize: 12)),
            ]),
            dropdownColor: pt.bg2,
            iconEnabledColor: pt.fgDim,
            isDense: true,
            style: TextStyle(color: pt.fg, fontSize: 12),
            onChanged: onChanged,
            items: devices.map((d) {
              final label = d.label.isNotEmpty ? d.label : 'Gerät ${d.deviceId.substring(0, 6)}';
              return DropdownMenuItem<String?>(
                value: d.deviceId,
                child: Text(label, overflow: TextOverflow.ellipsis),
              );
            }).toList(),
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    final pt = widget.pt;
    return SingleChildScrollView(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _SectionTitle(
              title: 'Sprache & Video',
              subtitle: 'Mikrofon, Kamera und Anrufeinstellungen.',
              pt: pt),
          if (_loading)
            const Center(child: Padding(padding: EdgeInsets.all(32), child: CircularProgressIndicator.adaptive()))
          else ...[
            if (_loadError != null)
              Padding(
                padding: const EdgeInsets.only(bottom: 16),
                child: Container(
                  padding: const EdgeInsets.all(12),
                  decoration: BoxDecoration(
                    color: pt.bg2,
                    borderRadius: BorderRadius.circular(8),
                    border: Border.all(color: pt.border),
                  ),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Row(children: [
                        Icon(Icons.warning_amber_rounded, size: 16, color: pt.away),
                        const SizedBox(width: 8),
                        Text('Geräteliste nicht verfügbar',
                            style: TextStyle(color: pt.fg, fontSize: 13, fontWeight: FontWeight.w500)),
                      ]),
                      const SizedBox(height: 6),
                      Text(_loadError!, style: TextStyle(color: pt.fgDim, fontSize: 11)),
                      const SizedBox(height: 6),
                      Text(
                        'Mikrofon und Lautsprecher legst du in diesem Fall über die '
                        'Windows-Soundeinstellungen (Standardgerät) fest. '
                        'Falls dort alles stimmt: Windows-Einstellungen → Datenschutz → Mikrofon prüfen.',
                        style: TextStyle(color: pt.fgMuted, fontSize: 11),
                      ),
                    ],
                  ),
                ),
              ),
            _SettingsGroup(title: 'Geräte', pt: pt, children: [
              _SettingsRow(
                title: 'Mikrofon',
                desc: _audioInputs.isEmpty ? 'Kein Gerät gefunden' : '${_audioInputs.length} Gerät(e) gefunden',
                pt: pt,
                control: _deviceDropdown(
                  devices: _audioInputs,
                  selected: _selectedMic,
                  placeholder: 'Standard',
                  icon: Icons.mic_rounded,
                  onChanged: (v) {
                    setState(() => _selectedMic = v);
                    _save(_kMic, v);
                  },
                ),
              ),
              _Divider(pt: pt),
              _SettingsRow(
                title: 'Lautsprecher',
                desc: _audioOutputs.isEmpty ? 'Kein Gerät gefunden' : '${_audioOutputs.length} Gerät(e) gefunden',
                pt: pt,
                control: _deviceDropdown(
                  devices: _audioOutputs,
                  selected: _selectedSpeaker,
                  placeholder: 'Standard',
                  icon: Icons.volume_up_rounded,
                  onChanged: (v) {
                    setState(() => _selectedSpeaker = v);
                    _save(_kSpeaker, v);
                  },
                ),
              ),
              _Divider(pt: pt),
              _SettingsRow(
                title: 'Kamera',
                desc: _videoInputs.isEmpty ? 'Kein Gerät gefunden' : '${_videoInputs.length} Gerät(e) gefunden',
                pt: pt,
                control: _deviceDropdown(
                  devices: _videoInputs,
                  selected: _selectedCamera,
                  placeholder: 'Standard',
                  icon: Icons.videocam_rounded,
                  onChanged: (v) {
                    setState(() => _selectedCamera = v);
                    _save(_kCamera, v);
                  },
                ),
              ),
            ]),
            const SizedBox(height: 16),
            _SettingsGroup(title: 'Pegel', pt: pt, children: [
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Row(
                      children: [
                        Icon(Icons.volume_up_rounded, size: 15, color: pt.fgMuted),
                        const SizedBox(width: 8),
                        Text('Ausgabelautstärke',
                            style: TextStyle(
                                color: pt.fg,
                                fontSize: 13,
                                fontWeight: FontWeight.w500)),
                        const Spacer(),
                        Text('${(_outputVolume * 100).round()} %',
                            style: TextStyle(
                                color: pt.fgMuted,
                                fontSize: 12,
                                fontFamily: 'monospace')),
                      ],
                    ),
                    SliderTheme(
                      data: SliderThemeData(
                        activeTrackColor: pt.accent,
                        inactiveTrackColor: pt.bg3,
                        thumbColor: pt.accent,
                        overlayColor: pt.accent.withAlpha(30),
                        trackHeight: 3,
                        thumbShape: const RoundSliderThumbShape(
                            enabledThumbRadius: 7),
                      ),
                      child: Slider(
                        value: _outputVolume.clamp(0.0, 1.5),
                        min: 0.0,
                        max: 1.5,
                        divisions: 30,
                        onChanged: _setOutputVolume,
                      ),
                    ),
                    Text(
                      'Lautstärke der anderen Teilnehmer in Calls. '
                      'Wirkt sofort auf laufende Anrufe.',
                      style: TextStyle(color: pt.fgDim, fontSize: 11),
                    ),
                  ],
                ),
              ),
            ]),
            const SizedBox(height: 16),
            _TurnUsageCard(pt: pt),
            const SizedBox(height: 12),
            TextButton.icon(
              style: TextButton.styleFrom(
                foregroundColor: pt.fgMuted,
                padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
              ),
              onPressed: () { setState(() { _loading = true; _loadError = null; }); _init(); },
              icon: const Icon(Icons.refresh_rounded, size: 14),
              label: const Text('Geräte neu laden', style: TextStyle(fontSize: 12)),
            ),
          ],
        ],
      ),
    );
  }
}

class _TurnUsageCard extends StatefulWidget {
  final PyramidTheme pt;
  const _TurnUsageCard({required this.pt});

  @override
  State<_TurnUsageCard> createState() => _TurnUsageCardState();
}

class _TurnUsageCardState extends State<_TurnUsageCard> {
  Map<String, dynamic>? _status;

  @override
  void initState() {
    super.initState();
    _load();
  }

  Future<void> _load() async {
    try {
      final resp = await http
          .get(Uri.parse(
              'https://steggi-matrix.work/.well-known/pyramid/turn-status.json'))
          .timeout(const Duration(seconds: 5));
      if (resp.statusCode == 200 && mounted) {
        setState(
            () => _status = jsonDecode(resp.body) as Map<String, dynamic>);
      }
    } catch (_) {
      // Endpoint (noch) nicht vorhanden — Karte einfach nicht anzeigen.
    }
  }

  @override
  Widget build(BuildContext context) {
    final s = _status;
    if (s == null) return const SizedBox.shrink();
    final pt = widget.pt;
    final used = (s['egress_gb'] as num?)?.toDouble() ?? 0;
    final limit = (s['limit_gb'] as num?)?.toDouble() ?? 990;
    final pct = limit > 0 ? (used / limit).clamp(0.0, 1.0) : 0.0;
    final active = s['turn_active'] != false;
    final barColor = !active
        ? pt.danger
        : pct > 0.8
            ? pt.away
            : pt.accent;

    return _SettingsGroup(title: 'Call-Relay (TURN)', pt: pt, children: [
      Padding(
        padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(children: [
              Icon(active ? Icons.cloud_done_rounded : Icons.cloud_off_rounded,
                  size: 15, color: active ? pt.online : pt.danger),
              const SizedBox(width: 8),
              Text(
                  active
                      ? 'Relay aktiv'
                      : 'Relay deaktiviert (Limit erreicht)',
                  style: TextStyle(
                      color: pt.fg,
                      fontSize: 13,
                      fontWeight: FontWeight.w500)),
              const Spacer(),
              Text(
                  '${used.toStringAsFixed(1)} / ${limit.toStringAsFixed(0)} GB',
                  style: TextStyle(
                      color: pt.fgMuted,
                      fontSize: 12,
                      fontFamily: 'monospace')),
            ]),
            const SizedBox(height: 8),
            ClipRRect(
              borderRadius: BorderRadius.circular(99),
              child: LinearProgressIndicator(
                value: pct,
                minHeight: 5,
                backgroundColor: pt.bg3,
                color: barColor,
              ),
            ),
            const SizedBox(height: 6),
            Text(
              active
                  ? 'Cloudflare-TURN-Traffic diesen Monat (Free Tier: ${limit.toStringAsFixed(0)} GB). '
                      'Bei Erreichen des Limits laufen Calls bis Monatsende ohne Relay weiter.'
                  : 'Monatslimit erreicht — Calls nutzen bis Monatsende nur direkte Verbindungen (STUN).',
              style: TextStyle(color: pt.fgDim, fontSize: 11),
            ),
          ],
        ),
      ),
    ]);
  }
}
