import PropTypes from 'prop-types'
import {
  Typography,
  Badge,
  Input,
  InputNumber,
  Form,
  Select,
  Switch
} from 'antd'
import IdDisplay from './IdDisplay'
import TimeDisplay from './TimeDisplay'
import TimeEdit from './TimeEdit'
import dayjs from 'dayjs'
import EmailDisplay from './EmailDisplay'
import UrlDisplay from './UrlDisplay'
import CountryDisplay from './CountryDisplay'
import CountrySelect from './CountrySelect'
import TagsDisplay from './TagsDisplay'
import TagsInput from './TagsInput'
import BoolDisplay from './BoolDisplay'
import ColorSelector from './ColorSelector'
import SecretDisplay from './SecretDisplay'
import EyeIcon from '../../Icons/EyeIcon'
import EyeSlashIcon from '../../Icons/EyeSlashIcon'
import { getPropertyValue } from '../../../database/ObjectModels'
import PropertyChanges from './PropertyChanges'
import NetGrossDisplay from './NetGrossDisplay'
import NetGrossInput from './NetGrossInput'
import MinMaxDisplay from './MinMaxDisplay'
import MinMaxInput from './MinMaxInput'
import InputNumberCal from './InputNumberCal'
import ObjectList from './ObjectList'
import VarianceDisplay from './VarianceDisplay'
import OperationDisplay from './OperationDisplay'
import MarkdownDisplay from './MarkdownDisplay'
import MarkdownInput from './MarkdownInput'
import ObjectSelect from './ObjectSelect'
import ObjectDisplay from './ObjectDisplay'
import ObjectTypeSelect from './ObjectTypeSelect'
import ObjectTypeDisplay from './ObjectTypeDisplay'
import CodeBlockEditor from './CodeBlockEditor'
import CustomSelect from './CustomSelect'
import StateDisplay from './StateDisplay'
import AlertsDisplay from './AlertsDisplay'
import FileUpload from './FileUpload'
import DataTree from './DataTree'
import FileList from './FileList'
import ObjectChildTable from './ObjectChildTable'
import MiscId from './MiscId'
import AddressDisplay from './AddressDisplay'
import ArrayDisplay from './ArrayDisplay'
import ArrayInput from './ArrayInput'
import { round } from '../utils/Utils'

const { Text } = Typography

const getTextValue = (value) => {
  if (value == null || typeof value === 'string' || typeof value === 'number') {
    return value
  }

  if (typeof value === 'object') {
    if (typeof value._id === 'string') {
      return value._id
    }
    if (typeof value._id?._id === 'string') {
      return value._id._id
    }

    try {
      return JSON.stringify(value)
    } catch {
      return String(value)
    }
  }

  return String(value)
}

const timeEditFormItemProps = {
  getValueProps: (v) => ({ value: v ? dayjs(v) : null })
}

