import { mapValues } from 'lodash'
import { ContextValue } from './ContextValue'
import { JinjaExpression, JinjaExpressionVariable, JinjaVariable } from './JinjaVariable'

export interface JinjaContextVariableConfig<Metadata = any> {
  initialValue?: any,
  expression?: JinjaExpression,
  metadata?: Metadata,
}

export interface JinjaContextConfig {
  variables: Record<string, JinjaContextVariableConfig>,
  /**
   * Can be used to identify the context.
   */
  name?: string,
  /**
   * Adds a prefix to every variable in this context. Useful for avoiding name conflicts for different scopes.
   * 
   * @example
   * if prefix is 'account', then the variable at `variables.example` will be referred by the name `account.example` instead of `example`.
   */
  prefix?: string,
  parent?: JinjaContextValue,
}

export class JinjaContextValue extends ContextValue {
  readonly prefix?: string
  readonly variables: Record<string, JinjaVariable> = {}
  readonly parent?: JinjaContextValue | undefined

  constructor({ name, prefix, variables, parent }: JinjaContextConfig) {
    super(name)
    this.parent = parent
    this.prefix = prefix
    if (!variables) return
    Object.keys(variables).forEach((key) => {
      const { initialValue, expression, metadata } = variables[key]
      this.variables[key] = expression
        ? new JinjaExpressionVariable(key, expression, prefix)
        : new JinjaVariable(key, initialValue, prefix, metadata)
    })
  }

  protected findVariableByKey(key: string): JinjaVariable {
    return this.variables[key]
  }

  getVariableInContext(key: string): JinjaVariable | undefined {
    return this.findVariableByKey(key)
  }

  getAllInContext(): any {
    return mapValues(this.variables, v => v.get())
  }

  protected override addVariablesToContext(variables: JinjaVariable[]): void {
    for (const variable of variables) {
      this.variables[variable.name] = variable
    }
  }

  forEachVariable(iteratee: (v: JinjaVariable) => void): void {
    Object.values(this.variables).forEach(iteratee)
  }
}
