import {
  BackgroundImageConfig,
  VirtualBackgroundConfig,
  VirtualBackgroundType,
} from '@zocks-communications/client-sdk-js'
import { useZocksStore } from '../../app'
import {
  selectCurrentVirtualBackground,
  selectIsScreenSharing,
  selectLocalParticipant,
  selectScreenSharingParticipant,
  useMediaDevice,
  ZocksMediaActions,
  ZocksTranscriptionActions,
} from '../../store'
import { useZocksDispatch } from '../../app/hooks'
import {
  clearCurrentVirtualBackground,
  selectAvailableVirtualBackgrounds,
  setCurrentVirtualBackground,
} from '../../store/virtualBackground/virtualBackgroundSlice'
import { selectCurrentAudioDeviceId, selectTranscriptionStatus } from '../../store'
import { setDisplayTranscriptions } from '../../store/transcript/transcriptSlice'
import { setVideoIsLoading } from '../../store/mediaDevice/mediaDeviceSlice'
import { useCallback, useEffect, useRef } from 'react'

export const useRoomControls = (
  mediaActions: ZocksMediaActions,
  audioEnabledByDefault: boolean = false,
  videoEnabledByDefault: boolean = false
) => {
  const dispatch = useZocksDispatch()
  const { currentAudioDeviceId, currentVideoDeviceId } = useMediaDevice()
  const {
    publishAudio,
    publishVideo,
    unpublishAudio,
    unpublishVideo,
    switchActiveDevice,
    publishScreenShare,
    unpublishScreenShare,
  } = mediaActions
  const localParticipant = useZocksStore(selectLocalParticipant)
  const isScreenSharing = useZocksStore(selectIsScreenSharing)
  const screenSharingParticipant = useZocksStore(selectScreenSharingParticipant)
  const isLocalScreenShare = screenSharingParticipant === localParticipant
  const audioIsOn = !!(localParticipant.tracks.audioTracks.length && !localParticipant.tracks.audioTracks[0].isMuted)
  const videoIsOn = !!(localParticipant.tracks.videoTracks.length && !localParticipant.tracks.videoTracks[0].isMuted)

  const toggleAudio = useCallback(async () => {
    const mediaTrackSettings = currentAudioDeviceId ? { deviceId: currentAudioDeviceId } : undefined
    if (audioIsOn) {
      await unpublishAudio(mediaTrackSettings)
    } else {
      await publishAudio(mediaTrackSettings)
    }
  }, [audioIsOn, currentAudioDeviceId, publishAudio, unpublishAudio])

  const toggleVideo = useCallback(async () => {
    const mediaTrackSettings = currentVideoDeviceId ? { deviceId: currentVideoDeviceId } : undefined
    if (videoIsOn) {
      await unpublishVideo(mediaTrackSettings)
    } else {
      try {
        dispatch(setVideoIsLoading(true))
        await publishVideo(mediaTrackSettings)
      } finally {
        dispatch(setVideoIsLoading(false))
      }
    }
  }, [currentVideoDeviceId, dispatch, publishVideo, unpublishVideo, videoIsOn])

  const toggleScreenShare = useCallback(async () => {
    if (isScreenSharing) {
      await unpublishScreenShare()
    } else {
      await publishScreenShare()
    }
  }, [isScreenSharing, publishScreenShare, unpublishScreenShare])

  const selectAudioDevice = useCallback(
    async (audioDevice: MediaDeviceInfo) => {
      const { deviceId } = audioDevice
      if (currentAudioDeviceId !== deviceId) {
        await switchActiveDevice('audioinput', audioDevice.deviceId)
      }
    },
    [currentAudioDeviceId, switchActiveDevice]
  )
  const selectVideoDevice = useCallback(
    async (videoDevice: MediaDeviceInfo) => {
      const { deviceId } = videoDevice
      if (currentVideoDeviceId !== deviceId) {
        try {
          dispatch(setVideoIsLoading(true))
          await switchActiveDevice('videoinput', deviceId)
        } finally {
          dispatch(setVideoIsLoading(false))
        }
      }
    },
    [currentVideoDeviceId, dispatch, switchActiveDevice]
  )

  const audioAutoEnabled = useRef(false)
  const videoAutoEnabled = useRef(false)

  useEffect(() => {
    if (videoEnabledByDefault && !videoAutoEnabled.current && !videoIsOn) {
      videoAutoEnabled.current = true
      toggleVideo()
    }
  }, [videoEnabledByDefault, toggleVideo, videoIsOn])

  useEffect(() => {
    if (audioEnabledByDefault && !audioAutoEnabled.current && !audioIsOn) {
      audioAutoEnabled.current = true
      toggleAudio()
    }
  }, [audioEnabledByDefault, toggleAudio, audioIsOn])

  return {
    audioIsOn,
    videoIsOn,
    isScreenSharing,
    isLocalScreenShare,
    currentVideoDeviceId,
    toggleAudio,
    toggleVideo,
    toggleScreenShare,
    selectAudioDevice,
    selectVideoDevice,
  }
}

