import { useQuery } from '@based/react'
import {
  getPropType,
  SchemaEnum,
  SchemaObject,
  SchemaProps,
  SchemaReference,
  SchemaReferences,
  SchemaSet,
  SchemaString,
  SchemaTimestamp,
  SchemaTypeName,
} from '@based/schema'
import { Form, type FormFieldsType, type FormProps } from '../Form/Form.js'
import { useMemo } from 'react'
import { Badge } from '../Badge/index.js'
import { expectNever } from '../../utils/misc.js'
import { FormFields } from '../Form/FormFields.js'
import { FormTabs } from '../Form/FormTabs.js'

function generateFormFields(props: SchemaProps) {
  const res: FormFieldsType = {}

  for (const rawKey in props) {
    const key = rawKey as SchemaTypeName
    const field = props[key]
    const fieldType = getPropType(field)

    switch (fieldType) {
      case 'binary': {
        res[key] = {
          type: 'text',
          label: key.charAt(0).toUpperCase() + key.slice(1),
          disabled: true,
        }
        continue
      }
      case 'boolean': {
        res[key] = {
          type: 'switch',
          label: key.charAt(0).toUpperCase() + key.slice(1),
        }
        continue
      }
      case 'enum': {
        const enumField = field as SchemaEnum
        res[key] = {
          type: 'select',
          options: enumField.enum.map((e) => ({
            value: e,
            label: <Badge color="neutral-subtle">{e.toString()}</Badge>,
          })),
          label: key.charAt(0).toUpperCase() + key.slice(1),
        }
        continue
      }

      case 'object': {
        const objectField = field as SchemaObject
        res[key] = generateFormFields(objectField.props)
        continue
      }
      case 'reference': {
        const referenceField = field as SchemaReference
        res[key] = {
          type: 'reference',
          variant: 'single',
          ref: referenceField.ref as SchemaTypeName,
          label: key.charAt(0).toUpperCase() + key.slice(1),
        }
        continue
      }
      case 'references': {
        const referencesFields = field as SchemaReferences
        res[key] = {
          type: 'reference',
          variant: 'multiple',
          ref: referencesFields.items.ref as SchemaTypeName,
          label: key.charAt(0).toUpperCase() + key.slice(1),
        }
        continue
      }
      case 'set': {
        const setField = field as SchemaSet
        // TODO fix typescript
        const itemsType = (setField.items as any).type

        res[key] = {
          type: 'tag',
          variant: itemsType,
          label: key.charAt(0).toUpperCase() + key.slice(1),
        }

        if (itemsType === 'enum') {
          // TODO fix typescript
          const enumOptions = (setField.items as any).enum
          res[key].options = enumOptions.map((e: any) => ({
            value: e,
            label: <Badge color="neutral-subtle">{e.toString()}</Badge>,
          }))
        }

        continue
      }
      case 'string': {
        const stringField = field as SchemaString

        if (key === 'id') {
          res[key] = {
            type: 'text',
            label: 'ID',
            disabled: true,
          }
          continue
        }

        if (stringField.format === 'multiline') {
          res[key] = {
            type: 'textarea',
            label: key.charAt(0).toUpperCase() + key.slice(1),
          }
          continue
        }

        if (stringField.format === 'html') {
          res[key] = {
            type: 'richtext',
            label: key.charAt(0).toUpperCase() + key.slice(1),
          }
          continue
        }

        res[key] = {
          type: 'text',
          label: key.charAt(0).toUpperCase() + key.slice(1),
        }
        continue
      }
      case 'text': {
        res[key] = {
          type: 'text',
          label: key.charAt(0).toUpperCase() + key.slice(1),
        }
        continue
      }
      case 'timestamp': {
        const timestampField = field as SchemaTimestamp
        res[key] = {
          type: 'datetime',
          label: key.charAt(0).toUpperCase() + key.slice(1),
          disabled: Boolean(timestampField.on),
        }
        continue
      }
      case 'number':
      case 'int16':
      case 'int32':
      case 'int8':
      case 'uint16':
      case 'uint32':
      case 'uint8': {
        res[key] = {
          type: 'number',
          label: key.charAt(0).toUpperCase() + key.slice(1),
        }
        continue
      }
      case 'alias':
      case 'hll':
        continue

      default:
        expectNever(fieldType)
        throw new Error('unknown field type')
    }
  }

  return res
}

type BasedFormProps = {
  type?: SchemaTypeName
  id?: string
  transformFields?: (generatedFields: FormFieldsType) => FormFieldsType
} & Omit<FormProps, 'fields'>

function BasedForm({
  type,
  id,
  transformFields,
  ...regularFormProps
}: BasedFormProps) {
  const { data: initialData, checksum: initialDataChecksum } = useQuery(
    id ? (regularFormProps?.endpoints?.query ?? 'db:get') : null,
    { $id: id },
  )
  const { data: schema } = useQuery(
    regularFormProps?.endpoints?.schema ?? 'db:schema',
  )
  const fields = useMemo(() => {
    if (!schema) return

    const generated = generateFormFields(schema.types[type].props)

    if (!transformFields) {
      return generated
    }

    return transformFields(generated)
  }, [schema, type, transformFields])

  if (!fields) return null

  return (
    <Form
      fields={fields}
      initialValues={initialData}
      initialValuesChecksum={initialDataChecksum}
      {...regularFormProps}
    />
  )
}

BasedForm.Fields = FormFields
BasedForm.Tabs = FormTabs

export { BasedForm }
export type { BasedFormProps }
