part of '../../main.dart';

class _PredictionPage extends StatelessWidget {
  const _PredictionPage({
    required this.studentNameController,
    required this.studentNames,
    required this.studentGender,
    required this.hasStoredStudentGender,
    required this.selectedAudio,
    required this.liveWavePreview,
    required this.wavePreview,
    required this.prediction,
    required this.errorMessage,
    required this.isPredicting,
    required this.isSaving,
    required this.isRecording,
    required this.onAddStudentName,
    required this.onSelectStudentName,
    required this.onGenderChanged,
    required this.onPickAudio,
    required this.onToggleRecording,
    required this.onPredict,
    required this.onSave,
  });

  final TextEditingController studentNameController;
  final List<String> studentNames;
  final String studentGender;
  final bool hasStoredStudentGender;
  final SelectedAudio? selectedAudio;
  final List<double> liveWavePreview;
  final List<double> wavePreview;
  final PredictionResult? prediction;
  final String? errorMessage;
  final bool isPredicting;
  final bool isSaving;
  final bool isRecording;
  final Future<void> Function(String name) onAddStudentName;
  final ValueChanged<String> onSelectStudentName;
  final ValueChanged<String?> onGenderChanged;
  final VoidCallback onPickAudio;
  final VoidCallback onToggleRecording;
  final VoidCallback onPredict;
  final VoidCallback onSave;

  @override
  Widget build(BuildContext context) {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        Row(
          children: [
            Container(
              width: 48,
              height: 48,
              decoration: BoxDecoration(
                color: AppColors.navy,
                borderRadius: BorderRadius.circular(8),
                border: Border.all(color: AppColors.border),
              ),
              child: const Icon(
                Icons.record_voice_over_outlined,
                color: AppColors.blueSoft,
              ),
            ),
            const SizedBox(width: 12),
            Expanded(
              child: Text(
                'Analisis Percaya Diri',
                style: Theme.of(context).textTheme.headlineSmall,
              ),
            ),
          ],
        ),
        const SizedBox(height: 4),
        Text(
          'Isi nama siswa/i, pilih atau rekam audio, lalu lihat hasil analisis.',
          style: Theme.of(context).textTheme.bodyMedium,
        ),
        const SizedBox(height: 16),
        Card(
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: _StudentNameSearchField(
              controller: studentNameController,
              names: studentNames,
              onAddName: onAddStudentName,
              onSelectName: onSelectStudentName,
            ),
          ),
        ),
        const SizedBox(height: 12),
        Card(
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: _StudentGenderField(
              value: studentGender,
              isStored: hasStoredStudentGender,
              onChanged: onGenderChanged,
            ),
          ),
        ),
        const SizedBox(height: 16),
        _AudioCard(
          selectedAudio: selectedAudio,
          liveWavePreview: liveWavePreview,
          wavePreview: wavePreview,
          onPickAudio: onPickAudio,
          onToggleRecording: onToggleRecording,
          isRecording: isRecording,
        ),
        const SizedBox(height: 16),
        FilledButton.icon(
          onPressed: isPredicting || isRecording ? null : onPredict,
          icon:
              isPredicting
                  ? const SizedBox.square(
                    dimension: 18,
                    child: CircularProgressIndicator(strokeWidth: 2),
                  )
                  : const Icon(Icons.analytics_outlined),
          label: Text(isPredicting ? 'Memproses...' : 'Prediksi'),
        ),
        if (errorMessage != null) ...[
          const SizedBox(height: 12),
          _WarningBox(message: errorMessage!),
        ],
        if (prediction != null) ...[
          const SizedBox(height: 16),
          _PredictionResultCard(result: prediction!),
          if (prediction!.isValidAudio) ...[
            const SizedBox(height: 12),
            FilledButton.icon(
              onPressed: isSaving ? null : onSave,
              icon:
                  isSaving
                      ? const SizedBox.square(
                        dimension: 18,
                        child: CircularProgressIndicator(strokeWidth: 2),
                      )
                      : const Icon(Icons.save_outlined),
              label: Text(isSaving ? 'Menyimpan...' : 'Simpan Hasil'),
            ),
          ],
        ],
      ],
    );
  }
}

class _StudentNameSearchField extends StatefulWidget {
  const _StudentNameSearchField({
    required this.controller,
    required this.names,
    required this.onAddName,
    required this.onSelectName,
  });

  final TextEditingController controller;
  final List<String> names;
  final Future<void> Function(String name) onAddName;
  final ValueChanged<String> onSelectName;

