import { buildFields } from './field-builder'
import { FormContextValue } from './FormContextValue'
import { FormRepeatableVariable } from './FormVariable'
import { FieldListener, FormConfig, FormModel, GenericFieldConfig, RenderedFieldComponent } from './types'

export class JinjaFormModel implements FormModel {
  private config: FormConfig
  readonly fields: RenderedFieldComponent[]
  readonly name: string
  readonly context: FormContextValue

  constructor(config: FormConfig) {
    this.name = config.name
    this.config = config
    this.context = new FormContextValue(config.name, config.fields, config.computed, config.parent)
    this.fields = buildFields(this.context.variables)
  }

  get(name: string): any {
    return this.context.findByKey(name)?.get()
  }

  async getResolved(name: string): Promise<any> {
    const variable = this.context.findByKey(name)
    return this.context.getResolvedVariable(variable)
  }

  set(name: string, value: any): void {
    const variable = this.context.findByKey(name)
    variable?.set(value)
    variable?.patchState({ touched: true })
  }

  getAll(): Record<string, any> {
    return this.context.getAllInContext()
  }

  async getAllResolved(): Promise<any> {
    const result: Record<string, any> = {}
    for (const key in this.context.variables) {
      result[key] = await this.getResolved(key)
    }
    return result
  }

  setAll(value: Record<string, any>): void {
    for (const key in value) {
      this.set(key, value[key])
    }
  }

  onChange(name: string, listener: FieldListener): () => void {
    const variable = this.context.findByKey(name)
    if (!variable) return () => {}
    const removeChangeValueListener = variable?.onChange(value => listener({ value, ...variable.getState() }))
    const removeChangeStateListener = variable?.onChangeState(state => listener({ value: variable.get()!, ...state }))
    return () => {
      removeChangeStateListener()
      removeChangeValueListener()
    }
  }

  async submit(): Promise<any> {
    const valid = await this.isValid()
    if (valid) this.config.onSubmit(this)
  }

  isValid() {
    return this.context.isValid()
  }

  reset(value?: Record<string, any>): void {
    if (value) {
      this.config.fields.forEach((field) => {
        (field as GenericFieldConfig).default = value[field.name]
      })
    }
    for (const key in this.context.variables) {
      for (const variable of this.context.variables[key]) {
        variable.set((variable.config as GenericFieldConfig).default)
        variable.patchState({ error: undefined, touched: false })
      }
    }
  }

  isRepeating(name: string): boolean {
    const variable = this.context.findByKey(name)
    return variable instanceof FormRepeatableVariable && variable.isRepeating()
  }
}
