(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 = [];

    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 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 (remoteAudio && event.streams && event.streams[0]) {
                remoteAudio.srcObject = event.streams[0];
                remoteAudio.play().catch(function () {});
            }
        };

        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 () {});
            }

            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.');
                await cleanup(false);
            });

            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;
        pc = null;
        socket = null;
        hasJoined = false;
        remoteDescriptionSet = false;
        pendingIceCandidates = [];
        setButtons(false);
    }

    async function endCall() {
        const seconds = durationSeconds();
        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);
})();
