import React, { useState, useContext, useEffect, useCallback } from 'react'
import axios from 'axios'
import {
  Form,
  Button,
  message,
  Typography,
  Flex,
  Steps,
  Divider,
  Input,
  Select,
  Space,
  Descriptions,
  List,
  InputNumber,
  notification,
  Progress,
  Modal,
  Radio
} from 'antd'
import { SearchOutlined, SettingOutlined } from '@ant-design/icons'
import PropTypes from 'prop-types'
import { PrintServerContext } from '../../context/PrintServerContext'
import EditIcon from '../../../Icons/EditIcon.jsx'

import config from '../../../../config.js'

const { Title } = Typography

const initialNewPrinterForm = {
  moonraker: {
    protocol: 'ws',
    host: '',
    port: '',
    apiKey: ''
  }
}

const NewPrinter = ({ onOk, reset }) => {
  NewPrinter.propTypes = {
    onOk: PropTypes.func.isRequired,
    reset: PropTypes.bool.isRequired
  }

  const { printServer } = useContext(PrintServerContext)
  const [messageApi, contextHolder] = message.useMessage()
  const [notificationApi, notificationContextHolder] =
    notification.useNotification()
  const [newPrinterLoading, setNewPrinterLoading] = useState(false)
  const [currentStep, setCurrentStep] = useState(0)
  const [nextEnabled, setNextEnabled] = useState(false)
  const [newPrinterForm] = Form.useForm()
  const [newPrinterFormValues, setNewPrinterFormValues] = useState(
    initialNewPrinterForm
  )
  const [discoveredPrinters, setDiscoveredPrinters] = useState([])
  const [discovering, setDiscovering] = useState(false)
  const [showManualSetup, setShowManualSetup] = useState(false)
  const [scanPort, setScanPort] = useState(7125)
  const [scanProtocol, setScanProtocol] = useState('ws')
  const [editingHostname, setEditingHostname] = useState(null)
  const [hostnameInput, setHostnameInput] = useState('')
  const [initialized, setInitialized] = useState(false)

  const newPrinterFormUpdateValues = Form.useWatch([], newPrinterForm)

  useEffect(() => {
    newPrinterForm
      .validateFields({
        validateOnly: true
      })
      .then(() => {
        if (currentStep === 0) {
          const moonraker = newPrinterForm.getFieldValue('moonraker')
          setNextEnabled(
            !!(moonraker?.protocol && moonraker?.host && moonraker?.port)
          )
        } else if (currentStep === 1) {
          const name = newPrinterForm.getFieldValue('name')
          setNextEnabled(!!name)
        } else {
          setNextEnabled(true)
        }
      })
      .catch(() => setNextEnabled(false))
  }, [newPrinterForm, newPrinterFormUpdateValues, currentStep])

  const summaryItems = [
    {
      key: 'name',
      label: 'Name',
      children: newPrinterFormValues.name
    },
    {
      key: 'protocol',
      label: 'Protocol',
      children: newPrinterFormValues.moonraker?.protocol
    },
    {
      key: 'host',
      label: 'Host',
      children: newPrinterFormValues.moonraker?.host
    },
    {
      key: 'port',
      label: 'Port',
      children: newPrinterFormValues.moonraker?.port
    }
  ]

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

  const handlePrinterSelect = (printer) => {
    newPrinterForm.setFieldsValue({
      moonraker: {
        protocol: printer.protocol,
        host: printer.host,
        port: printer.port
      }
    })
    setNewPrinterFormValues({
      ...newPrinterFormValues,
      moonraker: {
        protocol: printer.protocol,
        host: printer.host,
        port: printer.port
      }
    })
  }

  const handleHostnameEdit = (printer, newHostname) => {
    if (newHostname && newHostname.trim() !== '') {
      const updatedPrinter = {
        ...printer,
        host: newHostname.trim()
      }
      setDiscoveredPrinters((prev) =>
        prev.map((p) => (p.host === printer.host ? updatedPrinter : p))
      )
      setEditingHostname(null)
      setHostnameInput('')
    }
  }

  const showEditHostnameDialog = (printer) => {
    setEditingHostname(printer.host)
    setHostnameInput(printer.host)
  }

  const handleNewPrinter = async () => {
    setNewPrinterLoading(true)
    try {
      await axios.post(
        `${config.backendUrl}/printers`,
        {
          ...newPrinterFormValues
        },
        {
          headers: {
            Accept: 'application/json'
          },
          withCredentials: true
        }
      )

      onOk()
    } catch (error) {
      messageApi.error('Error adding new printer: ' + error.message)
    } finally {
      setNewPrinterLoading(false)
    }
  }

  const notifyScanNetworkFound = useCallback(
    (data) => {
      const newPrinter = {
        protocol: scanProtocol,
        host: data.hostname || data.ip,
        port: scanPort
      }
      notificationApi.info({
        message: 'Printer Found',
        description: `Printer found: ${data.hostname || data.ip}!`
      })
      setDiscoveredPrinters((prev) => [...prev, newPrinter])
    },
    [scanProtocol, scanPort, notificationApi]
  )

  const notifyScanNetworkComplete = useCallback(
    (data) => {
      setDiscovering(false)
      notificationApi.destroy('network-scan')
      if (data == false) {
        messageApi.error('Error discovering printers!')
      } else {
        messageApi.success('Finished discovering printers!')
      }
    },
    [messageApi, notificationApi]
  )

  const notifyScanNetworkProgress = useCallback(
    (data) => {
      notificationApi.info({
        message: 'Scanning Network',
        description: (
          <div>
            <div style={{ marginBottom: 12 }}>
              Scanning IP: {data.currentIP}
            </div>
            <Progress
              percent={data.progress}
              size='small'
              status='active'
              showInfo={false}
            />
          </div>
        ),
        duration: 0,
        key: 'network-scan',
        icon: null,
        placement: 'bottomRight',
        style: {
          width: 360
        },
        className: 'network-scan-notification',
        closeIcon: null,
        onClose: () => {},
        btn: null
      })
    },
    [notificationApi]
  )

  const discoverPrinters = useCallback(() => {
    if (!discovering) {
      setDiscovering(true)
      setDiscoveredPrinters([])
      messageApi.info('Discovering printers...')
      printServer.off('notify_scan_network_found')
      printServer.off('notify_scan_network_progress')
      printServer.off('notify_scan_network_complete')

      printServer.on('notify_scan_network_found', notifyScanNetworkFound)
      printServer.on('notify_scan_network_progress', notifyScanNetworkProgress)
      printServer.on('notify_scan_network_complete', notifyScanNetworkComplete)

      printServer.emit('bridge.scan_network.start', {
        port: scanPort,
        protocol: scanProtocol
      })
    }
  }, [
    discovering,
    printServer,
    scanPort,
    scanProtocol,
    messageApi,
    notifyScanNetworkFound,
    notifyScanNetworkProgress,
    notifyScanNetworkComplete
  ])

  useEffect(() => {
    setInitialized(true)
    if (!initialized) {
      discoverPrinters()
    }
  }, [initialized, discoverPrinters])

  const stopDiscovery = () => {
    if (discovering) {
      setDiscovering(false)
      notificationApi.destroy('network-scan')
      messageApi.info('Stopping discovery...')
      printServer.off('notify_scan_network_found')
      printServer.off('notify_scan_network_progress')
      printServer.off('notify_scan_network_complete')
      printServer.emit('bridge.scan_network.stop', (response) => {
        if (response == false) {
          messageApi.error('Error stopping discovery!')
        }
      })
    }
  }

  const handlePortChange = (value) => {
    stopDiscovery()
    setScanPort(value)
  }

  const handleProtocolChange = (value) => {
    stopDiscovery()
    setScanProtocol(value)
  }

  const steps = [
    {
      title: 'Discovery',
      key: 'discovery',
      content: (
        <>
          <Flex vertical style={{ width: '100%' }} gap='large'>
            {!showManualSetup ? (
              <>
                <Flex
                  style={{ width: '100%' }}
                  justify='space-between'
                  align='center'
                  gap='middle'
                >
                  <Space.Compact>
                    <InputNumber
                      min={1}
                      max={65535}
                      value={scanPort}
                      onChange={handlePortChange}
                      style={{ width: '80px' }}
                      placeholder='Port'
                    />
                    <Select
                      value={scanProtocol}
                      onChange={handleProtocolChange}
                      options={[
                        { value: 'ws', label: 'ws' },
                        { value: 'wss', label: 'wss' }
                      ]}
                    />
                    <Button
                      icon={<SearchOutlined />}
                      onClick={discoverPrinters}
                      loading={discovering}
                    >
                      {discovering ? 'Discovering...' : 'Discover'}
                    </Button>
                  </Space.Compact>
                  <Button
                    icon={<SettingOutlined />}
                    onClick={() => setShowManualSetup(true)}
                  >
                    Manual Setup
                  </Button>
                </Flex>
                <List
                  dataSource={discoveredPrinters}
                  renderItem={(printer) => (
                    <List.Item
                      key={`${printer.host}:${printer.port}`}
                      actions={[
                        <Radio
                          key='select'
                          defaultChecked={
                            newPrinterFormValues.moonraker?.host ===
                            printer.host
                          }
                          onChange={() => handlePrinterSelect(printer)}
                        />
                      ]}
                    >
                      <List.Item.Meta
                        title={
                          <Space>
                            {printer.host}
                            {!printer.hostname && (
                              <Button
                                type='text'
                                icon={<EditIcon />}
                                onClick={() => showEditHostnameDialog(printer)}
                              />
                            )}
                          </Space>
                        }
                        description={`Protocol: ${printer.protocol}, Port: ${printer.port}`}
                      />
                    </List.Item>
                  )}
                />
                <Modal
                  title='Edit Host'
                  open={editingHostname !== null}
                  onOk={() => {
                    const printer = discoveredPrinters.find(
                      (p) => p.host === editingHostname
                    )
                    if (printer) {
                      handleHostnameEdit(printer, hostnameInput)
                    }
                  }}
                  onCancel={() => {
                    setEditingHostname(null)
                    setHostnameInput('')
                  }}
                >
                  <Form.Item label='Host' required>
                    <Input
                      value={hostnameInput}
                      onChange={(e) => setHostnameInput(e.target.value)}
                      placeholder='Enter host'
                      autoFocus
                    />
                  </Form.Item>
                </Modal>
              </>
            ) : (
              <>
                <Flex style={{ width: '100%' }} justify='end'>
                  <Button
                    icon={<SearchOutlined />}
                    onClick={() => setShowManualSetup(false)}
                  >
                    Back to Discovery
                  </Button>
                </Flex>
                <Flex vertical>
                  <Form.Item
                    label='Protocol'
                    name={['moonraker', 'protocol']}
                    rules={[
                      { required: true, message: 'Protocol is required' }
                    ]}
                  >
                    <Select
                      defaultValue='ws'
                      options={[
                        { value: 'ws', label: 'Websocket' },
                        { value: 'wss', label: 'Websocket Secure' }
                      ]}
                    />
                  </Form.Item>
                  <Form.Item
                    label='Host'
                    name={['moonraker', 'host']}
                    rules={[{ required: true, message: 'Host is required' }]}
                  >
                    <Input />
                  </Form.Item>
                  <Form.Item
                    label='Port'
                    name={['moonraker', 'port']}
                    rules={[{ required: true, message: 'Port is required' }]}
                  >
                    <InputNumber
                      min={1}
                      max={65535}
                      style={{ width: '100%' }}
                    />
                  </Form.Item>
                  <Form.Item
                    label='API Key'
                    name={['moonraker', 'apiKey']}
                    rules={[{ required: false }]}
                  >
                    <Input.Password
                      placeholder='Optional API key'
                      style={{ width: '100%' }}
                    />
                  </Form.Item>
                </Flex>
              </>
            )}
          </Flex>
        </>
      )
    },
    {
      title: 'Required',
      key: 'required',
      content: (
        <>
          <Form.Item
            label='Name'
            name='name'
            rules={[{ required: true, message: 'Name is required' }]}
          >
            <Input />
          </Form.Item>
        </>
      )
    },
    {
      title: 'Summary',
      key: 'summary',
      content: (
        <Form.Item>
          <Descriptions column={1} items={summaryItems} size={'small'} />
        </Form.Item>
      )
    }
  ]

  return (
    <Flex gap={'middle'}>
      {contextHolder}
      {notificationContextHolder}

      <div style={{ minWidth: '160px' }}>
        <Steps current={currentStep} items={steps} direction='vertical' />
      </div>

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

      <Flex vertical={'true'} style={{ flexGrow: 1 }}>
        <Title level={2} style={{ marginTop: 0 }}>
          New Printer
        </Title>
        <Form
          name='basic'
          autoComplete='off'
          form={newPrinterForm}
          onFinish={handleNewPrinter}
          onValuesChange={(changedValues) =>
            setNewPrinterFormValues((prevValues) => ({
              ...prevValues,
              ...changedValues
            }))
          }
          initialValues={initialNewPrinterForm}
        >
          <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'
                htmlType='submit'
                loading={newPrinterLoading}
              >
                Done
              </Button>
            )}
          </Flex>
        </Form>
      </Flex>
    </Flex>
  )
}

export default NewPrinter
