/**
 * MediaManager - Media device and stream management
 */

export interface MediaConstraints {
    video?: boolean | MediaTrackConstraints;
    audio?: boolean | MediaTrackConstraints;
}

export interface MediaDevice {
    deviceId: string;
    kind: MediaDeviceKind;
    label: string;
    groupId: string;
}

export class MediaManager {
    private currentStream?: MediaStream;
    private devices: MediaDevice[] = [];

    async getDevices(): Promise<MediaDevice[]> {
        try {
            const devices = await navigator.mediaDevices.enumerateDevices();
            this.devices = devices.map(device => ({
                deviceId: device.deviceId,
                kind: device.kind,
                label: device.label,
                groupId: device.groupId
            }));
            return this.devices;
        } catch (error) {
            throw new Error(`Failed to get media devices: ${error}`);
        }
    }

    async getUserMedia(constraints: MediaConstraints = { video: true, audio: true }): Promise<MediaStream> {
        try {
            const stream = await navigator.mediaDevices.getUserMedia(constraints);
            this.currentStream = stream;
            return stream;
        } catch (error) {
            throw new Error(`Failed to get user media: ${error}`);
        }
    }

    async getDisplayMedia(constraints?: MediaStreamConstraints): Promise<MediaStream> {
        try {
            const stream = await navigator.mediaDevices.getDisplayMedia(constraints);
            return stream;
        } catch (error) {
            throw new Error(`Failed to get display media: ${error}`);
        }
    }

    async switchCamera(deviceId?: string): Promise<MediaStream> {
        if (!this.currentStream) {
            throw new Error('No active stream to switch camera');
        }

        const videoTrack = this.currentStream.getVideoTracks()[0];
        if (!videoTrack) {
            throw new Error('No video track found');
        }

        const constraints: MediaTrackConstraints = {
            deviceId: deviceId ? { exact: deviceId } : undefined
        };

        try {
            const newStream = await navigator.mediaDevices.getUserMedia({
                video: constraints,
                audio: this.hasAudio()
            });

            // Replace video track
            const newVideoTrack = newStream.getVideoTracks()[0];
            if (newVideoTrack) {
                this.currentStream.removeTrack(videoTrack);
                this.currentStream.addTrack(newVideoTrack);
                videoTrack.stop();
            }

            return this.currentStream;
        } catch (error) {
            throw new Error(`Failed to switch camera: ${error}`);
        }
    }

    muteAudio(muted: boolean = true): void {
        if (!this.currentStream) return;

        this.currentStream.getAudioTracks().forEach(track => {
            track.enabled = !muted;
        });
    }

    muteVideo(muted: boolean = true): void {
        if (!this.currentStream) return;

        this.currentStream.getVideoTracks().forEach(track => {
            track.enabled = !muted;
        });
    }

    stopStream(stream?: MediaStream): void {
        const targetStream = stream || this.currentStream;
        if (!targetStream) return;

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

        if (targetStream === this.currentStream) {
            this.currentStream = undefined;
        }
    }

    getCurrentStream(): MediaStream | undefined {
        return this.currentStream;
    }

    hasVideo(): boolean {
        return (this.currentStream?.getVideoTracks().length || 0) > 0;
    }

    hasAudio(): boolean {
        return (this.currentStream?.getAudioTracks().length || 0) > 0;
    }

    isAudioMuted(): boolean {
        const audioTrack = this.currentStream?.getAudioTracks()[0];
        return audioTrack ? !audioTrack.enabled : true;
    }

    isVideoMuted(): boolean {
        const videoTrack = this.currentStream?.getVideoTracks()[0];
        return videoTrack ? !videoTrack.enabled : true;
    }

    getVideoDevices(): MediaDevice[] {
        return this.devices.filter(device => device.kind === 'videoinput');
    }

    getAudioDevices(): MediaDevice[] {
        return this.devices.filter(device => device.kind === 'audioinput');
    }

    async checkPermissions(): Promise<{
        camera: PermissionState;
        microphone: PermissionState;
    }> {
        try {
            const [camera, microphone] = await Promise.all([
                navigator.permissions.query({ name: 'camera' as PermissionName }),
                navigator.permissions.query({ name: 'microphone' as PermissionName })
            ]);

            return {
                camera: camera.state,
                microphone: microphone.state
            };
        } catch (error) {
            throw new Error(`Failed to check permissions: ${error}`);
        }
    }
} 