import React, { useState, useContext, useEffect } from 'react'
import {
  Form,
  Button,
  Typography,
  Flex,
  Steps,
  Divider,
  Descriptions,
  Alert
} from 'antd'
import { useMediaQuery } from 'react-responsive'
import PropTypes from 'prop-types'
import { PrintServerContext } from '../../context/PrintServerContext'

import FilamentStockSelect from '../../common/FilamentStockSelect'
import PrinterSelect from '../../common/PrinterSelect'
import FilamentStockDisplay from '../../common/FilamentStockDisplay'
import PrinterTemperaturePanel from '../../common/PrinterTemperaturePanel'

import { LoadingOutlined } from '@ant-design/icons'
import PrinterState from '../../common/PrinterState'

const { Title } = Typography

const LoadFilamentStock = ({
  onOk,
  reset,
  printer = null,
  filamentStockLoaded = false
}) => {
  const isMobile = useMediaQuery({ maxWidth: 768 })

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

  const { printServer } = useContext(PrintServerContext)

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

  const [loadFilamentStockLoading, setLoadFilamentStockLoading] =
    useState(false)
  const [currentStep, setCurrentStep] = useState(0)
  const [nextEnabled, setNextEnabled] = 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
  )

  // Add websocket temperature monitoring
  useEffect(() => {
    if (loadFilamentStockFormValues.printer) {
      const params = {
        printerId: loadFilamentStockFormValues.printer._id,
        objects: {
          extruder: null,
          'filament_switch_sensor fsensor': null
        }
      }

      const notifyStatusUpdate = (statusUpdate) => {
        if (statusUpdate?.extruder?.temperature !== undefined) {
          setCurrentTemperature(statusUpdate.extruder.temperature)
        }
        if (statusUpdate?.extruder?.target !== undefined) {
          setTargetTemperature(statusUpdate.extruder.target)
        }
        if (
          statusUpdate?.['filament_switch_sensor fsensor']
            ?.filament_detected !== undefined
        ) {
          setFilamentSensorDetected(
            Boolean(
              statusUpdate['filament_switch_sensor fsensor'].filament_detected
            )
          )
        }
      }

      printServer.emit('printer.objects.subscribe', params)
      printServer.emit('printer.objects.query', params)
      printServer.on('notify_status_update', notifyStatusUpdate)

      return () => {
        printServer.off('notify_status_update', notifyStatusUpdate)
        printServer.emit('printer.objects.unsubscribe', params)
      }
    }
  }, [printServer, loadFilamentStockFormValues.printer])

  React.useEffect(() => {
    loadFilamentStockForm
      .validateFields({
        validateOnly: true
      })
      .then(() => setNextEnabled(filamentSensorDetected))
      .catch(() => setNextEnabled(false))
  }, [
    loadFilamentStockForm,
    loadFilamentStockFormUpdateValues,
    filamentSensorDetected
  ])

  useEffect(() => {
    if (
      filamentSensorDetected == true &&
      currentTemperature >= targetTemperature
    ) {
      setNextEnabled(filamentSensorDetected)
      if (currentStep == 0) {
        setCurrentStep(1)
      }
    } else if (filamentSensorDetected == false) {
      setCurrentStep(0)
    }
  }, [
    filamentSensorDetected,
    targetTemperature,
    currentTemperature,
    currentStep
  ])

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

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

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

    try {
      // Set the extruder temperature
      await printServer.emit('printer.filamentstock.load', {
        printerId: loadFilamentStockFormValues.printer._id,
        filamentStockId: loadFilamentStockFormValues.filamentStock._id
      })
      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'
              }
            ]}
          >
            <PrinterSelect 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 &&
          filamentSensorDetected == false ? (
            <Alert
              message={'Insert filament to continue'}
              type='success'
              showIcon
            />
          ) : null}

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

  return (
    <Flex gap={'middle'}>
      {!isMobile && (
        <div style={{ minWidth: '160px' }}>
          <Steps
            current={currentStep}
            items={steps}
            direction='vertical'
            style={{ width: 'fit-content' }}
          />
        </div>
      )}

      {!isMobile && <Divider type={'vertical'} style={{ height: 'unset' }} />}

      <Flex vertical={'true'} style={{ flexGrow: 1 }} gap='middle'>
        <Title level={2} style={{ marginTop: 0, marginBottom: 4 }}>
          Load Filament Stock
        </Title>
        <Form
          name='basic'
          autoComplete='off'
          form={loadFilamentStockForm}
          onFinish={handleLoadFilamentStock}
          onValuesChange={(changedValues) =>
            setLoadFilamentStockFormValues((prevValues) => ({
              ...prevValues,
              ...changedValues
            }))
          }
          initialValues={initialLoadFilamentStockForm}
        >
          <div style={{ minHeight: '260px' }}>{steps[currentStep].content}</div>

          <Flex justify={'end'}>
            <Button
              style={{
                margin: '0 8px'
              }}
              onClick={() => setCurrentStep(currentStep - 1)}
              disabled={!(currentStep > 0)}
            >
              Previous
            </Button>
            {currentStep < steps.length - 1 && (
              <Button
                type='primary'
                disabled={!nextEnabled}
                onClick={() => {
                  setCurrentStep(currentStep + 1)
                }}
              >
                Next
              </Button>
            )}
            {currentStep === steps.length - 1 && (
              <Button
                type='primary'
                loading={loadFilamentStockLoading}
                onClick={() => {
                  loadFilamentStockForm.submit()
                }}
              >
                Done
              </Button>
            )}
          </Flex>
        </Form>
      </Flex>
    </Flex>
  )
}

export default LoadFilamentStock
