import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc;
import 'package:get/get.dart' hide Response;
import 'package:permission_handler/permission_handler.dart';
import 'package:just_audio/just_audio.dart';

import '../../constant/box_name.dart';
import '../../constant/links.dart';
import '../../main.dart';
import '../../print.dart';
import '../../services/signaling_service.dart';
import '../../views/widgets/voice_call_bottom_sheet.dart';
import 'functions/crud.dart';

// EN: Enum representing the different states of a voice call.
// AR: تعداد يمثل الحالات المختلفة للمكالمة الصوتية.
enum VoiceCallState { idle, dialing, ringing, connecting, active, ended }

class VoiceCallController extends GetxController with WidgetsBindingObserver {
  // EN: Instance of the signaling service to manage WebSocket communication.
  // AR: مثيل لخدمة الإشارات لإدارة الاتصال عبر الـ WebSocket.
  final SignalingService _signaling = SignalingService();

  // --- Observable Variables (GetX) / المتغيرات التفاعلية ---

  // EN: Current state of the call.
  // AR: الحالة الحالية للمكالمة.
  var state = VoiceCallState.idle.obs;

  // EN: Unique identifier for the WebRTC session.
  // AR: المعرف الفريد لجلسة الاتصال.
  var sessionId = "".obs;

  // EN: ID of the current active ride.
  // AR: معرف الرحلة النشطة الحالية.
  var rideId = "".obs;

  // EN: Name of the other party (Driver/Passenger).
  // AR: اسم الطرف الآخر في المكالمة (سائق/راكب).
  var remoteName = "User".obs;

  // EN: Microphone mute status.
  // AR: حالة كتم الميكروفون.
  var isMuted = false.obs;

  // EN: Speakerphone status.
  // AR: حالة مكبر الصوت الخارجي.
  var isSpeakerOn = false.obs;

  // EN: Timer countdown variable, starts from 60 seconds.
  // AR: متغير العد التنازلي للمؤقت، يبدأ من 60 ثانية.
  var elapsedSeconds = 60.obs;

  // --- Core State Variables / متغيرات الحالة الأساسية ---

  // EN: Flag to determine if the current user initiated the call.
  // AR: مؤشر لتحديد ما إذا كان المستخدم الحالي هو من بدأ المكالمة.
  bool isCaller = false;

  // EN: ID of the current user.
  // AR: معرف المستخدم الحالي.
  String currentUserId = "";

  // --- WebRTC Internal Variables / متغيرات WebRTC الداخلية ---

  // EN: The main connection object between peers.
  // AR: كائن الاتصال الرئيسي بين الطرفين.
  rtc.RTCPeerConnection? _peerConnection;

  // EN: The local audio stream captured from the microphone.
  // AR: دفق الصوت المحلي الملتقط من الميكروفون.
  rtc.MediaStream? _localStream;

  // EN: Timer to enforce the 60-second call limit.
  // AR: مؤقت لفرض حد الـ 60 ثانية للمكالمة.
  Timer? _countdownTimer;

  // EN: Timer to hang up if the call is not answered within 30 seconds.
  // AR: مؤقت لإنهاء المكالمة إذا لم يتم الرد خلال 30 ثانية.
  Timer? _ringingTimeoutTimer;

  // EN: Flag to indicate if the peer connection is currently attempting ICE reconnection.
  // AR: مؤشر يوضح ما إذا كان الاتصال يحاول إعادة بناء مسارات الشبكة حالياً.
  bool _isReconnecting = false;
  Timer? _reconnectTimer;
  List<dynamic> _dynamicIceServers = [];

  AudioPlayer? _ringtonePlayer;

  void _startRingtone() async {
    try {
      _ringtonePlayer ??= AudioPlayer();
      await _ringtonePlayer!.setAsset('assets/start.wav');
      await _ringtonePlayer!.setLoopMode(LoopMode.one);
      _ringtonePlayer!.play();
    } catch (e) {
      Log.print("Error playing ringtone: $e");
    }
  }

  void _stopRingtone() {
    try {
      _ringtonePlayer?.stop();
    } catch (e) {
      Log.print("Error stopping ringtone: $e");
    }
  }

  @override
  void onInit() {
    super.onInit();
    // EN: Add lifecycle observer.
    // AR: إضافة مراقب لدورة حياة التطبيق.
    WidgetsBinding.instance.addObserver(this);

    // EN: Initialize WebSocket signaling listeners.
    // AR: تهيئة مستمعي إشارات الـ WebSocket.
    _initSignalingCallbacks();
  }

