import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:record/record.dart';
import 'package:audioplayers/audioplayers.dart';
import 'package:path_provider/path_provider.dart';
import '../../core/theme/app_colors.dart';
import '../../core/theme/app_typography.dart';
import '../../services/app_provider.dart';
import '../../services/api_service.dart';
import '../../widgets/pulsing_orb.dart';
import '../../widgets/waveform_visualizer.dart';
import '../../widgets/glass_card.dart';
import '../../widgets/upgrade_prompt.dart';

/// Voice call using REST combined endpoint for low-latency turns.
/// Flow: Record WAV -> POST /voice/turn/ -> Whisper+GPT+TTS -> Play MP3 -> loop
class VoiceCallScreen extends StatefulWidget {
  const VoiceCallScreen({super.key});

  @override
  State<VoiceCallScreen> createState() => _VoiceCallScreenState();
}

class _VoiceCallScreenState extends State<VoiceCallScreen>
    with SingleTickerProviderStateMixin {
  bool _active = false;
  bool _muted = false;
  bool _recording = false;
  bool _processing = false;
  bool _playing = false;
  String _status = 'Tap to start call';
  double _amplitude = 0.0;
  String? _conversationId;
  final List<Map<String, String>> _transcript = [];

  String _selectedVoice = 'nova';
  static const _voices = {
    'nova': 'Nova (Female)',
    'echo': 'Echo (Male)',
    'alloy': 'Alloy (Neutral)',
    'shimmer': 'Shimmer (Female)',
    'onyx': 'Onyx (Male)',
    'fable': 'Fable (Neutral)',
  };

  late AnimationController _fadeCtrl;
  final AudioRecorder _recorder = AudioRecorder();
  final AudioPlayer _player = AudioPlayer();
  Timer? _silenceTimer;
  Timer? _ampTimer;
  String? _currentRecordPath;

  static const _silenceDuration = Duration(milliseconds: 1200);
  static const _ampPollInterval = Duration(milliseconds: 120);
  static const _silenceAmpThreshold = -35.0;

  @override
  void initState() {
    super.initState();
    _fadeCtrl = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 400),
    );
    _player.onPlayerComplete.listen((_) => _onPlaybackDone());
  }

  @override
  void dispose() {
    _cleanup();
    _fadeCtrl.dispose();
    _recorder.dispose();
    _player.dispose();
    _silenceTimer?.cancel();
    _ampTimer?.cancel();
    super.dispose();
  }

  Future<void> _toggleCall() async {
    if (_active) {
      await _endCall();
    } else {
      await _startCall();
    }
  }

  Future<void> _startCall() async {
    final micStatus = await Permission.microphone.request();
    if (!micStatus.isGranted) {
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: const Text('Microphone permission required'),
            backgroundColor: AppColors.error,
            behavior: SnackBarBehavior.floating,
            shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
          ),
        );
      }
      return;
    }

    setState(() {
      _active = true;
      _status = 'Starting call...';
      _amplitude = 0.15;
      _transcript.clear();
    });
    _fadeCtrl.forward();

    try {
      final provider = context.read<AppProvider>();
      _conversationId = await provider.createConversation(title: 'Voice Call');
      setState(() => _status = 'Listening...');
      _startRecording();
    } on ApiException catch (e) {
      if (e.isLimitError) {
        setState(() { _active = false; _status = 'Tap to start call'; _amplitude = 0.0; });
        _fadeCtrl.reverse();
        if (mounted) showUpgradePrompt(context, feature: 'voice calls');
      } else {
        setState(() => _status = 'Error: ${e.message}');
        await Future.delayed(const Duration(seconds: 2));
        if (mounted) _endCall();
      }
    } catch (e) {
      debugPrint('Start call error: $e');
      setState(() => _status = 'Failed to connect');
      await Future.delayed(const Duration(seconds: 2));
      if (mounted) _endCall();
    }
  }

  Future<void> _endCall() async {
    if (!_active) return;
    setState(() { _status = 'Call ended'; _amplitude = 0.05; });
    _cleanup();
    _fadeCtrl.reverse();
    if (mounted) {
      setState(() {
        _active = false; _recording = false; _processing = false;
        _playing = false; _amplitude = 0.0; _conversationId = null;
      });
      Future.delayed(const Duration(seconds: 2), () {
        if (mounted) setState(() => _status = 'Tap to start call');
      });
    }
  }

  void _cleanup() {
    _silenceTimer?.cancel();
    _silenceTimer = null;
    _ampTimer?.cancel();
    _ampTimer = null;
    try { _recorder.stop(); } catch (_) {}
    try { _player.stop(); } catch (_) {}
  }

  Future<void> _startRecording() async {
    if (_recording || _muted || !_active || _processing || _playing) return;
    if (!await _recorder.hasPermission()) return;

    final dir = await getTemporaryDirectory();
    _currentRecordPath = '${dir.path}/voice_${DateTime.now().millisecondsSinceEpoch}.wav';

    try {
      await _recorder.start(
        const RecordConfig(
          encoder: AudioEncoder.wav,
          sampleRate: 16000,
          numChannels: 1,
          bitRate: 256000,
        ),
        path: _currentRecordPath!,
      );
      setState(() { _recording = true; _status = 'Listening...'; _amplitude = 0.3; });
      _startSilenceDetection();
    } catch (e) {
      debugPrint('Record start error: $e');
      setState(() => _status = 'Mic error');
    }
  }

  void _startSilenceDetection() {
    _silenceTimer?.cancel();
    _silenceTimer = null;
    _ampTimer?.cancel();

    _ampTimer = Timer.periodic(_ampPollInterval, (_) async {
      if (!_recording || !_active) return;
      try {
        final amp = await _recorder.getAmplitude();
        final db = amp.current;
        if (mounted) {
          final visual = ((db + 55) / 55).clamp(0.1, 0.8);
          setState(() => _amplitude = visual);
        }

        if (db < _silenceAmpThreshold) {
          _silenceTimer ??= Timer(_silenceDuration, _onSilenceDetected);
        } else {
          _silenceTimer?.cancel();
          _silenceTimer = null;
        }
      } catch (_) {}
    });
  }

  void _onSilenceDetected() {
    if (!_recording || !_active) return;
    _stopAndSend();
  }

  Future<void> _stopAndSend() async {
    if (!_recording) return;
    _silenceTimer?.cancel();
    _silenceTimer = null;
    _ampTimer?.cancel();

    try {
      final path = await _recorder.stop();
      setState(() { _recording = false; _amplitude = 0.15; });

      final filePath = path ?? _currentRecordPath;
      if (filePath == null) {
        _startRecording();
        return;
      }

      final file = File(filePath);
      if (!await file.exists() || await file.length() < 1024) {
        _startRecording();
        return;
      }

      await _processTurn(filePath);
    } catch (e) {
      debugPrint('Stop recording error: $e');
      if (_active) _startRecording();
    }
  }

  Future<void> _processTurn(String audioPath) async {
    if (!_active || _conversationId == null) return;

    setState(() { _processing = true; _status = 'Thinking...'; _amplitude = 0.2; });

    try {
      final provider = context.read<AppProvider>();
      final result = await provider.api.voiceTurn(
        filePath: audioPath,
        conversationId: _conversationId!,
        voice: _selectedVoice,
      );

      final userText = result['userText'] as String? ?? '';
      final aiText = result['aiText'] as String? ?? '';
      final audioBytes = result['audioBytes'];

      if (userText.isNotEmpty) {
        setState(() => _transcript.add({'role': 'user', 'text': userText}));
      }
      if (aiText.isNotEmpty) {
        setState(() => _transcript.add({'role': 'assistant', 'text': aiText}));
      }

      if (audioBytes != null && audioBytes is List<int> && audioBytes.isNotEmpty) {
        await _playResponse(Uint8List.fromList(audioBytes));
      } else {
        setState(() { _processing = false; _status = 'Listening...'; });
        _startRecording();
      }
    } on ApiException catch (e) {
      setState(() { _processing = false; });
      if (e.isLimitError) {
        if (mounted) showUpgradePrompt(context, feature: 'voice calls');
        _endCall();
      } else {
        setState(() => _status = 'Error, retrying...');
        await Future.delayed(const Duration(seconds: 1));
        if (_active) _startRecording();
      }
    } catch (e) {
      debugPrint('Voice turn error: $e');
      setState(() { _processing = false; _status = 'Error, retrying...'; });
      await Future.delayed(const Duration(seconds: 1));
      if (_active) _startRecording();
    }

    try { File(audioPath).deleteSync(); } catch (_) {}
  }

  Future<void> _playResponse(Uint8List audioBytes) async {
    setState(() { _processing = false; _playing = true; _status = 'AI Speaking...'; _amplitude = 0.5; });

    try {
      final dir = await getTemporaryDirectory();
      final file = File('${dir.path}/ai_resp_${DateTime.now().millisecondsSinceEpoch}.mp3');
      await file.writeAsBytes(audioBytes);
      await _player.play(DeviceFileSource(file.path));
    } catch (e) {
      debugPrint('Playback error: $e');
      _onPlaybackDone();
    }
  }

  void _onPlaybackDone() {
    if (!mounted) return;
    setState(() { _playing = false; _amplitude = 0.3; });
    if (_active && !_muted) {
      setState(() => _status = 'Listening...');
      _startRecording();
    }
  }

  void _toggleMute() {
    setState(() => _muted = !_muted);
    if (_muted) {
      _silenceTimer?.cancel();
      _ampTimer?.cancel();
      try { _recorder.stop(); } catch (_) {}
      setState(() { _recording = false; _status = 'Muted'; _amplitude = 0.1; });
    } else if (_active && !_playing && !_processing) {
      _startRecording();
    }
  }

  void _showVoiceSelector() {
    showModalBottomSheet(
      context: context,
      backgroundColor: AppColors.navy,
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
      ),
      builder: (ctx) => SafeArea(
        child: Padding(
          padding: const EdgeInsets.all(20),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text('Select AI Voice', style: AppTypography.headingMd.copyWith(color: Colors.white)),
              const SizedBox(height: 16),
              ..._voices.entries.map((e) => ListTile(
                contentPadding: EdgeInsets.zero,
                leading: Icon(
                  e.key == _selectedVoice ? Icons.radio_button_checked : Icons.radio_button_off,
                  color: e.key == _selectedVoice ? AppColors.royalBlue : AppColors.textMuted,
                ),
                title: Text(e.value, style: AppTypography.bodyMd.copyWith(color: Colors.white)),
                onTap: () {
                  setState(() => _selectedVoice = e.key);
                  Navigator.pop(ctx);
                },
              )),
            ],
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: AppColors.navy,
      body: SafeArea(
        child: Column(
          children: [
            Padding(
              padding: const EdgeInsets.fromLTRB(8, 8, 8, 0),
              child: Row(
                children: [
                  IconButton(
                    icon: const Icon(Icons.arrow_back_rounded, color: Colors.white),
                    onPressed: () {
                      if (_active) _endCall();
                      Navigator.of(context).pop();
                    },
                  ),
                  const Spacer(),
                  Text('Voice Call', style: AppTypography.headingMd.copyWith(color: Colors.white)),
                  const Spacer(),
                  const SizedBox(width: 48),
                ],
              ),
            ),

            const Spacer(flex: 2),

            if (!_active)
              Padding(
                padding: const EdgeInsets.only(bottom: 24),
                child: GestureDetector(
                  onTap: _showVoiceSelector,
                  child: Container(
                    padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
                    decoration: BoxDecoration(
                      color: AppColors.glassSurface,
                      borderRadius: BorderRadius.circular(30),
                      border: Border.all(color: AppColors.glassBorder),
                    ),
                    child: Row(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        const Icon(Icons.record_voice_over_rounded, color: AppColors.royalBlue, size: 20),
                        const SizedBox(width: 10),
                        Text(_voices[_selectedVoice] ?? 'Nova',
                            style: AppTypography.bodyMd.copyWith(color: Colors.white)),
                        const SizedBox(width: 6),
                        const Icon(Icons.arrow_drop_down_rounded, color: AppColors.textMuted, size: 20),
                      ],
                    ),
                  ),
                ),
              ),

            GestureDetector(
              onTap: _toggleCall,
              child: PulsingOrb(
                size: 240,
                amplitude: _amplitude,
                active: _active,
              ),
            ),

            const SizedBox(height: 32),

            Text(
              _status,
              style: AppTypography.headingLg.copyWith(
                color: _active ? AppColors.gold : AppColors.textMuted,
              ),
              textAlign: TextAlign.center,
            ),

            const SizedBox(height: 8),

            if (_active)
              Text(
                _recording ? 'Listening...' : _playing ? 'AI Speaking...' : _processing ? 'Thinking...' : '',
                style: AppTypography.bodyMd.copyWith(color: AppColors.textSecondary),
              ),

            const SizedBox(height: 32),

            if (_active)
              FadeTransition(
                opacity: CurvedAnimation(parent: _fadeCtrl, curve: Curves.easeOut),
                child: WaveformVisualizer(
                  width: MediaQuery.of(context).size.width * 0.8,
                  height: 60,
                  amplitude: _amplitude,
                  active: _active,
                ),
              ),

            const Spacer(flex: 1),

            if (_transcript.isNotEmpty)
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 20),
                child: GlassCard(
                  padding: const EdgeInsets.all(16),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text('Transcript', style: AppTypography.bodySm.copyWith(color: AppColors.textMuted)),
                      const SizedBox(height: 8),
                      Text(
                        '${_transcript.last['role'] == 'user' ? 'You' : 'AI'}: ${_transcript.last['text']}',
                        style: AppTypography.bodyMd.copyWith(color: Colors.white),
                        maxLines: 3,
                        overflow: TextOverflow.ellipsis,
                      ),
                    ],
                  ),
                ),
              ),

            const Spacer(flex: 1),

            Padding(
              padding: const EdgeInsets.fromLTRB(40, 0, 40, 32),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  _ControlButton(
                    icon: _muted ? Icons.mic_off_rounded : Icons.mic_rounded,
                    label: _muted ? 'Unmute' : 'Mute',
                    active: _muted,
                    onTap: _toggleMute,
                  ),
                  GestureDetector(
                    onTap: _toggleCall,
                    child: AnimatedContainer(
                      duration: const Duration(milliseconds: 300),
                      width: 72,
                      height: 72,
                      decoration: BoxDecoration(
                        shape: BoxShape.circle,
                        gradient: LinearGradient(
                          colors: _active
                              ? [Colors.red, Colors.red.withValues(alpha: 0.7)]
                              : [AppColors.royalBlue, AppColors.royalBlue.withValues(alpha: 0.7)],
                        ),
                        boxShadow: [
                          BoxShadow(
                            color: (_active ? Colors.red : AppColors.royalBlue).withValues(alpha: 0.5),
                            blurRadius: 30,
                            spreadRadius: 5,
                          ),
                        ],
                      ),
                      child: Icon(
                        _active ? Icons.call_end_rounded : Icons.phone_rounded,
                        color: Colors.white,
                        size: 32,
                      ),
                    ),
                  ),
                  _ControlButton(
                    icon: Icons.speaker_phone_rounded,
                    label: 'Speaker',
                    active: false,
                    onTap: () {},
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class _ControlButton extends StatelessWidget {
  final IconData icon;
  final String label;
  final bool active;
  final VoidCallback onTap;

  const _ControlButton({
    required this.icon,
    required this.label,
    required this.active,
    required this.onTap,
  });

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Container(
            width: 52,
            height: 52,
            decoration: BoxDecoration(
              shape: BoxShape.circle,
              color: active ? Colors.white.withValues(alpha: 0.15) : AppColors.glassSurface,
              border: Border.all(color: AppColors.glassBorder),
            ),
            child: Icon(icon, color: active ? Colors.white : AppColors.textMuted, size: 24),
          ),
          const SizedBox(height: 6),
          Text(label, style: AppTypography.bodySm.copyWith(color: AppColors.textMuted)),
        ],
      ),
    );
  }
}