  @override
  State<_StudentNameSearchField> createState() =>
      _StudentNameSearchFieldState();
}

class _StudentGenderField extends StatelessWidget {
  const _StudentGenderField({
    required this.value,
    required this.isStored,
    required this.onChanged,
  });

  final String value;
  final bool isStored;
  final ValueChanged<String?> onChanged;

  @override
  Widget build(BuildContext context) {
    if (isStored && value.isNotEmpty) {
      return InputDecorator(
        decoration: const InputDecoration(
          border: OutlineInputBorder(),
          prefixIcon: Icon(Icons.wc_outlined),
          labelText: 'Jenis kelamin siswa/i',
        ),
        child: Row(
          children: [
            Expanded(child: Text(value)),
            const Text(
              'Tersimpan',
              style: TextStyle(color: AppColors.blueSoft),
            ),
          ],
        ),
      );
    }

    return DropdownButtonFormField<String>(
      value: value.isEmpty ? null : value,
      decoration: const InputDecoration(
        border: OutlineInputBorder(),
        prefixIcon: Icon(Icons.wc_outlined),
        labelText: 'Jenis kelamin siswa/i',
      ),
      items: const [
        DropdownMenuItem(value: 'Laki-laki', child: Text('Laki-laki')),
        DropdownMenuItem(value: 'Perempuan', child: Text('Perempuan')),
      ],
      onChanged: onChanged,
    );
  }
}

class _StudentNameSearchFieldState extends State<_StudentNameSearchField> {
  final FocusNode _focusNode = FocusNode();
  String _query = '';

  @override
  void initState() {
    super.initState();
    _query = widget.controller.text;
    _focusNode.addListener(_handleFocusChanged);
  }

  @override
  void dispose() {
    _focusNode
      ..removeListener(_handleFocusChanged)
      ..dispose();
    super.dispose();
  }

  @override
  void didUpdateWidget(covariant _StudentNameSearchField oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.controller != widget.controller) {
      _query = widget.controller.text;
    }
  }

  void _handleFocusChanged() {
    if (mounted) {
      setState(() {});
    }
  }

  String _cleanName(String name) {
    return name
        .trim()
        .split(RegExp(r'\s+'))
        .where((part) => part.isNotEmpty)
        .join(' ');
  }

  List<String> get _matches {
    final cleanQuery = _cleanName(_query).toLowerCase();
    if (cleanQuery.isEmpty) {
      return widget.names.take(5).toList();
    }
    return widget.names
        .where((name) => name.toLowerCase().contains(cleanQuery))
        .take(5)
        .toList();
  }

  bool get _hasExactMatch {
    final cleanQuery = _cleanName(_query);
    return cleanQuery.isNotEmpty &&
        widget.names.any(
          (name) => name.toLowerCase() == cleanQuery.toLowerCase(),
        );
  }

  bool get _shouldShowPanel {
    return _focusNode.hasFocus &&
        (_matches.isNotEmpty || _cleanName(_query).isNotEmpty);
  }

  Future<void> _addCurrentName() async {
    final cleanName = _cleanName(_query);
    if (cleanName.isEmpty) {
      return;
    }
    await widget.onAddName(cleanName);
    widget.controller.text = cleanName;
    widget.controller.selection = TextSelection.collapsed(
      offset: cleanName.length,
    );
    if (mounted) {
      setState(() {
        _query = cleanName;
      });
    }
  }

  void _handleAddNameTap() {
    _addCurrentName();
    _focusNode.unfocus();
  }

  void _selectName(String name) {
    widget.controller.text = name;
    widget.controller.selection = TextSelection.collapsed(offset: name.length);
    setState(() {
      _query = name;
    });
    widget.onSelectName(name);
    _focusNode.unfocus();
  }

  @override
  Widget build(BuildContext context) {
    final matches = _matches;
    final cleanQuery = _cleanName(_query);

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        TextField(
          controller: widget.controller,
          focusNode: _focusNode,
          decoration: const InputDecoration(
            border: OutlineInputBorder(),
            prefixIcon: Icon(Icons.search),
            labelText: 'Cari atau tambah nama siswa/i',
          ),
          textInputAction: TextInputAction.next,
          onChanged: (value) {
            setState(() {
              _query = value;
            });
          },
        ),
        if (_shouldShowPanel) ...[
          const SizedBox(height: 10),
          Container(
            width: double.infinity,
            decoration: BoxDecoration(
              color: AppColors.surfaceRaised,
              borderRadius: BorderRadius.circular(8),
              border: Border.all(color: AppColors.border),
            ),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                for (final name in matches)
                  _StudentNameOption(
                    icon: Icons.person_outline,
                    label: name,
                    onSelect: () => _selectName(name),
                  ),
                if (cleanQuery.isNotEmpty && !_hasExactMatch)
                  _StudentNameOption(
                    icon: Icons.add_circle_outline,
                    label: 'Tambah "$cleanQuery"',
                    onSelect: _handleAddNameTap,
                  ),
              ],
            ),
          ),
        ],
      ],
    );
  }
}