  // EN: Lifecycle hook: handle app switching background/foreground.
  // AR: معالجة انتقال التطبيق إلى الخلفية أو العودة للواجهة.
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    Log.print("VoiceCall: didChangeAppLifecycleState -> $state");
    if (state == AppLifecycleState.paused) {
      Log.print(
        "WARNING: App is in background. Microphone access might be suspended by the OS.",
      );
    } else if (state == AppLifecycleState.resumed) {
      Log.print("App resumed. Verifying WebRTC connection health.");
      if (this.state.value == VoiceCallState.active) {
        _ensureMicrophoneActive();
        _attemptIceRestart();
      }
    }
  }

  // EN: Registers all event listeners for the signaling server.
  // AR: تسجيل جميع مستمعي الأحداث لخادم الإشارات.
  void _initSignalingCallbacks() {
    // EN: Triggered when successfully connected to the signaling server.
    // AR: يُستدعى عند الاتصال بنجاح بخادم الإشارات.
    _signaling.onConnected = (iceServers) {
      Log.print("WebRTC Signaling Connected & Authenticated");
      _dynamicIceServers = iceServers;
    };

    // EN: Triggered when the WebSocket connection drops.
    // AR: يُستدعى عند انقطاع اتصال الـ WebSocket.
    _signaling.onDisconnected = (reason) {
      Log.print("WebRTC Signaling Disconnected: $reason");
      if (state.value != VoiceCallState.idle) {
        _endCallInternal("signaling_disconnected");
      }
    };

    // EN: Triggered when the remote user joins the room.
    // AR: يُستدعى عند انضمام الطرف الآخر إلى غرفة الاتصال.
    _signaling.onParticipantJoined = () async {
      Log.print("Remote participant joined signaling session");
      // EN: If we are the caller, initiate the WebRTC handshake by creating an Offer.
      // AR: إذا كنا نحن المتصل، نبدأ مصافحة WebRTC بإنشاء عرض (Offer).
      if (isCaller && state.value == VoiceCallState.dialing) {
        state.value = VoiceCallState.connecting;
        await _createOffer();
      }
    };

    // EN: Triggered when an SDP Offer is received from the remote peer.
    // AR: يُستدعى عند استلام عرض اتصال (Offer) من الطرف الآخر.
    _signaling.onOffer = (sdpMap) async {
      Log.print("Received WebRTC SDP Offer");
      if (!isCaller) {
        state.value = VoiceCallState.connecting;
        await _initializePeerConnection();

        // EN: Set the remote peer's settings.
        // AR: تعيين إعدادات الطرف الآخر.
        final description = rtc.RTCSessionDescription(
          sdpMap['sdp'],
          sdpMap['type'],
        );
        await _peerConnection!.setRemoteDescription(description);

        // EN: Respond with an Answer.
        // AR: الرد بإجابة (Answer).
        await _createAnswer();
      }
    };

    // EN: Triggered when an SDP Answer is received.
    // AR: يُستدعى عند استلام إجابة (Answer) من الطرف الآخر.
    _signaling.onAnswer = (sdpMap) async {
      Log.print("Received WebRTC SDP Answer");
      if (isCaller && _peerConnection != null) {
        final description = rtc.RTCSessionDescription(
          sdpMap['sdp'],
          sdpMap['type'],
        );
        await _peerConnection!.setRemoteDescription(description);
      }
    };

    // EN: Triggered when ICE candidates (Network routing info) are exchanged.
    // AR: يُستدعى عند تبادل مسارات الشبكة (ICE Candidates) لتأسيس الاتصال.
    _signaling.onIceCandidate = (candidateMap) async {
      Log.print("Received Remote ICE Candidate");
      if (_peerConnection != null) {
        final candidate = rtc.RTCIceCandidate(
          candidateMap['candidate'],
          candidateMap['sdpMid'],
          candidateMap['sdpMLineIndex'],
        );
        await _peerConnection!.addCandidate(candidate);
      }
    };

    // EN: Triggered when a hangup event is received from the server.
    // AR: يُستدعى عند استلام حدث إنهاء المكالمة من السيرفر.
    _signaling.onCallEnded = (reason) {
      Log.print("WebRTC Call Ended: $reason");
      _endCallInternal(reason);
    };
  }

  // --- CALL LIFECYCLE / دورة حياة المكالمة ---

  // EN: Initiates an outgoing call.
  // AR: يبدأ مكالمة صادرة.
  Future<void> startCall({
    required String rideIdVal,
    required String driverId,
    required String passengerId,
    required String remoteNameVal,
  }) async {
    if (state.value != VoiceCallState.idle) return;

    // EN: Setup call variables.
    // AR: إعداد متغيرات المكالمة.
    state.value = VoiceCallState.dialing;
    isCaller = true;
    currentUserId = passengerId;
    rideId.value = rideIdVal;
    remoteName.value = remoteNameVal;
    isMuted.value = false;
    isSpeakerOn.value = false;
    elapsedSeconds.value = 60;
    _isReconnecting = false;

    _showCallBottomSheet();
    HapticFeedback.vibrate();

    try {
      // 1. EN: Request Microphone Permission / AR: طلب صلاحية الميكروفون
      final permissionStatus = await Permission.microphone.request();
      if (!permissionStatus.isGranted) {
        _endCallInternal("permission_denied");
        Get.snackbar(
          "Error",
          "Microphone permission is required for voice calls".tr,
        );
        return;
      }

      // 2. EN: Call PHP Backend to create Node.js session & notify Driver via FCM.
      //    AR: استدعاء واجهة PHP لإنشاء الجلسة على Node.js وإشعار السائق عبر FCM.
      final response = await CRUD().post(
        link: "${AppLink.server}/ride/call/passenger/create_call_session.php",
        payload: {'ride_id': rideIdVal},
      );

      if (response == null ||
          response == 'failure' ||
          response['status'] != 'success') {
        _endCallInternal("session_creation_failed");
        Get.snackbar(
          "Error",
          "Failed to initiate call session. Please try again.".tr,
        );
        return;
      }

      final data = response['data'];
      sessionId.value = data['session_id'];

      // 3. EN: Connect to WebRTC signaling server / AR: الاتصال بخادم الإشارات
      await _signaling.connect(sessionId.value, currentUserId);

      // 4. EN: Initialize Local WebRTC Audio Stream / AR: تهيئة دفق الصوت المحلي
      await _initializeLocalStream();

      // 5. EN: Start Ringing Timeout Timer (30s max wait for driver to answer).
      //    AR: بدء مؤقت الرنين (أقصى انتظار 30 ثانية لرد السائق).
      _ringingTimeoutTimer = Timer(const Duration(seconds: 30), () {
        if (state.value == VoiceCallState.dialing) {
          _signaling.send("hangup", {"reason": "no_answer"});
          _endCallInternal("no_answer");
        }
      });
    } catch (e) {
      Log.print("Error starting WebRTC call: $e");
      _endCallInternal("error");
    }
  }

  // EN: Handles incoming call requests via FCM/Socket.
  // AR: معالجة طلبات المكالمات الواردة.
  Future<void> receiveCall({
    required String sessionIdVal,
    required String remoteNameVal,
    required String rideIdVal,
  }) async {
    // EN: If already in a call, send busy signal.
    // AR: إذا كان في مكالمة بالفعل، إرسال إشارة مشغول.
    if (state.value != VoiceCallState.idle) {
      _signaling.send("hangup", {"reason": "busy"});
      return;
    }

    state.value = VoiceCallState.ringing;
    isCaller = false;
    currentUserId = box.read(BoxName.passengerID).toString();
    sessionId.value = sessionIdVal;
    rideId.value = rideIdVal;
    remoteName.value = remoteNameVal;
    isMuted.value = false;
    isSpeakerOn.value = false;
    elapsedSeconds.value = 60;
    _isReconnecting = false;

    _showCallBottomSheet();
    _startRingtone();
    HapticFeedback.vibrate();

    // EN: Max 30s ringing timeout for receiver before auto-decline.
    // AR: أقصى مدة للرنين 30 ثانية قبل الرفض التلقائي.
    _ringingTimeoutTimer = Timer(const Duration(seconds: 30), () {
      if (state.value == VoiceCallState.ringing) {
        declineCall();
      }
    });
  }

  // EN: Accepts the incoming call.
  // AR: قبول المكالمة الواردة.
  Future<void> acceptCall() async {
    if (state.value != VoiceCallState.ringing) return;

    _ringingTimeoutTimer?.cancel();
    _stopRingtone();
    state.value = VoiceCallState.connecting;

    try {
      // EN: Check Mic permissions / AR: التحقق من صلاحيات الميكروفون
      final permissionStatus = await Permission.microphone.request();
      if (!permissionStatus.isGranted) {
        declineCall();
        Get.snackbar(
          "Error",
          "Microphone permission is required for voice calls".tr,
        );
        return;
      }

      await _signaling.connect(sessionId.value, currentUserId);
      await _initializeLocalStream();

      // EN: Notify caller we accepted / AR: إشعار المتصل بأننا قبلنا المكالمة
      _signaling.send("join", {});
    } catch (e) {
      Log.print("Error accepting call: $e");
      declineCall();
    }
  }

  // EN: Declines an incoming call.
  // AR: رفض المكالمة الواردة.
  void declineCall() {
    _ringingTimeoutTimer?.cancel();
    _stopRingtone();
    _signaling.send("hangup", {"reason": "declined"});
    _endCallInternal("declined");
  }

  // EN: Ends an active or dialing call.
  // AR: إنهاء المكالمة النشطة أو الجاري الاتصال بها.
  void hangup() {
    _signaling.send("hangup", {"reason": "normal"});
    _endCallInternal("hangup");
  }

  // --- WEBRTC CORE HELPERS / دوال WebRTC الأساسية ---

  // EN: Captures the audio from the microphone with optimization constraints.
  // AR: التقاط الصوت من الميكروفون مع قيود تحسين الجودة (إلغاء الصدى والضوضاء).
  Future<void> _initializeLocalStream() async {
    final Map<String, dynamic> mediaConstraints = {
      'audio': {
        'echoCancellation': true,
        'noiseSuppression': true,
        'autoGainControl': true,
      },
      'video': false, // EN: Audio only / AR: صوت فقط
    };

    _localStream = await rtc.navigator.mediaDevices.getUserMedia(
      mediaConstraints,
    );
    rtc.Helper.setSpeakerphoneOn(isSpeakerOn.value);
  }

  // EN: Verifies local microphone stream health on app resume and recreates/replaces track if suspended.
  // AR: التحقق من سلامة مسار الميكروفون المحلي عند استئناف التطبيق وإعادة إنشائه إذا تم تعليقه.
  Future<void> _ensureMicrophoneActive() async {
    if (_localStream == null || _peerConnection == null) return;

    bool needsRecreation = false;
    if (_localStream!.active == false) {
      needsRecreation = true;
    } else {
      for (var track in _localStream!.getAudioTracks()) {
        if (!track.enabled && !isMuted.value) {
          needsRecreation = true;
          break;
        }
      }
    }

    if (needsRecreation) {
      Log.print(
        "Local audio track ended or disabled. Recreating local stream...",
      );
      try {
        _localStream?.getTracks().forEach((track) => track.stop());
        _localStream?.dispose();
        _localStream = null;

        await _initializeLocalStream();

        final senders = await _peerConnection!.getSenders();
        for (var sender in senders) {
          final track = sender.track;
          if (track != null && track.kind == 'audio') {
            final newTracks = _localStream?.getAudioTracks();
            if (newTracks != null && newTracks.isNotEmpty) {
              await sender.replaceTrack(newTracks.first);
              Log.print(
                "Replaced suspended/ended audio track with a new active one.",
              );
            }
            break;
          }
        }
      } catch (e) {
        Log.print("Error recreating local stream on resume: $e");
      }
    } else {
      _localStream!.getAudioTracks().forEach((track) {
        track.enabled = !isMuted.value;
      });
    }
  }

  // EN: Creates the peer connection object and sets up ICE servers (STUN/TURN).
  // AR: إنشاء كائن الاتصال المباشر وإعداد خوادم STUN/TURN لاختراق الجدران النارية.
  Future<void> _initializePeerConnection() async {
    if (_peerConnection != null) return;

    final List<Map<String, dynamic>> iceServers = [];
    if (_dynamicIceServers.isNotEmpty) {
      for (var server in _dynamicIceServers) {
        if (server is Map) {
          iceServers.add({
            "urls": server["urls"] ?? server["url"],
            if (server["username"] != null) "username": server["username"],
            if (server["credential"] != null)
              "credential": server["credential"],
          });
        }
      }
    } else {
      // EN: Fallback STUN servers / AR: خوادم STUN الاحتياطية
      iceServers.addAll([
        {"urls": "stun:stun.l.google.com:19302"},
        {"urls": "stun:stun1.l.google.com:19302"},
      ]);
    }

    final Map<String, dynamic> configuration = {"iceServers": iceServers};

    _peerConnection = await rtc.createPeerConnection(configuration);

    // EN: Gather local network routing info and send to remote peer.
    // AR: جمع بيانات مسارات الشبكة المحلية وإرسالها للطرف الآخر.
    _peerConnection!.onIceCandidate = (candidate) {
      if (candidate.candidate != null) {
        _signaling.send("ice_candidate", {
          "candidate": {
            "candidate": candidate.candidate,
            "sdpMid": candidate.sdpMid,
            "sdpMLineIndex": candidate.sdpMLineIndex,
          },
        });
      }
    };

    // EN: Monitor connection status changes and handle disconnections.
    // AR: مراقبة تغيرات حالة الاتصال ومعالجة انقطاع الشبكة.
    _peerConnection!.onConnectionState = (connState) {
      Log.print("RTCPeerConnectionState: $connState");
      if (connState ==
          rtc.RTCPeerConnectionState.RTCPeerConnectionStateConnected) {
        _onCallConnected();
      } else if (connState ==
              rtc.RTCPeerConnectionState.RTCPeerConnectionStateFailed ||
          connState ==
              rtc.RTCPeerConnectionState.RTCPeerConnectionStateDisconnected) {
        _handleIceConnectionFailure();
      }
    };

    // EN: Add local audio stream to the connection to send it to the other peer.
    // AR: إضافة دفق الصوت المحلي للاتصال لإرساله للطرف الآخر.
    if (_localStream != null) {
      _localStream!.getTracks().forEach((track) {
        _peerConnection!.addTrack(track, _localStream!);
      });
    }
  }

  // EN: Attempts an ICE restart to reconnect the WebRTC session when disconnections occur.
  // AR: محاولة إعادة تأسيس الاتصال (ICE Restart) في حالة انقطاع الشبكة.
  void _handleIceConnectionFailure() {
    if (_isReconnecting) return;
    _isReconnecting = true;
    Log.print(
      "ICE connection dropped. Attempting ICE Restart reconnection for 5s...",
    );

    if (isCaller) {
      _attemptIceRestart();
    }

    _reconnectTimer?.cancel();
    _reconnectTimer = Timer(const Duration(seconds: 5), () {
      if (state.value == VoiceCallState.active &&
          _peerConnection?.connectionState !=
              rtc.RTCPeerConnectionState.RTCPeerConnectionStateConnected) {
        Log.print("ICE reconnection timed out. Hanging up.");
        _endCallInternal("connection_lost");
      } else {
        _isReconnecting = false;
        Log.print("ICE Reconnection succeeded!");
      }
    });
  }

  // EN: Initiates ICE Restart SDP exchange.
  // AR: بدء تبادل حزم SDP لإعادة بناء مسارات الاتصال.
  Future<void> _attemptIceRestart() async {
    if (_peerConnection == null || !isCaller) return;
    try {
      Log.print("Caller initiating WebRTC ICE Restart...");
      final constraints = {
        'mandatory': {
          'OfferToReceiveAudio': true,
          'OfferToReceiveVideo': false,
        },
        'optional': [
          {'IceRestart': true},
        ],
      };
      final offer = await _peerConnection!.createOffer(constraints);
      await _peerConnection!.setLocalDescription(offer);
      _signaling.send("offer", {
        "sdp": {"sdp": offer.sdp, "type": offer.type},
      });
    } catch (e) {
      Log.print("Error initiating WebRTC ICE Restart: $e");
    }
  }

  // EN: Generates an SDP Offer to initialize the connection.
  // AR: إنشاء عرض (Offer) لبدء الاتصال وتحديد قدرات الجهاز.
  Future<void> _createOffer() async {
    await _initializePeerConnection();

    final constraints = {
      'mandatory': {'OfferToReceiveAudio': true, 'OfferToReceiveVideo': false},
      'optional': [],
    };

    final offer = await _peerConnection!.createOffer(constraints);
    await _peerConnection!.setLocalDescription(offer);

    _signaling.send("offer", {
      "sdp": {"sdp": offer.sdp, "type": offer.type},
    });
  }

  // EN: Generates an SDP Answer in response to an Offer.
  // AR: الرد بإنشاء إجابة (Answer) بناءً على العرض المستلم.
  Future<void> _createAnswer() async {
    final constraints = {
      'mandatory': {'OfferToReceiveAudio': true, 'OfferToReceiveVideo': false},
      'optional': [],
    };

    final answer = await _peerConnection!.createAnswer(constraints);
    await _peerConnection!.setLocalDescription(answer);

    _signaling.send("answer", {
      "sdp": {"sdp": answer.sdp, "type": answer.type},
    });
  }

  // EN: Triggered when connection is fully established. Starts the 60s timer.
  // AR: يُستدعى عند تأسيس الاتصال بنجاح، ويقوم ببدء مؤقت الـ 60 ثانية.
  void _onCallConnected() {
    _ringingTimeoutTimer?.cancel();
    _reconnectTimer?.cancel();
    _isReconnecting = false;

    if (state.value != VoiceCallState.active) {
      state.value = VoiceCallState.active;
      HapticFeedback.vibrate();

      // EN: Start 120s countdown timer / AR: بدء العد التنازلي لمدة 120 ثانية
      _countdownTimer?.cancel();
      elapsedSeconds.value = 120;
      _countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
        if (elapsedSeconds.value > 1) {
          elapsedSeconds.value--;
        } else {
          elapsedSeconds.value = 0;
          _countdownTimer?.cancel();
          // EN: Force hangup when timer reaches 0 / AR: إغلاق إجباري عند وصول المؤقت لصفر
          hangup();
        }
      });
    }
  }

  // EN: Internal cleanup function. Closes all connections and streams.
  // AR: دالة التنظيف الداخلية. تقوم بإغلاق جميع الاتصالات وتفريغ الذاكرة.
  void _endCallInternal(String reason) {
    _countdownTimer?.cancel();
    _ringingTimeoutTimer?.cancel();
    _reconnectTimer?.cancel();
    _stopRingtone();

    state.value = VoiceCallState.ended;

    // EN: Close WebRTC connection / AR: إغلاق اتصال WebRTC
    _peerConnection?.close();
    _peerConnection = null;

    // EN: Stop mic capture / AR: إيقاف التقاط الميكروفون
    _localStream?.getTracks().forEach((track) => track.stop());
    _localStream?.dispose();
    _localStream = null;

    // EN: Disconnect WebSockets / AR: إغلاق اتصال الـ WebSockets
    _signaling.disconnect();

    // EN: Close UI BottomSheet after delay / AR: إغلاق واجهة المكالمة بعد فترة زمنية قصيرة
    Future.delayed(const Duration(milliseconds: 1500), () {
      if (state.value == VoiceCallState.ended) {
        state.value = VoiceCallState.idle;
        Get.back();
      }
    });
  }

  // --- ACTIONS (UI Controls) / إجراءات الواجهة ---

  // EN: Toggles microphone mute state.
  // AR: تبديل حالة كتم الميكروفون.
  void toggleMute() {
    isMuted.value = !isMuted.value;
    _localStream?.getAudioTracks().forEach((track) {
      track.enabled = !isMuted.value;
    });
  }

  // EN: Toggles loudspeaker mode.
  // AR: تبديل حالة مكبر الصوت الخارجي.
  void toggleSpeaker() {
    isSpeakerOn.value = !isSpeakerOn.value;
    rtc.Helper.setSpeakerphoneOn(isSpeakerOn.value);
  }

  // EN: Displays the call UI overlay.
  // AR: إظهار نافذة المكالمة السفلية.
  void _showCallBottomSheet() {
    Get.bottomSheet(
      const VoiceCallBottomSheet(),
      isScrollControlled: true,
      enableDrag: false,
      isDismissible: false,
    );
  }

  // EN: Lifecycle hook: clean up resources when controller is destroyed.
  // AR: دورة الحياة: تفريغ الذاكرة وإغلاق الموارد عند تدمير المتحكم.
  @override
  void onClose() {
    WidgetsBinding.instance.removeObserver(this);
    _countdownTimer?.cancel();
    _ringingTimeoutTimer?.cancel();
    _reconnectTimer?.cancel();
    _stopRingtone();
    _ringtonePlayer?.dispose();
    _peerConnection?.close();
    _localStream?.dispose();
    _signaling.disconnect();
    super.onClose();
  }
}
