import { useState, useContext, useEffect } from 'react'
import { Form, Flex, Descriptions, Alert } from 'antd'
import PropTypes from 'prop-types'

import PrinterTemperaturePanel from '../../common/PrinterTemperaturePanel'

import { LoadingOutlined } from '@ant-design/icons'
import ObjectSelect from '../../common/ObjectSelect'
import ObjectDisplay from '../../common/ObjectDisplay'
import WizardView from '../../common/WizardView'
import { ApiServerContext } from '../../context/ApiServerContext'

const LoadFilamentStock = ({
  onOk,
  reset,
  printer = null,
  filamentStockLoaded = false
}) => {
  const { connected, subscribeToObjectEvent, sendObjectAction } =
    useContext(ApiServerContext)

  LoadFilamentStock.propTypes = {
    onOk: PropTypes.func.isRequired,
    reset: PropTypes.bool.isRequired,
    printer: PropTypes.object,
    filamentStockLoaded: PropTypes.bool
  }

  const initialLoadFilamentStockForm = {
    printer: printer,
    filamentStock: null
  }

  const [loadFilamentStockLoading, setLoadFilamentStockLoading] =
    useState(false)
  const [formValid, setFormValid] = useState(false)
  const [currentTemperature, setCurrentTemperature] = useState(-1)
  const [targetTemperature, setTargetTemperature] = useState(0)
  const [filamentSensorDetected, setFilamentSensorDetected] =
    useState(filamentStockLoaded)
  const [loadFilamentStockForm] = Form.useForm()
  const [loadFilamentStockFormValues, setLoadFilamentStockFormValues] =
    useState(initialLoadFilamentStockForm)

  const loadFilamentStockFormUpdateValues = Form.useWatch(
    [],
    loadFilamentStockForm
  )

  useEffect(() => {
    if (printer?._id && connected) {
      const temperatureEventUnsubscribe = subscribeToObjectEvent(
        printer._id,
        'printer',
        'temperature',
        (event) => {
          if (event.data?.extruder?.current) {
            setCurrentTemperature(event.data.extruder.current)
          }
          if (event.data?.extruder?.target) {
            setTargetTemperature(event.data.extruder.target)
          }
        }
      )
      const filamentStockEventUnsubscribe = subscribeToObjectEvent(
        printer._id,
        'printer',
        'filamentSensor',
        (event) => {
          setFilamentSensorDetected(event.data.detected)
        }
      )
      return () => {
        if (temperatureEventUnsubscribe) temperatureEventUnsubscribe()
        if (filamentStockEventUnsubscribe) filamentStockEventUnsubscribe()
      }
    }
  }, [printer?._id, connected, subscribeToObjectEvent])

  useEffect(() => {
    // Validate form fields
    loadFilamentStockForm
      .validateFields({
        validateOnly: true
      })
      .then(() => {
        const hasPrinter = Boolean(loadFilamentStockFormValues.printer)
        const hasFilamentStock = Boolean(
          loadFilamentStockFormValues.filamentStock
        )

        // Step 0 (Preheat): needs printer + filament detected + (temp reached or no temp set)
        const preheatReady =
          hasPrinter &&
          filamentSensorDetected &&
          (targetTemperature === 0 || currentTemperature >= targetTemperature)

        // Step 1+ (Required/Summary): needs filamentStock selected
        // Form is valid if preheat is ready (for step 0) OR filamentStock is selected (for step 1+)
        // This allows progression: step 0 can proceed when preheat is ready,
        // and step 1+ can proceed when filamentStock is selected
        setFormValid(preheatReady || (hasPrinter && hasFilamentStock))
      })
      .catch(() => setFormValid(false))
  }, [
    loadFilamentStockForm,
    loadFilamentStockFormUpdateValues,
    loadFilamentStockFormValues,
    filamentSensorDetected,
    currentTemperature,
    targetTemperature
  ])

  const summaryItems = [
    {
      key: 'filamentStock',
      label: 'Stock',
      children: loadFilamentStockFormValues.filamentStock ? (
        <ObjectDisplay
          objectType='filamentStock'
          object={loadFilamentStockFormValues.filamentStock}
        />
      ) : (
        'n/a'
      )
    },
    {
      key: 'printer',
      label: 'Printer',
      children: loadFilamentStockFormValues.printer ? (
        <ObjectDisplay
          objectType='printer'
          object={loadFilamentStockFormValues.printer}
        />
      ) : (
        'n/a'
      )
    }
  ]

  useEffect(() => {
    if (reset) {
      loadFilamentStockForm.resetFields()
    }
  }, [reset, loadFilamentStockForm])

  const handleLoadFilamentStock = async () => {
    setLoadFilamentStockLoading(true)

    try {
      // Set the extruder temperature
      await sendObjectAction(
        loadFilamentStockFormValues.printer._id,
        'printer',
        {
          type: 'loadFilamentStock',
          data: {
            filamentStock: loadFilamentStockFormValues.filamentStock
          }
        }
      )
      onOk()
    } finally {
      setLoadFilamentStockLoading(false)
    }
  }

  const steps = [
    {
      title: 'Preheat',
      key: 'preHeat',
      content: (
        <Flex vertical gap={'middle'} style={{ paddingBottom: 16 }}>
          <Form.Item
            label='Printer'
            name='printer'
            style={{ margin: 0 }}
            rules={[
              {
                required: true,
                message: 'Please select a printer'
              }
            ]}
          >
            <ObjectSelect type='printer' checkable={false} />
          </Form.Item>
          {targetTemperature == 0 ? (
            <Alert
              message={'Heat the extruder to begin loading filament.'}
              type='info'
              showIcon
            />
          ) : null}
          {targetTemperature > 0 && currentTemperature < targetTemperature ? (
            <Alert
              message={'Heating extruder...'}
              type='error'
              showIcon
              icon={<LoadingOutlined />}
            />
          ) : null}

          {targetTemperature > 0 &&
          currentTemperature >= targetTemperature - 2 &&
          filamentSensorDetected == false ? (
            <Alert
              message={'Insert filament to continue'}
              type='success'
              showIcon
            />
          ) : null}

          {loadFilamentStockFormValues.printer ? (
            <PrinterTemperaturePanel
              showBed={false}
              showMoreInfo={false}
              id={loadFilamentStockFormValues.printer._id}
              offline={!connected}
            />
          ) : null}
        </Flex>
      )
    },
    {
      title: 'Required',
      key: 'required',
      content: (
        <>
          <Form.Item
            label='Stock:'
            name='filamentStock'
            rules={[
              {
                required: true,
                message: 'Please enter a G Code File.'
              }
            ]}
          >
            <ObjectSelect type='filamentStock' />
          </Form.Item>
        </>
      )
    },
    {
      title: 'Summary',
      key: 'summary',
      content: (
        <Form.Item>
          <Descriptions column={1} items={summaryItems} size={'small'} />
        </Form.Item>
      )
    }
  ]

  return (
    <Form
      name='loadFilamentStock'
      autoComplete='off'
      form={loadFilamentStockForm}
      onFinish={handleLoadFilamentStock}
      onValuesChange={(changedValues) =>
        setLoadFilamentStockFormValues((prevValues) => ({
          ...prevValues,
          ...changedValues
        }))
      }
      initialValues={initialLoadFilamentStockForm}
    >
      <WizardView
        title='Load Filament Stock'
        steps={steps}
        onSubmit={() => loadFilamentStockForm.submit()}
        formValid={formValid}
        loading={loadFilamentStockLoading}
        submitText='Done'
      />
    </Form>
  )
}

export default LoadFilamentStock
