(function () {
    const cfg = window.USHS_CALL;
    if (!cfg) return;

    const startBtn = document.getElementById('startBtn');
    const muteBtn = document.getElementById('muteBtn');
    const endBtn = document.getElementById('endBtn');
    const statusEl = document.getElementById('callStatus');
    const remoteAudio = document.getElementById('remoteAudio');
    const localAudio = document.getElementById('localAudio');
    const notesForm = document.getElementById('notesForm');
    const notesInput = document.getElementById('callNotes');

    let socket = null;
    let pc = null;
    let localStream = null;
    let startedAt = null;
    let isMuted = false;
    let hasJoined = false;
    let remoteDescriptionSet = false;
    let pendingIceCandidates = [];
    let remoteStream = null;
    let recordAudioContext = null;
    let recordDestination = null;
    let recordSources = [];
    let mediaRecorder = null;
    let recordedChunks = [];
    let isRecording = false;
    let recordingUploaded = false;
    let startNotified = false;
    const recordingStatus = document.getElementById('recordingStatus');

    const iceServers = {
        iceServers: [
            { urls: 'stun:stun.l.google.com:19302' }
            // Production recommendation:
            // Add a TURN server here for more reliable calls across strict networks.
            // Example:
            // { urls: 'turn:your-turn-domain.com:3478', username: 'user', credential: 'pass' }
        ]
    };

    function setStatus(text) {
        if (statusEl) statusEl.textContent = text;
    }

    function setRecordingStatus(text, active) {
        if (!recordingStatus) return;
        recordingStatus.classList.toggle('idle', !active);
        recordingStatus.innerHTML = active
            ? '<span class="recording-dot"></span>' + text
            : '<i class="fas fa-circle-info"></i> ' + text;
    }

    function isCsrCall() {
        return cfg.isCsr === true || cfg.participantType === 'csr';
    }

    function addStreamToRecording(stream) {
        if (!recordDestination || !recordAudioContext || !stream) return;
        try {
            const source = recordAudioContext.createMediaStreamSource(stream);
            source.connect(recordDestination);
            recordSources.push(source);
        } catch (err) {
            console.warn('Unable to add stream to recording mix:', err);
        }
    }

    async function startAutoRecording() {
        if (!isCsrCall() || isRecording || recordingUploaded || !window.MediaRecorder || !localStream) return;

        try {
            const AudioContextClass = window.AudioContext || window.webkitAudioContext;
            if (!AudioContextClass) throw new Error('AudioContext is not supported.');

            recordAudioContext = new AudioContextClass();
            recordDestination = recordAudioContext.createMediaStreamDestination();
            recordedChunks = [];

            addStreamToRecording(localStream);
            if (remoteStream) addStreamToRecording(remoteStream);

            let mimeType = '';
            if (MediaRecorder.isTypeSupported && MediaRecorder.isTypeSupported('audio/webm;codecs=opus')) {
                mimeType = 'audio/webm;codecs=opus';
            } else if (MediaRecorder.isTypeSupported && MediaRecorder.isTypeSupported('audio/webm')) {
                mimeType = 'audio/webm';
            }

            mediaRecorder = mimeType
                ? new MediaRecorder(recordDestination.stream, { mimeType })
                : new MediaRecorder(recordDestination.stream);

            mediaRecorder.ondataavailable = function (event) {
                if (event.data && event.data.size > 0) recordedChunks.push(event.data);
            };

            mediaRecorder.start(1000);
            isRecording = true;
            setRecordingStatus('Recording call automatically...', true);
        } catch (err) {
            console.warn('Automatic recording failed:', err);
            setRecordingStatus('Automatic recording unavailable in this browser.', false);
        }
    }

    async function stopAndUploadRecording() {
        if (!isCsrCall() || recordingUploaded) return;

        if (!mediaRecorder || !isRecording) return;

        await new Promise(function (resolve) {
            try {
                mediaRecorder.onstop = resolve;
                if (mediaRecorder.state !== 'inactive') {
                    mediaRecorder.stop();
                } else {
                    resolve();
                }
            } catch (err) {
                resolve();
            }
        });

        isRecording = false;
        setRecordingStatus('Saving call recording...', false);

        if (recordAudioContext) {
            try { await recordAudioContext.close(); } catch (err) {}
        }

        recordAudioContext = null;
        recordDestination = null;
        recordSources = [];

        if (!recordedChunks.length) {
            setRecordingStatus('No recording captured.', false);
            return;
        }

        const blob = new Blob(recordedChunks, { type: recordedChunks[0].type || 'audio/webm' });
        const form = new FormData();
        form.append('room_token', cfg.roomToken);
        form.append('call_id', String(cfg.callId || ''));
        form.append('recording', blob, 'call_' + (cfg.callId || 'webrtc') + '_' + Date.now() + '.webm');

        try {
            const res = await fetch(cfg.apiBase + '/upload_recording.php', {
                method: 'POST',
                body: form,
                credentials: 'same-origin'
            });
            const data = await res.json().catch(() => ({}));
            recordingUploaded = !!data.ok;
            setRecordingStatus(data.ok ? 'Recording saved.' : (data.error || 'Recording upload failed.'), false);
        } catch (err) {
            setRecordingStatus('Recording upload failed.', false);
        }
    }

    async function notifyCallStarted() {
        if (!isCsrCall() || startNotified) return;
        startNotified = true;

        const form = new FormData();
        form.append('room_token', cfg.roomToken);
        form.append('call_id', String(cfg.callId || ''));

        try {
            await fetch(cfg.apiBase + '/start_call.php', {
                method: 'POST',
                body: form,
                credentials: 'same-origin'
            });
        } catch (err) {}
    }

    function durationSeconds() {
        if (!startedAt) return 0;
        return Math.floor((Date.now() - startedAt) / 1000);
    }

    function setButtons(joined) {
        if (startBtn) startBtn.disabled = joined;
        if (muteBtn) muteBtn.disabled = !joined;
        if (endBtn) endBtn.disabled = !joined;
    }

    async function flushPendingIceCandidates() {
        if (!pc || !remoteDescriptionSet || pendingIceCandidates.length === 0) return;

        const candidates = pendingIceCandidates.slice();
        pendingIceCandidates = [];

        for (const candidate of candidates) {
            try {
                await pc.addIceCandidate(new RTCIceCandidate(candidate));
            } catch (err) {
                console.error('Queued ICE candidate error:', err);
            }
        }
    }

    async function createPeerConnection() {
        pc = new RTCPeerConnection(iceServers);

        pc.onicecandidate = (event) => {
            if (event.candidate && socket) {
                socket.emit('ice-candidate', {
                    roomToken: cfg.roomToken,
                    candidate: event.candidate
                });
            }
        };

        pc.ontrack = (event) => {
            if (event.streams && event.streams[0]) {
                remoteStream = event.streams[0];
                if (remoteAudio) {
                    remoteAudio.srcObject = remoteStream;
                    remoteAudio.play().catch(function () {});
                }
                if (isRecording) {
                    addStreamToRecording(remoteStream);
                }
            }
        };

        pc.onconnectionstatechange = () => {
            setStatus('Call connection: ' + pc.connectionState);
            if (pc.connectionState === 'connected') {
                startedAt = startedAt || Date.now();
            }
            if (['failed', 'disconnected'].includes(pc.connectionState)) {
                setStatus('Call connection issue: ' + pc.connectionState + '. You may need to rejoin.');
            }
        };

        pc.oniceconnectionstatechange = () => {
            if (pc && pc.iceConnectionState === 'connected') {
                startedAt = startedAt || Date.now();
            }
        };

        localStream.getTracks().forEach((track) => {
            pc.addTrack(track, localStream);
        });
    }

    async function joinCall() {
        if (hasJoined) return;
        hasJoined = true;
        remoteDescriptionSet = false;
        pendingIceCandidates = [];

        try {
            if (typeof io === 'undefined') {
                throw new Error('Socket.IO client was not loaded. Check your internet connection or CDN access.');
            }

            setStatus('Requesting microphone permission...');

            localStream = await navigator.mediaDevices.getUserMedia({
                audio: true,
                video: false
            });

            if (localAudio) {
                localAudio.srcObject = localStream;
                localAudio.play().catch(function () {});
            }

            await notifyCallStarted();
            await startAutoRecording();

            socket = io(cfg.signalingUrl, {
                path: cfg.signalingPath || '/socket.io',
                transports: ['websocket', 'polling'],
                forceNew: true,
                reconnectionAttempts: 5,
                timeout: 15000
            });

            await createPeerConnection();

            socket.on('connect', () => {
                setStatus('Connected to signaling server. Joining call room...');
                socket.emit('join-room', {
                    roomToken: cfg.roomToken,
                    userId: cfg.userId,
                    participantType: cfg.participantType
                });
            });

            socket.on('connect_error', (err) => {
                console.error('Socket connection error:', err);
                setStatus('Cannot connect to WebRTC server. Check signaling URL/path.');
            });

            socket.on('server-error', (payload) => {
                setStatus((payload && payload.message) ? payload.message : 'WebRTC server error.');
            });

            socket.on('participant-update', (payload) => {
                if (!payload) return;
                if (payload.hasCustomer && payload.hasCsr) {
                    setStatus('Both participants are in the call room. Connecting audio...');
                } else {
                    setStatus('Waiting for the other participant to join...');
                }
            });

            socket.on('ready-for-offer', async () => {
                if (cfg.participantType !== 'csr' || !pc) return;
                setStatus('Creating call offer...');
                const offer = await pc.createOffer({ offerToReceiveAudio: true, offerToReceiveVideo: false });
                await pc.setLocalDescription(offer);
                socket.emit('offer', {
                    roomToken: cfg.roomToken,
                    offer
                });
            });

            socket.on('offer', async (offer) => {
                if (!pc) return;
                setStatus('Received call offer. Creating answer...');
                await pc.setRemoteDescription(new RTCSessionDescription(offer));
                remoteDescriptionSet = true;
                await flushPendingIceCandidates();

                const answer = await pc.createAnswer();
                await pc.setLocalDescription(answer);
                socket.emit('answer', {
                    roomToken: cfg.roomToken,
                    answer
                });
            });

            socket.on('answer', async (answer) => {
                if (!pc) return;
                setStatus('Received answer. Connecting audio...');
                await pc.setRemoteDescription(new RTCSessionDescription(answer));
                remoteDescriptionSet = true;
                await flushPendingIceCandidates();
            });

            socket.on('ice-candidate', async (candidate) => {
                if (!candidate || !pc) return;

                if (!remoteDescriptionSet) {
                    pendingIceCandidates.push(candidate);
                    return;
                }

                try {
                    await pc.addIceCandidate(new RTCIceCandidate(candidate));
                } catch (err) {
                    console.error('ICE candidate error:', err);
                }
            });

            socket.on('call-ended', async () => {
                setStatus('The other participant ended the call. Saving recording...');
                await stopAndUploadRecording();
                await cleanup(false);
                setStatus('The other participant ended the call.');
            });

            socket.on('participant-left', (payload) => {
                const label = payload && payload.participantType ? payload.participantType : 'participant';
                setStatus('The other ' + label + ' left the call room.');
            });

            setButtons(true);
            setStatus('Waiting for the other participant...');
        } catch (err) {
            console.error(err);
            setStatus('Unable to start call: ' + err.message);
            hasJoined = false;
            setButtons(false);
        }
    }

    async function cleanup(notifyServer) {
        if (notifyServer && socket) {
            socket.emit('end-call', { roomToken: cfg.roomToken });
        }

        if (localStream) {
            localStream.getTracks().forEach((track) => track.stop());
        }

        if (pc) {
            pc.close();
        }

        if (socket) {
            socket.disconnect();
        }

        if (remoteAudio) remoteAudio.srcObject = null;
        if (localAudio) localAudio.srcObject = null;

        localStream = null;
        remoteStream = null;
        pc = null;
        socket = null;
        hasJoined = false;
        remoteDescriptionSet = false;
        pendingIceCandidates = [];
        setButtons(false);
    }

    async function endCall() {
        const seconds = durationSeconds();
        setStatus('Saving call recording...');
        await stopAndUploadRecording();
        await cleanup(true);
        setStatus('Call ended. Saving call log...');

        const form = new FormData();
        form.append('room_token', cfg.roomToken);
        form.append('duration_seconds', seconds.toString());
        form.append('result', seconds > 0 ? 'connected' : 'ended');

        try {
            const res = await fetch(cfg.apiBase + '/end_call.php', {
                method: 'POST',
                body: form,
                credentials: 'same-origin'
            });
            const data = await res.json().catch(() => ({}));
            setStatus(data.ok === false ? (data.error || 'Call ended, but saving failed.') : 'Call ended and saved.');
        } catch (err) {
            setStatus('Call ended, but saving failed.');
        }
    }

    function toggleMute() {
        if (!localStream) return;
        isMuted = !isMuted;
        localStream.getAudioTracks().forEach((track) => {
            track.enabled = !isMuted;
        });
        if (muteBtn) muteBtn.textContent = isMuted ? 'Unmute' : 'Mute';
    }

    async function saveNotes(event) {
        event.preventDefault();
        if (!notesInput) return;

        const form = new FormData();
        form.append('room_token', cfg.roomToken);
        form.append('notes', notesInput.value || '');

        try {
            const res = await fetch(cfg.apiBase + '/save_call_notes.php', {
                method: 'POST',
                body: form,
                credentials: 'same-origin'
            });

            const data = await res.json();
            alert(data.ok ? 'Notes saved.' : (data.error || 'Unable to save notes.'));
        } catch (err) {
            alert('Unable to save notes.');
        }
    }

    if (startBtn) startBtn.addEventListener('click', joinCall);
    if (muteBtn) muteBtn.addEventListener('click', toggleMute);
    if (endBtn) endBtn.addEventListener('click', endCall);
    if (notesForm) notesForm.addEventListener('submit', saveNotes);
})();