class _StudentNameOption extends StatelessWidget {
  const _StudentNameOption({
    required this.icon,
    required this.label,
    required this.onSelect,
  });

  final IconData icon;
  final String label;
  final VoidCallback onSelect;

  @override
  Widget build(BuildContext context) {
    return Material(
      color: Colors.transparent,
      child: InkWell(
        onTapDown: (_) => onSelect(),
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
          child: Row(
            children: [
              Icon(icon, color: AppColors.blueSoft, size: 20),
              const SizedBox(width: 12),
              Expanded(
                child: Text(
                  label,
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class _AudioCard extends StatelessWidget {
  const _AudioCard({
    required this.selectedAudio,
    required this.liveWavePreview,
    required this.wavePreview,
    required this.onPickAudio,
    required this.onToggleRecording,
    required this.isRecording,
  });

  final SelectedAudio? selectedAudio;
  final List<double> liveWavePreview;
  final List<double> wavePreview;
  final VoidCallback onPickAudio;
  final VoidCallback onToggleRecording;
  final bool isRecording;

  @override
  Widget build(BuildContext context) {
    final file = selectedAudio;
    final displayWave = isRecording ? liveWavePreview : wavePreview;

    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Icon(
                  Icons.audio_file,
                  color: Theme.of(context).colorScheme.primary,
                ),
                const SizedBox(width: 10),
                Expanded(
                  child: Text(
                    file == null ? 'Belum ada audio' : file.name,
                    maxLines: 2,
                    overflow: TextOverflow.ellipsis,
                    style: Theme.of(context).textTheme.titleMedium,
                  ),
                ),
              ],
            ),
            const SizedBox(height: 12),
            SizedBox(
              height: 120,
              child: WaveformPreview(values: displayWave, isLive: isRecording),
            ),
            const SizedBox(height: 12),
            Row(
              children: [
                Expanded(
                  child: OutlinedButton.icon(
                    onPressed: isRecording ? null : onPickAudio,
                    icon: const Icon(Icons.folder_open),
                    label: const Text('Pilih Audio'),
                  ),
                ),
                const SizedBox(width: 10),
                Expanded(
                  child: FilledButton.icon(
                    onPressed: onToggleRecording,
                    icon: Icon(
                      isRecording ? Icons.stop : Icons.fiber_manual_record,
                    ),
                    label: Text(isRecording ? 'Stop' : 'Rekam'),
                  ),
                ),
              ],
            ),
            if (isRecording) ...[
              const SizedBox(height: 10),
              const _RecordingBanner(),
            ],
          ],
        ),
      ),
    );
  }
}

class _RecordingBanner extends StatelessWidget {
  const _RecordingBanner();

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(
        color: AppColors.recordingBackground,
        borderRadius: BorderRadius.circular(8),
        border: Border.all(color: AppColors.recordingBorder),
      ),
      child: const Row(
        children: [
          Icon(Icons.mic, color: AppColors.recording),
          SizedBox(width: 10),
          Expanded(
            child: Text('Sedang merekam suara... tekan Stop setelah selesai.'),
          ),
        ],
      ),
    );
  }
}

class _PredictionResultCard extends StatelessWidget {
  const _PredictionResultCard({required this.result});

  final PredictionResult result;

