let localStream = null;
const remoteStreams = {};

const Media = {
    async loadDevices() {
        const devices = await navigator.mediaDevices.enumerateDevices();

        const cameras = devices.filter(d => d.kind === "videoinput");
        const mics = devices.filter(d => d.kind === "audioinput");

        const cameraSelect = document.getElementById("cameraSelect");
        const micSelect = document.getElementById("micSelect");

        if (!cameraSelect || !micSelect) return;

        const selectedCamera = cameraSelect.value;
        const selectedMic = micSelect.value;

        cameraSelect.innerHTML = "";
        micSelect.innerHTML = "";

        const noCamera = document.createElement("option");
        noCamera.value = "";
        noCamera.textContent = "No camera / audio only";
        cameraSelect.appendChild(noCamera);

        const defaultMic = document.createElement("option");
        defaultMic.value = "";
        defaultMic.textContent = "Default microphone";
        micSelect.appendChild(defaultMic);

        for (const camera of cameras) {
            const option = document.createElement("option");
            option.value = camera.deviceId;
            option.textContent = camera.label || `Camera ${cameraSelect.length}`;
            cameraSelect.appendChild(option);
        }

        for (const mic of mics) {
            const option = document.createElement("option");
            option.value = mic.deviceId;
            option.textContent = mic.label || `Microphone ${micSelect.length}`;
            micSelect.appendChild(option);
        }

        cameraSelect.value = [...cameraSelect.options].some(o => o.value === selectedCamera)
            ? selectedCamera
            : "";

        micSelect.value = [...micSelect.options].some(o => o.value === selectedMic)
            ? selectedMic
            : "";

        LogMessage(`Loaded devices: ${cameras.length} cameras, ${mics.length} mics`);
    },

    async ensureLocalMedia() {
        const cameraSelect = document.getElementById("cameraSelect");
        const micSelect = document.getElementById("micSelect");

        if (localStream) {
            return localStream;
        }

        const audioDeviceId = micSelect?.value || "";
        const videoDeviceId = cameraSelect?.value || "";

        const constraints = {
            audio: audioDeviceId
                ? { deviceId: { exact: audioDeviceId } }
                : true,
            video: videoDeviceId
                ? { deviceId: { exact: videoDeviceId } }
                : false
        };

        try {
            localStream = await navigator.mediaDevices.getUserMedia(constraints);
        } catch (err) {
            LogMessage("Selected media failed: " + err);

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

            LogMessage("No camera available, continuing without video");
        }

        this.attachLocalStream(localStream);
        LogMessage("Local media initialized");

        return localStream;
    },

    attachLocalStream(stream) {
        const localVideo = document.getElementById("localVideo");
        const localMediaStatus = document.getElementById("localMediaStatus");
        const localVideoStatus = document.getElementById("localVideoStatus");

        const audioTracks = stream.getAudioTracks();
        const videoTracks = stream.getVideoTracks();

        if (localVideo) {
            localVideo.srcObject = videoTracks.length > 0 ? stream : null;
        }

        if (localMediaStatus) {
            localMediaStatus.textContent =
                audioTracks.length > 0
                    ? "Microphone active"
                    : "No microphone";
        }

        if (localVideoStatus) {
            localVideoStatus.textContent =
                videoTracks.length > 0
                    ? "Local video active"
                    : "Local video unavailable";
        }
    },

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

        await this.ensureLocalMedia();

        if (window.RelayRtc?.applyLocalStreamToAllPeerConnections) {
            await window.RelayRtc.applyLocalStreamToAllPeerConnections();
        }
    },

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

        await this.loadDevices();
        await this.ensureLocalMedia();

        if (window.RelayRtc?.applyLocalStreamToAllPeerConnections) {
            await window.RelayRtc.applyLocalStreamToAllPeerConnections();
        }
    },

    async applyLocalStreamToPeerConnection(pc, username) {
        const stream = await this.ensureLocalMedia();
        const existingSenders = pc.getSenders();

        for (const track of stream.getTracks()) {
            const existingSender = existingSenders.find(sender =>
                sender.track && sender.track.kind === track.kind
            );

            if (existingSender) {
                await existingSender.replaceTrack(track);
                LogMessage(`Replaced local ${track.kind} track for ${username}`);
            } else {
                pc.addTrack(track, stream);
                LogMessage(`Added local ${track.kind} track for ${username}`);
            }
        }
    },

    async applyLocalStreamToAllPeerConnections() {
        if (!window.RelayRtc?.peerConnections) return;

        for (const [username, pc] of Object.entries(window.RelayRtc.peerConnections)) {
            await this.applyLocalStreamToPeerConnection(pc, username);
        }
    },

    attachRemoteStream(username, stream) {
        remoteStreams[username] = stream;

        const tile = this.ensureRemoteTile(username);
        const video = tile.querySelector("video");
        const status = tile.querySelector(".remote-media-status");

        if (video) {
            video.srcObject = stream;
        }

        const audioTracks = stream.getAudioTracks();
        const videoTracks = stream.getVideoTracks();

        if (status) {
            status.textContent =
                `${audioTracks.length > 0 ? "Audio" : "No audio"} / ` +
                `${videoTracks.length > 0 ? "Video" : "No video"}`;
        }
    },

    ensureRemoteTile(username) {
        const container = document.getElementById("remoteMediaContainer");
        if (!container) return null;

        let tile = document.getElementById(`remote-tile-${username}`);
        if (tile) return tile;

        tile = document.createElement("div");
        tile.id = `remote-tile-${username}`;
        tile.className = "remote-media-tile";

        const title = document.createElement("div");
        title.className = "remote-media-title";
        title.textContent = username;

        const video = document.createElement("video");
        video.autoplay = true;
        video.playsInline = true;

        const status = document.createElement("div");
        status.className = "remote-media-status";
        status.textContent = "Remote media: waiting...";

        tile.appendChild(title);
        tile.appendChild(video);
        tile.appendChild(status);

        container.appendChild(tile);

        return tile;
    },

    removeRemoteStream(username) {
        delete remoteStreams[username];

        const tile = document.getElementById(`remote-tile-${username}`);
        if (tile) {
            tile.remove();
        }
    },

    wireDeviceSelectors() {
        const cameraSelect = document.getElementById("cameraSelect");
        const micSelect = document.getElementById("micSelect");

        if (cameraSelect) {
            cameraSelect.addEventListener("change", async () => {
                LogMessage("Camera changed");
                await this.restartLocalMedia();
            });
        }

        if (micSelect) {
            micSelect.addEventListener("change", async () => {
                LogMessage("Microphone changed");
                await this.restartLocalMedia();
            });
        }
    }
};

window.Media = Media;