import React, { createContext, useCallback, useEffect, useState } from 'react'

export interface Toggle {
  /** The application name that owns this toggle */
  application: string
  /** Whether the toggle is currently enabled or disabled */
  enabled: boolean
  /** Unique identifier for the toggle */
  key: string
  /** Human-readable name for the toggle */
  name: string
}

interface ToggleProviderContextValue {
  /** Array of available toggles, undefined while loading */
  toggles: Toggle[] | undefined
  /** Function to set a toggle override */
  setToggleOverride?: (key: string, value: boolean) => void
}

export const ToggleProviderContext = createContext<ToggleProviderContextValue>({
  toggles: [],
  setToggleOverride: () => {},
})

interface ToggleProviderProps {
  /** Child components that will have access to the toggle context */
  children: React.ReactNode
  /** Distribution key used to identify which toggles to fetch from the CDN */
  distributionKey: string
  /** Environment to fetch toggles for (determines which CDN endpoint to use) */
  environment: 'development' | 'staging' | 'production'
  /** Optional callback function that gets called if there's an error fetching toggles */
  errorCallback?: (error: string | Record<string, unknown>) => void
  /** Optional callback function that gets called when toggle loading is finished (success or error) */
  finishedLoadingCallback?: (isLoaded: boolean) => void
}

// uncomment this to test against staging
// const TOGGLE_API_URL = 'https://stage-toggle-api.usepredict.com'
const TOGGLE_API_URL = 'https://toggle-api.usepredict.com'

const ToggleProvider = ({
  children,
  distributionKey,
  environment,
  errorCallback,
  finishedLoadingCallback,
}: ToggleProviderProps): React.JSX.Element => {
  const [togglesFromCDN, setTogglesFromCDN] = useState<Toggle[]>()
  const [toggleOverrides, setToggleOverrides] = useState<
    Record<string, boolean>
  >({})

  const setToggleOverride = useCallback((key: string, value: boolean) => {
    // Update cookie
    document.cookie = `${key}=${value.toString()}; path=/;`

    // Update local state
    setToggleOverrides((prev) => ({
      ...prev,
      [key]: value,
    }))
  }, [])

  useEffect(() => {
    const controller = new AbortController()
    const { signal } = controller
    fetch(`${TOGGLE_API_URL}/cdn/${distributionKey}/${environment}`, { signal })
      .then((response) => response.json())
      .then((responseJson) => {
        setTogglesFromCDN(responseJson)
        finishedLoadingCallback?.(true)
      })
      .catch((error) => {
        if (error?.name === 'AbortError') return

        console.error({ error })
        if (errorCallback) {
          errorCallback(error)
        }
        setTogglesFromCDN([])

        // If the query is cancelled, don't set finished loading to true. This fixes issues in strict mode
        if (!error?.message.toLowerCase().includes('canceled')) {
          finishedLoadingCallback?.(true)
        }
      })
    return () => {
      controller.abort()
    }
  }, [distributionKey, environment, errorCallback, finishedLoadingCallback])

  // Merge backend toggles with local overrides
  const mergedToggles = togglesFromCDN?.map((toggle) => ({
    ...toggle,
    enabled: Object.prototype.hasOwnProperty.call(toggleOverrides, toggle.key)
      ? toggleOverrides[toggle.key]
      : toggle.enabled,
  }))

  return (
    <ToggleProviderContext.Provider
      value={{
        toggles: mergedToggles,
        setToggleOverride,
      }}
    >
      {children}
    </ToggleProviderContext.Provider>
  )
}

export default ToggleProvider