  @override
  Widget build(BuildContext context) {
    if (!result.isValidAudio) {
      return _InvalidAudioCard(result: result);
    }

    final pdConfidencePercent = (result.probabilityPd * 100).toStringAsFixed(2);
    final lowConfidence = result.confidence < 0.60;

    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'Hasil Prediksi',
              style: Theme.of(context).textTheme.titleLarge,
            ),
            if (result.studentName.isNotEmpty || result.id != null) ...[
              const SizedBox(height: 8),
              Text(
                [
                  if (result.studentName.isNotEmpty) result.studentName,
                  result.genderLabel,
                  if (result.id != null) 'ID #${result.id}',
                ].join(' - '),
                style: Theme.of(context).textTheme.bodyMedium,
              ),
            ],
            const SizedBox(height: 12),
            Row(
              children: [
                Expanded(
                  child: _MetricBox(label: 'Label', value: result.label),
                ),
                const SizedBox(width: 10),
                Expanded(
                  child: _MetricBox(
                    label: 'Confidence',
                    value: '$pdConfidencePercent%',
                  ),
                ),
              ],
            ),
            const SizedBox(height: 12),
            Text(
              result.description,
              style: Theme.of(context).textTheme.titleMedium,
            ),
            if (result.explanation.isNotEmpty) ...[
              const SizedBox(height: 8),
              Text(result.explanation),
            ],
            const SizedBox(height: 16),
            _ProbabilityBar(label: 'PD', value: result.probabilityPd),
            const SizedBox(height: 10),
            _ProbabilityBar(label: 'TPD', value: result.probabilityTpd),
            const SizedBox(height: 12),
            _DetailRow(
              label: 'Kualitas audio',
              value: _qualityStatus(result.audioQuality),
            ),
            if (lowConfidence) ...[
              const SizedBox(height: 12),
              const _WarningBox(
                message:
                    'Model belum yakin. Confidence di bawah 60%, coba rekam ulang dengan suara lebih jelas.',
              ),
            ],
          ],
        ),
      ),
    );
  }
}

class _InvalidAudioCard extends StatelessWidget {
  const _InvalidAudioCard({required this.result});

  final PredictionResult result;

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'Audio Tidak Valid',
              style: Theme.of(context).textTheme.titleLarge,
            ),
            const SizedBox(height: 12),
            _WarningBox(message: result.errorMessage ?? result.explanation),
            const SizedBox(height: 12),
            const Text(
              'Silakan rekam ulang dengan durasi minimal 2 detik, suara jelas, dan volume normal.',
            ),
          ],
        ),
      ),
    );
  }
}

String _qualityStatus(AudioQuality quality) {
  if (quality.isTooShort) {
    return 'Terlalu pendek';
  }
  if (quality.isClipped) {
    return 'Terlalu keras';
  }
  if (quality.isTooQuiet) {
    return 'Terlalu pelan';
  }
  return 'Baik';
}

class _DetailRow extends StatelessWidget {
  const _DetailRow({required this.label, required this.value});

  final String label;
  final String value;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 3),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text(label),
          const SizedBox(width: 16),
          Flexible(
            child: Text(
              value,
              textAlign: TextAlign.end,
              style: const TextStyle(fontWeight: FontWeight.w600),
            ),
          ),
        ],
      ),
    );
  }
}

class _MetricBox extends StatelessWidget {
  const _MetricBox({required this.label, required this.value});

  final String label;
  final String value;

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(
        color: AppColors.surfaceRaised,
        borderRadius: BorderRadius.circular(8),
        border: Border.all(color: AppColors.border),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(label, style: Theme.of(context).textTheme.labelMedium),
          const SizedBox(height: 4),
          Text(value, style: Theme.of(context).textTheme.titleMedium),
        ],
      ),
    );
  }
}

class _ProbabilityBar extends StatelessWidget {
  const _ProbabilityBar({required this.label, required this.value});

  final String label;
  final double value;

  @override
  Widget build(BuildContext context) {
    final percent = (value * 100).clamp(0, 100).toStringAsFixed(2);

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [Text(label), Text('$percent%')],
        ),
        const SizedBox(height: 6),
        LinearProgressIndicator(value: value.clamp(0.0, 1.0), minHeight: 10),
      ],
    );
  }
}

class _WarningBox extends StatelessWidget {
  const _WarningBox({required this.message});

  final String message;

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(
        color: AppColors.warningBackground,
        borderRadius: BorderRadius.circular(8),
        border: Border.all(color: AppColors.warningBorder),
      ),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const Icon(Icons.warning_amber_rounded, color: AppColors.warningText),
          const SizedBox(width: 10),
          Expanded(
            child: Text(
              message,
              style: const TextStyle(color: AppColors.warningText),
            ),
          ),
        ],
      ),
    );
  }
}

class WaveformPreview extends StatefulWidget {
  const WaveformPreview({super.key, required this.values, this.isLive = false});

  final List<double> values;
  final bool isLive;

  @override
  State<WaveformPreview> createState() => _WaveformPreviewState();
}

