import { createContext, useContext, useEffect, useMemo, useState } from 'react'
import { runJinja } from '../python'
import { JinjaContextConfig, JinjaContextValue } from './JinjaContextValue'
import { JinjaExpression } from './JinjaVariable'

const context = createContext<JinjaContextValue | undefined>(undefined)

export function JinjaContext({ children, ...props }: Omit<JinjaContextConfig, 'parent'> & { children: React.ReactNode }) {
  const parent = useContext(context)
  const value = useMemo(() => new JinjaContextValue({ ...props, parent }), [props.name, props.prefix, props.variables])
  return <context.Provider value={value}>{children}</context.Provider>
}

export function JinjaRawContext({ children, value }: { children: React.ReactNode, value: JinjaContextValue }) {
  return <context.Provider value={value}>{children}</context.Provider>
}

export function useJinjaContext(name?: string) {
  const ctx = useContext(context)
  return useMemo(() => {
    let result: JinjaContextValue | undefined = ctx
    while (name && result && result.name !== name) {
      result = result.parent
    }
    return result
  }, [ctx, name])
}

function getVariable(ctx: JinjaContextValue | undefined, key: string, ignore: string[] | undefined) {
  while (ctx && (ignore?.includes(ctx.name ?? '') || !ctx.has(key))) ctx = ctx.parent
  return ctx?.get(key)
}

export function useJinjaVariable(key: string, ignore?: string[]) {
  const ctx = useJinjaContext()
  return useMemo(() => getVariable(ctx, key, ignore), [ctx, ignore?.join(',')])
}

export function useJinjaVariables(keys: string[], ignore?: string[]) {
  const ctx = useJinjaContext()
  return useMemo(() => keys.map(key => getVariable(ctx, key, ignore)), [ctx, ignore?.join(',')])
}

export function useAllJinjaVariables(ignore?: string[]) {
  const ctx = useJinjaContext()
  return useMemo(() => ctx?.getAll(ignore), [ctx, ignore?.join(',')])
}

export function useJinjaExpressions(expressions: JinjaExpression[], ignore?: string[]) {
  const [results, setResults] = useState<any[]>()
  const variables = useAllJinjaVariables(ignore)
  useEffect(() => {
    (async () => {
      const result = await Promise.all(expressions.map(e => runJinja(e, variables)))
      setResults(result)
    })()
  }, [])
  return results
}

export function useJinja(expression: JinjaExpression, ignore?: string[]) {
  return useJinjaExpressions([expression], ignore)?.[0]
}