export const useTranscription = (audioIsOn: boolean, transcriptionActions?: ZocksTranscriptionActions) => {
  const dispatch = useZocksDispatch()

  const transcriptionStatus = useZocksStore(selectTranscriptionStatus)
  const currentAudioDeviceId = useZocksStore(selectCurrentAudioDeviceId)
  const { serviceEnabled, autoEnabled, displayTranscriptions } = transcriptionStatus

  const toggleTranscription = async () => {
    if (!transcriptionActions) {
      return
    }
    if (!autoEnabled) {
      await toggleManuallyEnabledTranscription(transcriptionActions)
    } else {
      dispatch(setDisplayTranscriptions(!displayTranscriptions))
    }
  }

  const toggleManuallyEnabledTranscription = async (transcriptionActions: ZocksTranscriptionActions) => {
    const { enableTranscription, disableTranscription, startTranscription, stopTranscription } = transcriptionActions
    if (serviceEnabled) {
      dispatch(setDisplayTranscriptions(false))
      await stopTranscription()
      await disableTranscription()
    } else {
      dispatch(setDisplayTranscriptions(true))
      await enableTranscription()
      if (audioIsOn) {
        const constraints = currentAudioDeviceId ? { deviceId: currentAudioDeviceId } : undefined
        await startTranscription(constraints)
      }
    }
  }

  return {
    transcriptionStatus,
    toggleTranscription,
  }
}
export const useVirtualBackgrounds = (mediaActions: ZocksMediaActions) => {
  const dispatch = useZocksDispatch()
  const { currentVideoDeviceId } = useMediaDevice()
  const currentVirtualBackground = useZocksStore(selectCurrentVirtualBackground)
  const availableBackgrounds = useZocksStore(selectAvailableVirtualBackgrounds)
  const { setVirtualBackground, clearVirtualBackground } = mediaActions

  const changeVirtualBackground = async (
    virtualBackgroundType: VirtualBackgroundType | null,
    config?: VirtualBackgroundConfig
  ) => {
    const mediaTrackSettings = currentVideoDeviceId ? { deviceId: currentVideoDeviceId } : undefined
    try {
      dispatch(setVideoIsLoading(true))
      if (virtualBackgroundType) {
        await setVirtualBackground(virtualBackgroundType, config, mediaTrackSettings)
        dispatch(setCurrentVirtualBackground({ type: virtualBackgroundType, url: config?.backgroundImageUrl }))
      } else {
        await clearVirtualBackground(mediaTrackSettings)
        dispatch(clearCurrentVirtualBackground())
      }
    } finally {
      dispatch(setVideoIsLoading(false))
    }
  }

  const toggleVirtualBackground = async (type: VirtualBackgroundType, config?: VirtualBackgroundConfig) => {
    if (
      currentVirtualBackground &&
      currentVirtualBackground.type === type &&
      currentVirtualBackground.url === config?.backgroundImageUrl
    ) {
      await changeVirtualBackground(null)
    } else {
      await changeVirtualBackground(type, config)
    }
  }

  const toggleBlur = async () => {
    await toggleVirtualBackground(VirtualBackgroundType.BLUR)
  }

  const toggleVirtualBackgroundImage = async (imageUrl: string) => {
    await toggleVirtualBackground(VirtualBackgroundType.IMAGE, {
      backgroundImageUrl: imageUrl,
    } as BackgroundImageConfig)
  }

  return {
    availableBackgrounds,
    currentVirtualBackground,
    toggleBlur,
    toggleVirtualBackgroundImage,
    toggleVirtualBackground,
  }
}
