import { Flex, IconBox, Switch, Text } from '@citric/core'
import { TimesCircle } from '@citric/icons'
import { listToClass } from '@stack-spot/portal-theme'
import { useTranslate } from '@stack-spot/portal-translate'
import { map } from 'lodash'
import { useCallback } from 'react'
import { JinjaRawContext, useJinjaContext } from '../context/JinjaContext'
import { errorDictionary } from '../dynamic/error-dictionary'
import { FormContextValue } from './FormContextValue'
import { FormObjectVariable, FormRepeatableVariable, FormVariable } from './FormVariable'
import { useFieldEffect, useFieldState, useFieldValue, useIsRepeating } from './hooks'
import { RenderedFieldComponent, StaticFieldConfig, VariableFieldConfig } from './types'

interface RenderedVariableProps {
  variable: FormVariable,
  id: string,
  renderLabel?: boolean,
  context?: FormContextValue,
  props: any,
}

interface RepeatableVariableProps extends Omit<RenderedVariableProps, 'context'> {
  variable: FormRepeatableVariable,
}

interface RenderedInputProps {
  variable: FormVariable,
  props: any,
  disabled?: boolean,
  id: string,
}

const RenderedInput = ({ variable, disabled, id, props }: RenderedInputProps) => {
  const value = useFieldValue(variable)
  const config = variable.config as StaticFieldConfig | VariableFieldConfig
  const context = useJinjaContext()
  if (!(context instanceof FormContextValue)) throw new Error("Can't use a RenderedInput without a Form component")

  const onChange = useCallback(async (newValue: any) => {
    variable.set(newValue)
    if (!variable.getState().touched || !config.validation) return
    variable.isValid(context as FormContextValue)
  }, [])

  const onBlur = useCallback(async () => {
    if (variable.getState().touched) return
    variable.isValid(context as FormContextValue)
    variable.patchState({ touched: true })
  }, [])

  return <config.component
    {...props}
    id={id}
    name={config.name}
    value={value}
    onChange={onChange}
    disabled={disabled}
    onBlur={onBlur}
    variable={variable}
  />
}

const RenderedRepeatableVariable = ({ variable, id, props }: RepeatableVariableProps) => {
  const repeating = useIsRepeating(variable)

  return (
    <div className="repeatable-field">
      <div className="field-header">
        <Flex alignItems="center" justifyContent="space-between">
          <Text>{variable.config.label}</Text>
          <label>
            <Text>Repeat</Text>
            <Switch checked={repeating} onChange={() => variable.setRepeating(!repeating)} />
          </label>
        </Flex>
        {'help' in variable.config && <Text className="help" appearance="microtext1" color="light.700">{variable.config.help}</Text>}
      </div>
      {repeating
        ? props.children
        : <RenderedVariable variable={variable.condensed} id={id} renderLabel={false} props={props} />}
    </div>
  )
}

const RenderedVariable = (allProps: RenderedVariableProps) => {
  const { variable, id, renderLabel = !!variable.config.label, context, props: { children, ...props } } = allProps
  const { disabled, visible, error } = useFieldState(variable)
  const t = useTranslate(errorDictionary)
  useFieldEffect(variable)
  if (!visible) return

  const fieldHeader = (
    <div className="field-header">
      <Text>{variable.config.label}</Text>
      {'help' in variable.config && <Text className="help" appearance="microtext1" colorScheme="light.700">{variable.config.help}</Text>}
    </div>
  )

  function wrap(element: React.ReactNode) {
    return context ? <JinjaRawContext value={context as any}>{element}</JinjaRawContext> : element
  }

  if (variable instanceof FormRepeatableVariable) {
    return wrap(<RenderedRepeatableVariable variable={variable} id={id} props={{ ...props, children }} />)
  }

  // Object fields
  if (variable instanceof FormObjectVariable) {
    return wrap(
      <div className="field-object-group">
        {renderLabel && fieldHeader}
        {children}
      </div>,
    )
  }

  const input = (
    <RenderedInput id={id} variable={variable} props={props} disabled={disabled} />
  )

  // Common fields
  return wrap(
    <div className={listToClass(['field', error && 'error', disabled && 'disabled'])}>
      {renderLabel ? <label>{fieldHeader}{input}</label> : input}
      {error && (
        <div className="error-message">
          <IconBox className="icon"><TimesCircle /></IconBox>
          <Text>{(t as any)[error] || error}</Text>
        </div>
      )}
    </div>,
  )
}

export function buildFields(variables: Record<string, FormVariable[]>, context?: FormContextValue): RenderedFieldComponent[] {
  return map(variables, (variableList, key) => variableList.map((variable, index) => {
    const component: RenderedFieldComponent = (
      props: any,
    ) => <RenderedVariable id={`${key}-${index}`} key={`${key}-${index}`} variable={variable} context={context} props={props} />
    component.variable = variable
    if (variable instanceof FormObjectVariable) {
      component.fields = buildFields(variable.children)
    } else if (variable instanceof FormRepeatableVariable) {
      if (variable.condensed instanceof FormObjectVariable) component.fields = buildFields(variable.condensed.children)
      component.repeat = Object.keys(variable.expanded).map(
        key => buildFields(variable.expanded[key].variables, variable.expanded[key]),
      ).flat()
    }
    return component
  })).flat()
}
