import { onBeforeUnmount, ref } from 'vue'
import { usePrivacyStore } from '@/stores/privacy'

type BrowserPermissionState = PermissionState | 'unsupported'

type PermissionQueryName = 'camera' | 'geolocation'

export function usePermissions() {
  const privacyStore = usePrivacyStore()
  const cameraPermissionState = ref<BrowserPermissionState>('prompt')
  const locationPermissionState = ref<BrowserPermissionState>('prompt')

  let cameraPermissionStatus: PermissionStatus | null = null
  let locationPermissionStatus: PermissionStatus | null = null

  const isPermissionsApiSupported = () =>
    typeof navigator !== 'undefined' && 'permissions' in navigator

  const setPermissionState = (
    target: typeof cameraPermissionState | typeof locationPermissionState,
    state: BrowserPermissionState
  ) => {
    target.value = state
  }

  const setPermissionChangeListener = (
    name: PermissionQueryName,
    status: PermissionStatus
  ) => {
    status.onchange = () => {
      const target =
        name === 'camera' ? cameraPermissionState : locationPermissionState
      setPermissionState(target, status.state)
    }
  }

  async function queryPermissionState(name: PermissionQueryName) {
    if (!isPermissionsApiSupported()) {
      return null
    }

    try {
      return await navigator.permissions.query({ name } as PermissionDescriptor)
    } catch (error) {
      return null
    }
  }

  async function syncPermissionState(name: PermissionQueryName) {
    const target =
      name === 'camera' ? cameraPermissionState : locationPermissionState
    const previousStatus =
      name === 'camera' ? cameraPermissionStatus : locationPermissionStatus

    if (previousStatus) {
      previousStatus.onchange = null
    }

    const status = await queryPermissionState(name)
    if (!status) {
      if (target.value === 'prompt') {
        setPermissionState(target, 'unsupported')
      }
      return
    }

    if (name === 'camera') {
      cameraPermissionStatus = status
    } else {
      locationPermissionStatus = status
    }

    setPermissionState(target, status.state)
    setPermissionChangeListener(name, status)
  }

  async function syncPermissionStates() {
    await Promise.all([
      syncPermissionState('camera'),
      syncPermissionState('geolocation'),
    ])
  }

  const checkPrivacyAccepted = (): boolean => {
    if (!privacyStore.isAccepted) {
      privacyStore.modalPrivacy = true
      return false
    }
    return true
  }

  async function requestCameraPermission() {
    if (!checkPrivacyAccepted()) {
      return false
    }

    if (cameraPermissionState.value === 'granted') {
      return true
    }

    if (!navigator.mediaDevices?.getUserMedia) {
      setPermissionState(cameraPermissionState, 'unsupported')
      return false
    }

    try {
      const stream = await navigator.mediaDevices.getUserMedia({
        video: { facingMode: 'user' },
        audio: false,
      })

      stream.getTracks().forEach((track) => track.stop())
      setPermissionState(cameraPermissionState, 'granted')
      return true
    } catch (error) {
      setPermissionState(cameraPermissionState, 'denied')
      return false
    } finally {
      await syncPermissionState('camera')
    }
  }

  async function requestLocationPermission() {
    if (!checkPrivacyAccepted()) {
      return false
    }

    if (locationPermissionState.value === 'granted') {
      return true
    }

    if (!navigator.geolocation) {
      setPermissionState(locationPermissionState, 'unsupported')
      return false
    }

    return new Promise<boolean>((resolve) => {
      navigator.geolocation.getCurrentPosition(
        async () => {
          setPermissionState(locationPermissionState, 'granted')
          await syncPermissionState('geolocation')
          resolve(true)
        },
        async () => {
          setPermissionState(locationPermissionState, 'denied')
          await syncPermissionState('geolocation')
          resolve(false)
        }
      )
    })
  }

  onBeforeUnmount(() => {
    if (cameraPermissionStatus) {
      cameraPermissionStatus.onchange = null
    }

    if (locationPermissionStatus) {
      locationPermissionStatus.onchange = null
    }
  })

  return {
    cameraPermissionState,
    locationPermissionState,
    checkPrivacyAccepted,
    syncPermissionStates,
    requestCameraPermission,
    requestLocationPermission,
  }
}