class _WaveformPreviewState extends State<WaveformPreview>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 950),
    );
    if (widget.isLive) {
      _controller.repeat();
    }
  }

  @override
  void didUpdateWidget(covariant WaveformPreview oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.isLive && !_controller.isAnimating) {
      _controller.repeat();
    } else if (!widget.isLive && _controller.isAnimating) {
      _controller.stop();
    }
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final emptyText =
        widget.isLive
            ? 'Mulai berbicara, volume suara akan bergerak di sini.'
            : 'Preview waveform akan muncul setelah audio dipilih.';

    return Container(
      decoration: BoxDecoration(
        color: AppColors.surfaceRaised,
        borderRadius: BorderRadius.circular(8),
        border: Border.all(
          color:
              widget.isLive
                  ? AppColors.blue.withValues(alpha: 0.45)
                  : AppColors.border,
        ),
      ),
      child:
          widget.isLive
              ? AnimatedBuilder(
                animation: _controller,
                builder: (context, _) {
                  return CustomPaint(
                    painter: _LiveFrequencyPainter(
                      progress: _controller.value,
                      color: AppColors.blueSoft,
                    ),
                  );
                },
              )
              : widget.values.isEmpty
              ? Center(child: Text(emptyText, textAlign: TextAlign.center))
              : CustomPaint(
                painter: _WaveformPainter(
                  values: widget.values,
                  color: Theme.of(context).colorScheme.primary,
                ),
              ),
    );
  }
}

class _WaveformPainter extends CustomPainter {
  const _WaveformPainter({required this.values, required this.color});

  final List<double> values;
  final Color color;

  @override
  void paint(Canvas canvas, Size size) {
    final centerY = size.height / 2;
    final axisPaint =
        Paint()
          ..color = AppColors.border
          ..strokeWidth = 1;
    canvas.drawLine(Offset(0, centerY), Offset(size.width, centerY), axisPaint);

    if (values.isEmpty) {
      return;
    }

    final paint =
        Paint()
          ..color = color
          ..strokeWidth = max(2, size.width / values.length * 0.45)
          ..strokeCap = StrokeCap.round;
    final gap = size.width / values.length;

    for (var i = 0; i < values.length; i++) {
      final x = gap * i + gap / 2;
      final amplitude = values[i].abs() * centerY * 0.9;
      canvas.drawLine(
        Offset(x, centerY - amplitude),
        Offset(x, centerY + amplitude),
        paint,
      );
    }
  }

  @override
  bool shouldRepaint(covariant _WaveformPainter oldDelegate) {
    return oldDelegate.values != values || oldDelegate.color != color;
  }
}

class _LiveFrequencyPainter extends CustomPainter {
  const _LiveFrequencyPainter({required this.progress, required this.color});

  final double progress;
  final Color color;

  @override
  void paint(Canvas canvas, Size size) {
    final centerY = size.height / 2;
    final barCount = 34;
    final gap = size.width / barCount;
    final barWidth = max(3.0, gap * 0.48);
    final phase = progress * pi * 2;

    final backgroundPaint =
        Paint()
          ..color = AppColors.blue.withValues(alpha: 0.10)
          ..strokeWidth = 1;
    canvas.drawLine(
      Offset(0, centerY),
      Offset(size.width, centerY),
      backgroundPaint,
    );

    final glowPaint =
        Paint()
          ..color = color.withValues(alpha: 0.20)
          ..strokeWidth = barWidth * 2.4
          ..strokeCap = StrokeCap.round;
    final barPaint =
        Paint()
          ..shader = LinearGradient(
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
            colors: [
              AppColors.blueSoft.withValues(alpha: 0.95),
              AppColors.blue,
              AppColors.blueSoft.withValues(alpha: 0.95),
            ],
          ).createShader(Rect.fromLTWH(0, 0, size.width, size.height))
          ..strokeWidth = barWidth
          ..strokeCap = StrokeCap.round;

    for (var i = 0; i < barCount; i++) {
      final x = gap * i + gap / 2;
      final waveA = sin(phase + i * 0.45);
      final waveB = sin(phase * 1.7 - i * 0.23);
      final envelope = 0.52 + 0.48 * sin((i / barCount) * pi);
      final heightFactor =
          (0.34 + 0.30 * waveA.abs() + 0.20 * waveB.abs()) * envelope;
      final amplitude = max(8.0, centerY * heightFactor);

      canvas.drawLine(
        Offset(x, centerY - amplitude),
        Offset(x, centerY + amplitude),
        glowPaint,
      );
      canvas.drawLine(
        Offset(x, centerY - amplitude),
        Offset(x, centerY + amplitude),
        barPaint,
      );
    }
  }

  @override
  bool shouldRepaint(covariant _LiveFrequencyPainter oldDelegate) {
    return oldDelegate.progress != progress || oldDelegate.color != color;
  }
}
