import { Spin, Descriptions, Flex } from 'antd'
import { useState, useEffect } from 'react'
import { LoadingOutlined } from '@ant-design/icons'
import PropTypes from 'prop-types'
import ObjectProperty from './ObjectProperty'
import { getModelProperties } from '../../../database/ObjectModels'
import mergeWith from 'lodash/mergeWith'

const arrayReplaceCustomizer = (objValue, srcValue) => {
  if (Array.isArray(srcValue)) {
    return srcValue
  }
}

const ObjectInfo = ({
  loading = false,
  isEditing = false,
  type = 'unknown',
  showHyperlink,
  showLabels = true,
  objectData = null,
  parentData = null,
  properties = [],
  propertyDefinitions = null,
  onPropertyChange = null,
  required = undefined,
  visibleProperties = {},
  objectPropertyProps = {},
  labelWidth = '150px',
  column = {
    xs: 1,
    sm: 1,
    md: 1,
    lg: 2,
    xl: 2,
    xxl: 2
  },
  ...rest
}) => {
  const allItems = getModelProperties(type)

  const [combinedObjectData, setCombinedObjectData] = useState(objectData)

  useEffect(() => {
    setCombinedObjectData((prev) =>
      mergeWith({}, prev, objectData, arrayReplaceCustomizer)
    )
  }, [objectData])

  // If properties array is empty, show all properties
  // Otherwise, filter and order by the properties array
  let items
  if (Array.isArray(propertyDefinitions) && propertyDefinitions.length > 0) {
    items = propertyDefinitions
  } else if (properties.length === 0) {
    items = allItems
  } else {
    items = properties
      .map((propName) => allItems.find((item) => item.name === propName))
      .filter(Boolean)
  }

  if (required != undefined) {
    items = items.filter((item) => item.required === required)
  }

  if (showHyperlink) {
    objectPropertyProps = { ...objectPropertyProps, showHyperlink }
  }
  // Filter items based on visibleProperties
  // If all values in visibleProperties are true, use whitelist mode
  // Otherwise, use blacklist mode (hide properties set to false)
  const visibleValues = Object.values(visibleProperties)
  const isWhitelistMode =
    visibleValues.length > 0 && visibleValues.every((value) => value === true)

  items = items.filter((item) => {
    const propertyName = item.name
    // Support property.visible as a function (objectData) => boolean
    if (typeof item.visible === 'function') {
      const visible = item.visible(objectData || combinedObjectData || {})
      if (!visible) return false
    }
    if (isWhitelistMode) {
      // Whitelist mode: only show properties that are explicitly set to true
      return visibleProperties[propertyName] === true
    } else {
      // Blacklist mode: hide properties that are explicitly set to false
      return !(
        propertyName in visibleProperties &&
        visibleProperties[propertyName] === false
      )
    }
  })

  const isControlled = typeof onPropertyChange === 'function'

  // Map items to Descriptions 'items' prop format
  const descriptionItems = items.map((item, idx) => {
    const key = item.name || item.label || idx
    return {
      key,
      label:
        showLabels == true ? (
          <Flex vertical style={{ height: '100%' }} justify='center'>
            {item.label}
          </Flex>
        ) : null,
      children: (
        <div style={{ maxWidth: '100%', minWidth: 0, width: '100%' }}>
          <ObjectProperty
            {...item}
            {...objectPropertyProps}
            isEditing={isEditing}
            objectData={combinedObjectData}
            parentData={parentData}
            showSince={true}
            useFormItem={isControlled ? false : objectPropertyProps.useFormItem}
            value={isControlled ? combinedObjectData?.[item.name] : undefined}
            modelType={type}
            onChange={
              isControlled
                ? (newVal) => onPropertyChange(item.name, newVal)
                : objectPropertyProps.onChange
            }
          />
        </div>
      ),
      span: item?.span || undefined
    }
  })

  return (
    <Spin spinning={loading} indicator={<LoadingOutlined />}>
      <Descriptions
        column={column}
        bordered={true}
        {...rest}
        items={descriptionItems}
        styles={{ label: { width: labelWidth } }}
        className='object-info-descriptions'
      />
    </Spin>
  )
}

ObjectInfo.propTypes = {
  loading: PropTypes.bool,
  column: PropTypes.object,
  showHyperlink: PropTypes.bool,
  showLabels: PropTypes.bool,
  indicator: PropTypes.node,
  properties: PropTypes.array,
  bordered: PropTypes.bool,
  items: PropTypes.arrayOf(PropTypes.object),
  isEditing: PropTypes.bool,
  type: PropTypes.string,
  objectData: PropTypes.object,
  parentData: PropTypes.object,
  propertyDefinitions: PropTypes.array,
  onPropertyChange: PropTypes.func,
  required: PropTypes.bool,
  visibleProperties: PropTypes.object,
  objectPropertyProps: PropTypes.object,
  labelWidth: PropTypes.string
}

export default ObjectInfo
