import React, { useState, useEffect, useContext, useCallback } from 'react'
import { Form, message } from 'antd'
import { ApiServerContext } from '../context/ApiServerContext'
import { AuthContext } from '../context/AuthContext'
import PropTypes from 'prop-types'
import DeleteObjectModal from './DeleteObjectModal'
import merge from 'lodash/merge'

/**
 * EditObjectForm is a reusable form component for editing any object type.
 * It handles fetching, updating, locking, unlocking, and validation logic.
 *
 * Props:
 * - id: string (required)
 * - type: string (required)
 * - formItems: array (for ObjectInfo/ObjectProperty items)
 * - children: function({
 *     loading, isEditing, startEditing, cancelEditing, handleUpdate, form, formValid, objectData, setIsEditing, setObjectData
 *   }) => ReactNode
 */
const EditObjectForm = ({ id, type, style, children }) => {
  const [objectData, setObjectData] = useState(null)
  const [serverObjectData, setServerObjectData] = useState(null)
  const [fetchLoading, setFetchLoading] = useState(true)
  const [editLoading, setEditLoading] = useState(false)
  const [lock, setLock] = useState({})
  const [initialized, setInitialized] = useState(false)
  const [isEditing, setIsEditing] = useState(false)
  const [formValid, setFormValid] = useState(false)
  const [form] = Form.useForm()
  const formUpdateValues = Form.useWatch([], form)
  const [messageApi, contextHolder] = message.useMessage()
  const [deleteModalOpen, setDeleteModalOpen] = useState(false)
  const [deleteLoading, setDeleteLoading] = useState(false)
  const {
    fetchObject,
    updateObject,
    deleteObject,
    lockObject,
    unlockObject,
    fetchObjectLock,
    showError,
    connected,
    subscribeToObject,
    subscribeToLock
  } = useContext(ApiServerContext)
  const { token } = useContext(AuthContext)
  // Validate form on change
  useEffect(() => {
    form
      .validateFields({ validateOnly: true })
      .then(() => setFormValid(true))
      .catch(() => setFormValid(false))
  }, [form, formUpdateValues])

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      if (id) {
        unlockObject(id, type)
      }
    }
  }, [id, type, unlockObject])

  const handleFetchObject = useCallback(async () => {
    try {
      setFetchLoading(true)
      const data = await fetchObject(id, type)
      const lockEvent = await fetchObjectLock(id, type)
      setLock(lockEvent)
      setObjectData(data)
      setServerObjectData(data)
      form.setFieldsValue(data)
      setFetchLoading(false)
    } catch (err) {
      messageApi.error('Failed to fetch object info')
      showError(
        `Failed to fetch object information. Message: ${err.message}. Code: ${err.code}`,
        fetchObject
      )
    }
  }, [fetchObject, fetchObjectLock, id, type, form, messageApi, showError])

  // Update event handler
  const updateObjectEventHandler = useCallback((value) => {
    setObjectData((prev) => merge({}, prev, value))
  }, [])

  // Update event handler
  const updateLockEventHandler = useCallback((value) => {
    setLock((prev) => ({ ...prev, ...value }))
  }, [])

  useEffect(() => {
    if (!initialized && id && token != null) {
      setInitialized(true)
      handleFetchObject()
    }
  }, [id, initialized, handleFetchObject, token])

  useEffect(() => {
    if (id && connected) {
      const objectUnsubscribe = subscribeToObject(
        id,
        type,
        updateObjectEventHandler
      )
      const lockUnsubscribe = subscribeToLock(id, type, updateLockEventHandler)
      return () => {
        if (objectUnsubscribe) objectUnsubscribe()
        if (lockUnsubscribe) lockUnsubscribe()
      }
    }
  }, [
    id,
    type,
    subscribeToObject,
    subscribeToLock,
    updateObjectEventHandler,
    connected,
    updateLockEventHandler
  ])

  const startEditing = () => {
    setIsEditing(true)
    lockObject(id, type)
  }

  const cancelEditing = () => {
    if (serverObjectData) {
      form.setFieldsValue(serverObjectData)
      setObjectData(serverObjectData)
    }
    setIsEditing(false)
    unlockObject(id, type)
  }

  const handleUpdate = async () => {
    try {
      const value = await form.validateFields()
      setEditLoading(true)
      await updateObject(id, type, value)
      setObjectData({ ...objectData, ...value })
      setIsEditing(false)
      messageApi.success('Information updated successfully')
    } catch (err) {
      if (err.errorFields) {
        return
      }
      messageApi.error('Failed to update information')
      showError(
        `Failed to update information. Message: ${err.message}. Code: ${err.code}`,
        () => handleUpdate()
      )
    } finally {
      handleFetchObject()
      setEditLoading(false)
    }
  }

  const handleDelete = () => {
    setDeleteModalOpen(true)
  }

  const confirmDelete = async () => {
    setDeleteLoading(true)
    try {
      await deleteObject(id, type)
      setDeleteModalOpen(false)
      messageApi.success('Deleted successfully')
      // Optionally: trigger a callback to parent to remove this object from view
    } catch (err) {
      messageApi.error('Failed to delete')
      showError(
        `Failed to delete. Message: ${err.message}. Code: ${err.code}`,
        confirmDelete
      )
    } finally {
      setDeleteLoading(false)
    }
  }

  return (
    <>
      <DeleteObjectModal
        open={deleteModalOpen}
        onOk={confirmDelete}
        onCancel={() => setDeleteModalOpen(false)}
        loading={deleteLoading}
        objectType={type}
        objectName={objectData?.name || objectData?.label || ''}
      />
      <Form
        form={form}
        layout='vertical'
        style={style}
        onValuesChange={(values) => {
          setObjectData((prev) => ({ ...prev, ...values }))
        }}
      >
        {contextHolder}
        {children({
          loading: fetchLoading,
          isEditing,
          startEditing,
          cancelEditing,
          handleUpdate,
          form,
          formValid,
          objectData,
          setIsEditing,
          setObjectData,
          editLoading,
          lock,
          handleFetchObject,
          handleDelete
        })}
      </Form>
    </>
  )
}

EditObjectForm.propTypes = {
  id: PropTypes.string.isRequired,
  type: PropTypes.string.isRequired,
  children: PropTypes.func.isRequired,
  style: PropTypes.object
}

export default EditObjectForm