const ObjectProperty = ({
  modelType = 'unknown',
  type = 'text',
  prefix,
  size,
  suffix,
  value,
  min,
  max,
  difference,
  step,
  isEditing = false,
  formItemProps = {},
  required = false,
  name,
  inTable = false,
  label,
  showLabel = false,
  masterFilter = {},
  language = '',
  objectData = null,
  parentData = null,
  objectType = 'unknown',
  readOnly = false,
  disabled = false,
  empty = false,
  initial = false,
  height = 'auto',
  minimal = false,
  autoCompleteObject = null,
  previewOpen = false,
  showPreview = true,
  useFormItem = true,
  options = [],
  roundNumber = false,
  fixedNumber = false,
  canAddRemove = true,
  showHyperlink,
  showSince,
  properties = [],
  onChange = null,
  maxWidth = '100%',
  loading = false,
  rollups = [],
  showCard = true,
  showDownload = true,
  columns = [],
  style = {},
  hiddenPropertyWidth = '100px',
  ...rest
}) => {
  if (value && typeof value == 'function' && objectData) {
    value = value(objectData, parentData)
  }

  if (max && typeof max == 'function' && objectData) {
    max = max(objectData, parentData)
  }

  if (min && typeof min == 'function' && objectData) {
    min = min(objectData, parentData)
  }

  if (objectType && typeof objectType == 'function' && objectData) {
    objectType = objectType(objectData, parentData)
  }

  if (disabled && typeof disabled == 'function' && objectData) {
    disabled = disabled(objectData, parentData)
  }

  if (empty && typeof empty == 'function' && objectData) {
    empty = empty(objectData, parentData)
  }

  if (difference && typeof difference == 'function' && objectData) {
    difference = difference(objectData, parentData)
  }

  if (prefix && typeof prefix == 'function' && objectData) {
    prefix = prefix(objectData, parentData)
  }

  if (suffix && typeof suffix == 'function' && objectData) {
    suffix = suffix(objectData, parentData)
  }

  if (masterFilter && typeof masterFilter == 'function' && objectData) {
    masterFilter = masterFilter(objectData, parentData)
  }

  if (options && typeof options == 'function' && objectData) {
    options = options(objectData, parentData)
  }

  if (readOnly && typeof readOnly == 'function' && objectData) {
    readOnly = readOnly(objectData, parentData)
  }

  if (type && typeof type == 'function' && objectData) {
    type = type(objectData, parentData)
  }

  if (columns && typeof columns == 'function' && objectData) {
    columns = columns(objectData, parentData)
  }

  if (value == null) {
    value = getPropertyValue(objectData, name)
  }

  // Split the name by "." to handle nested object properties
  var formItemName = name

  if (name?.includes('.')) {
    formItemName = name ? name.split('.') : undefined
  }
  // For state type with options (editable), form field is state.type
  if (type === 'state' && options?.length && !readOnly) {
    formItemName = ['state', 'type']
  }

  var textParams = { style: { whiteSpace: 'nowrap', minWidth: '0' } }

  if (disabled == true) {
    textParams = { ...textParams, delete: true, type: 'secondary' }
  }

  const renderProperty = () => {
    if (empty == true) {
      return <Text type='secondary'>n/a</Text>
    }
    if (!isEditing || (readOnly && !initial)) {
      switch (type) {
        case 'netGross':
          return (
            <NetGrossDisplay value={value} prefix={prefix} suffix={suffix} />
          )
        case 'minMax':
          return (
            <MinMaxDisplay
              value={value}
              prefix={prefix}
              suffix={suffix}
              step={step}
            />
          )
        case 'secret':
          if (value != null) {
            return <SecretDisplay value={value} {...rest} />
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        case 'wsprotocol':
          switch (value) {
            case 'ws':
              return <Text {...textParams}>Websocket</Text>
            case 'wss':
              return <Text {...textParams}>Websocket Secure</Text>
            default:
              return (
                <Text type='secondary' {...textParams}>
                  n/a
                </Text>
              )
          }
        case 'select': {
          if (options && Array.isArray(options)) {
            const selectValue =
              options.find((option) => option.value === value) || 'n/a'
            return <Text {...textParams}>{selectValue.label}</Text>
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        }
        case 'priceMode':
          switch (value) {
            case 'margin':
              return <Text {...textParams}>Margin %</Text>
            case 'amount':
              return <Text {...textParams}>£ Amount</Text>
            default:
              return (
                <Text type='secondary' {...textParams}>
                  n/a
                </Text>
              )
          }
        case 'bool': {
          if (value != null) {
            return <BoolDisplay value={value} yesNo={true} {...rest} />
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        }
        case 'dateTime': {
          if (value != null) {
            return (
              <TimeDisplay dateTime={value} showSince={showSince} {...rest} />
            )
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        }
        case 'country': {
          if (value != null) {
            return <CountryDisplay countryCode={value} />
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        }
        case 'color': {
          if (value) {
            return <Badge color={value} count={value.toUpperCase()} />
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        }
        case 'number': {
          if (value != null) {
            if (Array.isArray(value)) {
              return (
                <Text {...textParams}>
                  {prefix}
                  {value.length}
                  {suffix}
                </Text>
              )
            } else {
              var roundedValue = value
              if (roundNumber != false && typeof value === 'number') {
                roundedValue = round(value, roundNumber)
              }
              if (fixedNumber != false && typeof value === 'number') {
                roundedValue = value.toFixed(fixedNumber)
              }

              return (
                <Text {...textParams}>
                  {prefix}
                  {typeof value === 'number' ? roundedValue : value}
                  {suffix}
                </Text>
              )
            }
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        }
        case 'numberList':
        case 'stringList':
          return <ArrayDisplay value={value} prefix={prefix} suffix={suffix} />
        case 'variance': {
          if (value != null) {
            return (
              <VarianceDisplay value={value} prefix={prefix} suffix={suffix} />
            )
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        }
        case 'text':
          if (value != null && value != '') {
            return (
              <Text ellipsis style={style}>
                {prefix}
                {getTextValue(value)}
                {suffix}
              </Text>
            )
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        case 'codeBlock':
          if (value != null && value != '') {
            return (
              <CodeBlockEditor
                code={value}
                language={language}
                height={height}
                readOnly={true}
                minimal={minimal}
                autoCompleteObject={autoCompleteObject}
              />
            )
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        case 'markdown':
          if (value != null && value != '') {
            return <MarkdownDisplay content={value} />
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        case 'email':
          if (value != null && value != '') {
            return <EmailDisplay email={value} />
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        case 'url':
          if (value != null && value != '') {
            return <UrlDisplay url={value} />
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        case 'object': {
          if (value && value._id) {
            return (
              <ObjectDisplay
                object={value}
                objectType={objectType}
                showHyperlink={showHyperlink}
              />
            )
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        }
        case 'objectType': {
          if (value) {
            return <ObjectTypeDisplay objectType={value} />
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        }
        case 'objectList': {
          return <ObjectList value={value} objectType={objectType} />
        }
        case 'objectChildren': {
          return (
            <ObjectChildTable
              name={name}
              value={value}
              properties={properties}
              objectData={objectData}
              maxWidth={maxWidth}
              loading={loading}
              rollups={rollups}
              size={size}
              hiddenPropertyWidth={hiddenPropertyWidth}
              minimal={minimal}
              label={label}
              columns={columns}
              canAddRemove={canAddRemove}
            />
          )
        }
        case 'state': {
          if (value && value?.type) {
            return <StateDisplay {...rest} state={value} />
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        }
        case 'id': {
          if (value) {
            return (
              <IdDisplay
                id={value}
                type={objectType}
                showHyperlink={showHyperlink}
                {...rest}
              />
            )
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        }
        case 'reference': {
          if (value) {
            return (
              <IdDisplay
                id={value}
                reference={value}
                type={objectType}
                {...rest}
              />
            )
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        }
        case 'miscId': {
          return <MiscId value={value} {...rest} />
        }
        case 'density': {
          if (value != null) {
            return <Text {...textParams}>{`${value} g/cm³`}</Text>
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        }
        case 'alerts': {
          if (value != null && value?.length != 0) {
            return (
              <AlertsDisplay
                object={objectData}
                objectType={objectType}
                showDismiss={false}
                showActions={false}
              />
            )
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        }
        case 'tags': {
          if (value != null || value?.length != 0) {
            return (
              <TagsDisplay
                tags={value}
                propertyName={name}
                scrollHorizontal={inTable}
                objectType={objectType}
              />
            )
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        }
        case 'address': {
          return (
            <AddressDisplay
              value={value}
              isEditing={false}
              disabled={disabled}
            />
          )
        }
        case 'operation': {
          if (value != null) {
            return <OperationDisplay operation={value} />
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        }
        case 'propertyChanges': {
          return <PropertyChanges type={objectType} value={value} />
        }
        case 'data': {
          return <DataTree data={value} />
        }
        case 'file': {
          if (value == null || value?.length == 0 || value == undefined) {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          } else {
            return (
              <FileList
                files={value}
                minimal={minimal}
                multiple={false}
                card={false}
                maxWidth={maxWidth}
                defaultPreviewOpen={previewOpen}
                showPreview={showPreview}
                showInfo={showHyperlink}
                showDownload={showDownload}
                masterFilter={masterFilter}
              />
            )
          }
        }
        case 'fileList': {
          return (
            <FileList
              files={value}
              minimal={minimal}
              multiple={true}
              maxWidth={maxWidth}
              defaultPreviewOpen={previewOpen}
              showPreview={showPreview}
              showInfo={showHyperlink}
              masterFilter={masterFilter}
            />
          )
        }
        default: {
          if (value != null && value !== '') {
            return <Text {...textParams}>{getTextValue(value)}</Text>
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        }
      }
    }

    // Editable mode: wrap in Form.Item
    // Merge required rule if needed
    let mergedFormItemProps = {
      ...formItemProps,
      style: { flexGrow: 1, width: '100%' }
    }
    if (required && disabled == false) {
      let rules
      if (mergedFormItemProps.rules) {
        rules = [...mergedFormItemProps.rules]
      } else {
        rules = []
      }
      const hasRequiredRule = rules.some((rule) => rule && rule.required)
      if (!hasRequiredRule) {
        rules.push({ required: true, message: '' })
      }
      mergedFormItemProps.rules = rules
    }
    if (type === 'minMax' && disabled == false) {
      const rules = [...(mergedFormItemProps.rules || [])]
      rules.push({
        validator: (_, range) => {
          if (range == null) return Promise.resolve()
          if (typeof range !== 'object' || Array.isArray(range)) {
            return Promise.reject(new Error('Value must be a min/max range'))
          }

          const values = [range.min, range.max].filter((item) => item != null)
          const valid = values.every(
            (item) =>
              typeof item === 'number' &&
              Number.isFinite(item) &&
              (min == null || item >= min) &&
              (max == null || item <= max)
          )

          return valid
            ? Promise.resolve()
            : Promise.reject(new Error('Enter valid min and max values'))
        }
      })
      mergedFormItemProps.rules = rules
    }
    if ((type === 'numberList' || type === 'stringList') && disabled == false) {
      const rules = [...(mergedFormItemProps.rules || [])]
      rules.push({
        validator: (_, items) => {
          if (items == null) return Promise.resolve()
          if (!Array.isArray(items)) {
            return Promise.reject(new Error('Value must be a list'))
          }

          const valid = items.every((item) => {
            if (type === 'numberList') {
              return (
                typeof item === 'number' &&
                Number.isFinite(item) &&
                (min == null || item >= min) &&
                (max == null || item <= max)
              )
            }
            return typeof item === 'string' && item.trim().length > 0
          })

          return valid
            ? Promise.resolve()
            : Promise.reject(new Error('Complete or remove empty list items'))
        }
      })
      mergedFormItemProps.rules = rules
    }
    // Remove name from mergedFormItemProps if present
    if (mergedFormItemProps.name) {
      delete mergedFormItemProps.name
    }
    // If label is provided, set it on Form.Item
    if (label && showLabel == true) {
      mergedFormItemProps.label = label
    }
    // Always apply style: { margin: 0 } unless overridden
    mergedFormItemProps.style = {
      margin: 0,
      ...(mergedFormItemProps.style || {})
    }

    if (typeof onChange === 'function') {
      mergedFormItemProps.onChange = onChange
    }

    const inputProps = useFormItem
      ? {}
      : {
          value,
          onChange,
          disabled
        }

    const renderInput = () => {
      switch (type) {
        case 'netGross':
          return (
            <NetGrossInput
              difference={difference}
              prefix={prefix}
              suffix={suffix}
              {...inputProps}
            />
          )
        case 'minMax':
          return (
            <MinMaxInput
              prefix={prefix}
              suffix={suffix}
              min={min}
              max={max}
              step={step}
              {...inputProps}
            />
          )
        case 'secret':
          return (
            <Input.Password
              placeholder={label}
              iconRender={(visible) =>
                visible ? <EyeSlashIcon /> : <EyeIcon />
              }
              {...mergedFormItemProps}
              {...inputProps}
            />
          )
        case 'wsprotocol':
          return (
            <Select
              defaultValue='ws'
              options={[
                { value: 'ws', label: 'Websocket' },
                { value: 'wss', label: 'Websocket Secure' }
              ]}
              {...inputProps}
            />
          )
        case 'select':
          return (
            <CustomSelect
              placeholder={'Select a ' + label.toLowerCase() + '...'}
              options={Array.isArray(options) ? options : []}
              {...inputProps}
            />
          )
        case 'state':
          if (options?.length && !readOnly) {
            return (
              <CustomSelect
                placeholder={'Select a ' + label.toLowerCase() + '...'}
                options={Array.isArray(options) ? options : []}
                {...inputProps}
                {...(useFormItem ? {} : { value: value?.type })}
              />
            )
          }
          return null
        case 'priceMode':
          return (
            <Select
              defaultValue='margin'
              options={[
                { value: 'margin', label: 'Margin %' },
                { value: 'amount', label: '£ Amount' }
              ]}
              {...inputProps}
            />
          )
        case 'bool':
          return (
            <Switch
              {...inputProps}
              {...(useFormItem ? {} : { checked: value })}
            />
          )
        case 'dateTime':
          return <TimeEdit {...inputProps} />
        case 'country':
          return <CountrySelect {...inputProps} />
        case 'color':
          return (
            <ColorSelector
              required={required}
              {...inputProps}
              {...(useFormItem ? {} : { value })}
            />
          )
        case 'weight':
          return (
            <InputNumber
              suffix='g'
              style={{ width: '100%' }}
              placeholder={label}
              {...inputProps}
            />
          )
        case 'number':
          return (
            <InputNumberCal
              placeholder={label}
              prefix={prefix}
              suffix={suffix}
              min={min}
              max={max}
              step={step}
              style={{ width: '100%' }}
              {...inputProps}
            />
          )
        case 'numberList':
          return (
            <ArrayInput
              numeric
              min={min}
              max={max}
              step={step}
              placeholder={label}
              disabled={disabled}
              {...inputProps}
            />
          )
        case 'stringList':
          return (
            <ArrayInput
              placeholder={label}
              disabled={disabled}
              {...inputProps}
            />
          )
        case 'text':
          return <Input placeholder={label} {...inputProps} />
        case 'codeBlock':
          return (
            <CodeBlockEditor
              code={value}
              language={language}
              height={height}
              minimal={minimal}
              autoCompleteObject={autoCompleteObject}
              {...inputProps}
            />
          )
        case 'markdown':
          return (
            <MarkdownInput {...inputProps} size={size} showCard={showCard} />
          )
        case 'id':
          // id is not editable, just show view mode
          if (value) {
            return <IdDisplay id={value} type={objectType} {...rest} />
          } else {
            return (
              <Text type='secondary' {...textParams}>
                n/a
              </Text>
            )
          }
        case 'object':
          return (
            <ObjectSelect
              type={objectType}
              masterFilter={masterFilter}
              {...inputProps}
            />
          )
        case 'objectType':
          return (
            <ObjectTypeSelect masterFilter={masterFilter} {...inputProps} />
          )
        case 'objectList':
          return <ObjectSelect type={objectType} multiple {...inputProps} />
        case 'tags':
          return (
            <TagsInput
              propertyName={name}
              modelType={modelType}
              disabled={disabled}
              placeholder={label}
              {...inputProps}
            />
          )
        case 'address':
          return (
            <AddressDisplay
              value={value}
              isEditing={true}
              onChange={onChange}
              disabled={disabled}
              {...inputProps}
            />
          )
        case 'file':
          return (
            <FileUpload
              multiple={false}
              minimal={minimal}
              defaultPreviewOpen={previewOpen}
              showPreview={showPreview}
              showInfo={showHyperlink}
              masterFilter={masterFilter}
              {...inputProps}
            />
          )
        case 'fileList':
          return (
            <FileUpload
              multiple={true}
              minimal={minimal}
              defaultPreviewOpen={previewOpen}
              showPreview={showPreview}
              showInfo={showHyperlink}
              masterFilter={masterFilter}
              {...inputProps}
            />
          )
        case 'objectChildren': {
          return (
            <ObjectChildTable
              name={name}
              properties={properties}
              columns={columns}
              objectData={objectData}
              isEditing={true}
              rollups={rollups}
              hiddenPropertyWidth={hiddenPropertyWidth}
              size={size}
              canAddRemove={canAddRemove}
              {...inputProps}
            />
          )
        }
        default:
          return <Input placeholder={label} {...inputProps} />
      }
    }

    if (!useFormItem) {
      return renderInput()
    }

    return (
      <Form.Item
        name={formItemName}
        {...mergedFormItemProps}
        {...(type === 'bool' ? { valuePropName: 'checked' } : {})}
        {...(type === 'color'
          ? { valuePropName: 'value', getValueFromEvent: (v) => v }
          : {})}
        {...(type === 'dateTime' ? timeEditFormItemProps : {})}
      >
        {renderInput()}
      </Form.Item>
    )
  }

  const property = renderProperty()
  // Render the property directly (remove useDescriptions functionality)
  return property
}

ObjectProperty.propTypes = {
  type: PropTypes.string.isRequired,
  modelType: PropTypes.string,
  value: PropTypes.oneOfType([PropTypes.any, PropTypes.func]),
  isEditing: PropTypes.bool,
  formItemProps: PropTypes.object,
  masterFilter: PropTypes.object,
  required: PropTypes.bool,
  name: PropTypes.string,
  language: PropTypes.string,
  autoCompleteObject: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
  prefix: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
  suffix: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
  min: PropTypes.number,
  max: PropTypes.number,
  step: PropTypes.number,
  showLabel: PropTypes.bool,
  objectType: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
  readOnly: PropTypes.bool,
  disabled: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
  empty: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
  difference: PropTypes.oneOfType([PropTypes.any, PropTypes.func]),
  objectData: PropTypes.object,
  height: PropTypes.string,
  previewOpen: PropTypes.bool,
  showPreview: PropTypes.bool,
  useFormItem: PropTypes.bool,
  showHyperlink: PropTypes.bool,
  options: PropTypes.array,
  showSince: PropTypes.bool,
  inTable: PropTypes.bool,
  loading: PropTypes.bool,
  rollups: PropTypes.arrayOf(PropTypes.object),
  canAddRemove: PropTypes.bool,
  showCard: PropTypes.bool,
  showDownload: PropTypes.bool,
  style: PropTypes.object,
  hiddenPropertyWidth: PropTypes.string
}

export default ObjectProperty
