import { useCallback, useEffect, useState } from 'react'
import {
  createVirtualBackground,
  LocalMediaStream,
  StreamKind,
  StreamSource,
  VirtualBackground,
  VirtualBackgroundType,
} from '@zocks-communications/client-sdk-js'
import { useZocksDispatch, useZocksStore } from '../../app/hooks'
import {
  clearCurrentVirtualBackground,
  selectCurrentVirtualBackground,
  setCurrentVirtualBackground,
} from '../virtualBackground/virtualBackgroundSlice'
import { selectCurrentAudioDeviceId, selectCurrentVideoDeviceId } from '../mediaDevice'

export const useLocalMedia = (videoEnabledByDefault?: boolean, audioEnabledByDefault?: boolean) => {
  const currentAudioDeviceId = useZocksStore(selectCurrentAudioDeviceId)
  const currentVideoDeviceId = useZocksStore(selectCurrentVideoDeviceId)
  const [localAudioStream, setLocalAudioStream] = useState<LocalMediaStream>()
  const [localVideoStream, setLocalVideoStream] = useState<LocalMediaStream>()
  const [videoStreamIsLoading, setVideoStreamIsLoading] = useState<boolean>(false)

  const startLocalAudioStream = useCallback((deviceId?: string) => {
    LocalMediaStream.create({ audio: { deviceId }, video: false }).then((audioStream) => {
      setLocalAudioStream(audioStream)
    })
  }, [])

  const startLocalVideoStream = useCallback((deviceId?: string) => {
    setVideoStreamIsLoading(true)
    LocalMediaStream.create({ audio: false, video: { deviceId } })
      .then((videoStream) => {
        setLocalVideoStream(videoStream)
      })
      .finally(() => setVideoStreamIsLoading(false))
  }, [])

  const toggleLocalAudioStream = () => {
    if (localAudioStream) {
      localAudioStream.stopStream()
      setLocalAudioStream(undefined)
    } else {
      startLocalAudioStream(currentAudioDeviceId || undefined)
    }
  }

  const toggleLocalVideoStream = () => {
    if (localVideoStream) {
      localVideoStream.stopStream()
      setLocalVideoStream(undefined)
    } else {
      startLocalVideoStream(currentVideoDeviceId || undefined)
    }
  }

  const selectAudioDevice = (audioDevice: MediaDeviceInfo) => {
    if (localAudioStream) {
      if (localAudioStream.mediaStreamTrack?.getSettings().deviceId === audioDevice.deviceId) {
        return
      }
      localAudioStream.stopStream()
      startLocalAudioStream(audioDevice.deviceId)
    }
  }

  const selectVideoDevice = (videoDevice: MediaDeviceInfo) => {
    if (localVideoStream) {
      if (localVideoStream.mediaStreamTrack?.getSettings().deviceId === videoDevice.deviceId) {
        return
      }
      localVideoStream.stopStream()
      setLocalVideoStream(undefined)
      startLocalVideoStream(videoDevice.deviceId)
    }
  }

  useEffect(() => {
    if (videoEnabledByDefault) {
      startLocalVideoStream()
    }
  }, [videoEnabledByDefault, startLocalVideoStream])

  useEffect(() => {
    if (audioEnabledByDefault) {
      startLocalAudioStream()
    }
  }, [audioEnabledByDefault, startLocalAudioStream])

  return {
    currentAudioDeviceId,
    currentVideoDeviceId,
    localAudioStream,
    localVideoStream,
    videoStreamIsLoading,
    toggleLocalAudioStream,
    toggleLocalVideoStream,
    selectAudioDevice,
    selectVideoDevice,
  }
}

export const useLocalVirtualBackgrounds = () => {
  const dispatch = useZocksDispatch()
  const currentVirtualBackground = useZocksStore(selectCurrentVirtualBackground)
  const availableVirtualBackgrounds = Object.values(VirtualBackgroundType)

  const [virtualBackground, setVirtualBackground] = useState<VirtualBackground>()
  const [virtualBackgroundIsLoading, setVirtualBackgroundIsLoading] = useState<boolean>(false)
  const [virtualBackgroundStream, setVirtualBackgroundStream] = useState<LocalMediaStream>()

  const changeVirtualBackground = async (virtualBackgroundType: VirtualBackgroundType | null, deviceId?: string) => {
    setVirtualBackgroundIsLoading(true)
    if (virtualBackgroundType) {
      try {
        await createNewVirtualBackground(virtualBackgroundType, deviceId)
        dispatch(setCurrentVirtualBackground(virtualBackgroundType))
      } finally {
        setVirtualBackgroundIsLoading(false)
      }
    } else {
      try {
        await stopVirtualBackground()
        dispatch(clearCurrentVirtualBackground())
      } finally {
        setVirtualBackgroundIsLoading(false)
      }
    }
  }

  const createNewVirtualBackground = async (virtualBackgroundType: VirtualBackgroundType, deviceId?: string) => {
    await virtualBackground?.stop()
    const sourceVideoStream = await LocalMediaStream.create({
      audio: false,
      video: { deviceId, width: { ideal: 1280 }, height: { ideal: 720 } },
    })
    const newVirtualBackground = createVirtualBackground(virtualBackgroundType, sourceVideoStream.mediaStream!)
    await newVirtualBackground.start()
    const targetVideoStream = LocalMediaStream.createFromMediaStreamTrack(
      newVirtualBackground.outputStream,
      newVirtualBackground.outputStreamTrack,
      StreamKind.VIDEO,
      StreamSource.CAMERA
    )
    setVirtualBackgroundStream(targetVideoStream)
    setVirtualBackground(newVirtualBackground)
  }

  const stopLocalVirtualBackground = async () => {
    if (!virtualBackground) {
      return
    }
    setVirtualBackgroundIsLoading(true)
    try {
      await stopVirtualBackground()
    } finally {
      setVirtualBackgroundIsLoading(false)
    }
  }

  const stopVirtualBackground = async () => {
    await virtualBackground?.stop()
    setVirtualBackgroundStream(undefined)
    setVirtualBackground(undefined)
  }

  return {
    availableVirtualBackgrounds,
    currentVirtualBackground,
    virtualBackgroundIsLoading,
    virtualBackgroundStream,
    changeVirtualBackground,
    stopLocalVirtualBackground,
  }
}
