import React, { createContext, useContext, useState, useEffect, useRef } from 'react';
import { Room, RoomEvent } from 'livekit-client';
import { LiveKitRoom, RoomAudioRenderer } from '@livekit/components-react';
import { useQuery, useConvex } from 'convex/react';
import { api } from '../../../../convex/_generated/api';
import '@livekit/components-styles';

import joinSound from '../assets/sounds/join_call.mp3';
import leaveSound from '../assets/sounds/leave_call.mp3';
import muteSound from '../assets/sounds/mute.mp3';
import unmuteSound from '../assets/sounds/unmute.mp3';
import deafenSound from '../assets/sounds/deafen.mp3';
import undeafenSound from '../assets/sounds/undeafen.mp3';

const soundMap = {
    join: joinSound,
    leave: leaveSound,
    mute: muteSound,
    unmute: unmuteSound,
    deafen: deafenSound,
    undeafen: undeafenSound
};

const VoiceContext = createContext();

export const useVoice = () => useContext(VoiceContext);

function playSound(type) {
    const src = soundMap[type];
    if (!src) return;
    const audio = new Audio(src);
    audio.volume = 0.5;
    audio.play().catch(e => console.error("Sound play failed", e));
}

export const VoiceProvider = ({ children }) => {
    const [activeChannelId, setActiveChannelId] = useState(null);
    const [activeChannelName, setActiveChannelName] = useState(null);
    const [connectionState, setConnectionState] = useState('disconnected');
    const [room, setRoom] = useState(null);
    const [token, setToken] = useState(null);
    const [activeSpeakers, setActiveSpeakers] = useState(new Set());
    const [isMuted, setIsMuted] = useState(false);
    const [isDeafened, setIsDeafened] = useState(false);
    const [isScreenSharing, setIsScreenSharingLocal] = useState(false);

    const convex = useConvex();

    const voiceStates = useQuery(api.voiceState.getAll) || {};

    async function updateVoiceState(fields) {
        const userId = localStorage.getItem('userId');
        if (!userId || !activeChannelId) return;
        try {
            await convex.mutation(api.voiceState.updateState, { userId, ...fields });
        } catch (e) {
            console.error('Failed to update voice state:', e);
        }
    }

    const connectToVoice = async (channelId, channelName, userId) => {
        if (activeChannelId === channelId) return;

        if (room) await room.disconnect();

        setActiveChannelId(channelId);
        setActiveChannelName(channelName);
        setConnectionState('connecting');

        try {
            const { token: lkToken } = await convex.action(api.voice.getToken, {
                channelId,
                userId,
                username: localStorage.getItem('username') || 'Unknown'
            });

            if (!lkToken) throw new Error('Failed to get token');

            setToken(lkToken);

            const newRoom = new Room({
              adaptiveStream: true,
              dynacast: true,
              autoSubscribe: true,
              audioCaptureDefaults: {
                autoGainControl: true,
                echoCancellation: true,
                noiseSuppression: false,
                channelCount: 1,
                sampleRate: 48000,
              },
              publishDefaults: {
                audioPreset: { maxBitrate: 96_000 },
                dtx: false,
                red: true,
                screenShareEncoding: {
                  maxBitrate: 10_000_000,
                  maxFramerate: 60,
                },
                screenShareSimulcastLayers: [],
              },
            });
            await newRoom.connect(import.meta.env.VITE_LIVEKIT_URL, lkToken);

            await newRoom.localParticipant.setMicrophoneEnabled(!isMuted && !isDeafened);

            setRoom(newRoom);
            setConnectionState('connected');
            window.voiceRoom = newRoom;
            playSound('join');

            await convex.mutation(api.voiceState.join, {
                channelId,
                userId,
                username: localStorage.getItem('username') || 'Unknown',
                isMuted,
                isDeafened,
            });

            newRoom.on(RoomEvent.Disconnected, async (reason) => {
                console.warn('Voice Room Disconnected. Reason:', reason);
                playSound('leave');
                setConnectionState('disconnected');
                setActiveChannelId(null);
                setRoom(null);
                setToken(null);
                setActiveSpeakers(new Set());

                try {
                    await convex.mutation(api.voiceState.leave, { userId });
                } catch (e) {
                    console.error('Failed to leave voice state:', e);
                }
            });

            newRoom.on(RoomEvent.ActiveSpeakersChanged, (speakers) => {
                setActiveSpeakers(new Set(speakers.map(p => p.identity)));
            });

        } catch (err) {
            console.error('Voice Connection Failed:', err);
            setConnectionState('error');
            setActiveChannelId(null);
        }
    };

    const disconnectVoice = () => {
        console.log('User manually disconnected voice');
        if (room) room.disconnect();
    };

    const toggleMute = async () => {
        const nextState = !isMuted;
        setIsMuted(nextState);
        playSound(nextState ? 'mute' : 'unmute');
        if (room) {
            room.localParticipant.setMicrophoneEnabled(!nextState);
        }
        await updateVoiceState({ isMuted: nextState });
    };

    const toggleDeafen = async () => {
        const nextState = !isDeafened;
        setIsDeafened(nextState);
        playSound(nextState ? 'deafen' : 'undeafen');
        if (room && !isMuted) {
            room.localParticipant.setMicrophoneEnabled(!nextState);
        }
        await updateVoiceState({ isDeafened: nextState });
    };

    const setScreenSharing = async (active) => {
        setIsScreenSharingLocal(active);
        await updateVoiceState({ isScreenSharing: active });
    };

    return (
        <VoiceContext.Provider value={{
            activeChannelId,
            activeChannelName,
            connectionState,
            connectToVoice,
            disconnectVoice,
            room,
            token,
            voiceStates,
            activeSpeakers,
            isMuted,
            isDeafened,
            toggleMute,
            toggleDeafen,
            isScreenSharing,
            setScreenSharing
        }}>
            {children}
            {room && (
                <LiveKitRoom
                    room={room}
                    style={{ position: 'absolute', width: 0, height: 0, overflow: 'hidden' }}
                >
                     <RoomAudioRenderer muted={isDeafened} />
                </LiveKitRoom>
            )}
        </VoiceContext.Provider>
    );
};
