import { useEffect, useMemo, useRef, useState } from 'react'
import PropTypes from 'prop-types'
import NewObjectForm from '../../common/NewObjectForm'
import ObjectForm from '../../common/ObjectForm'
import ObjectInfo from '../../common/ObjectInfo'
import WizardView from '../../common/WizardView'
import {
  FILAMENT_PROFILE_BED_TEMPERATURE_PROPERTIES,
  FILAMENT_PROFILE_COMPATIBILITY_PROPERTIES,
  FILAMENT_PROFILE_COOLING_PROPERTIES,
  FILAMENT_PROFILE_EXHAUST_FAN_PROPERTIES,
  FILAMENT_PROFILE_FLOW_PRESSURE_PROPERTIES,
  FILAMENT_PROFILE_MATERIAL_PROPERTIES,
  FILAMENT_PROFILE_SPEED_PROPERTIES,
  FILAMENT_PROFILE_TEMPERATURE_PROPERTIES
} from '../../../../database/models/FilamentProfile'

const PRUSA_GENERIC_PETG_DEFAULTS = {
  filamentType: 'filament',
  filamentIsSupport: false,
  filamentSoluble: false,
  filamentPrintable: 3,
  filamentAdhesivenessCategory: 0,
  temperatureVitrification: 80,
  idleTemperature: 0,
  pelletFlowCoefficient: 0.4157,
  requiredNozzleHrc: 0,
  filamentFlowRatio: 0.98,
  enablePressureAdvance: false,
  pressureAdvance: 0.02,
  adaptivePressureAdvance: false,
  adaptivePressureAdvanceBridges: false,
  adaptivePressureAdvanceOverhangs: false,
  adaptivePressureAdvanceModel: '0,0,0\n0,0,0',
  activateChamberTempControl: false,
  chamberTemperature: 0,
  chamberMinimalTemperature: 0,
  nozzleTemperatureInitialLayer: 230,
  nozzleTemperature: 240,
  nozzleTemperatureRangeLow: 220,
  nozzleTemperatureRangeHigh: 260,
  hotPlateTempInitialLayer: 85,
  hotPlateTemp: 85,
  coolPlateTempInitialLayer: 60,
  coolPlateTemp: 60,
  engPlateTempInitialLayer: 0,
  engPlateTemp: 0,
  texturedPlateTempInitialLayer: 45,
  texturedPlateTemp: 45,
  texturedCoolPlateTempInitialLayer: 40,
  texturedCoolPlateTemp: 40,
  supertackPlateTempInitialLayer: 35,
  supertackPlateTemp: 35,
  filamentAdaptiveVolumetricSpeed: false,
  filamentMaxVolumetricSpeed: 8,
  volumetricSpeedCoefficients: '',
  closeFanTheFirstXLayers: 3,
  fullFanSpeedLayer: 0,
  fanMinSpeed: 40,
  fanMaxSpeed: 90,
  reduceFanStopStartFreq: true,
  slowDownForLayerCooling: true,
  dontSlowDownOuterWall: false,
  slowDownMinSpeed: 10,
  slowDownLayerTime: 8,
  fanCoolingLayerTime: 30,
  enableOverhangBridgeFan: true,
  overhangFanThreshold: '25%',
  overhangFanSpeed: 90,
  internalBridgeFanSpeed: -1,
  supportMaterialInterfaceFanSpeed: -1,
  ironingFanSpeed: -1,
  initialLayerFanSpeed: -1,
  firstXLayerFanSpeed: 0,
  additionalCoolingFanSpeed: 0,
  additionalFanFullSpeedLayer: 0,
  closeAdditionalFanFirstXLayers: true,
  activateAirFiltration: false,
  activateAirFiltrationDuringPrint: true,
  activateAirFiltrationOnCompletion: true,
  duringPrintExhaustFanSpeed: 60,
  completePrintExhaustFanSpeed: 80,
  filamentLoadingSpeed: 28,
  filamentLoadingSpeedStart: 3,
  filamentUnloadingSpeed: 90,
  filamentUnloadingSpeedStart: 100,
  filamentChangeLength: 10,
  filamentChangeLengthNc: 10,
  filamentToolchangeDelay: 0,
  filamentExtruderCompatibility: 0,
  filamentExtruderVariant: 'Direct Drive Standard',
  filamentMultitoolRamming: false,
  filamentMultitoolRammingFlow: 10,
  filamentMultitoolRammingVolume: 10,
  filamentRammingParameters:
    '120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6',
  filamentRammingTravelTime: 0,
  filamentRammingTravelTimeNc: 0,
  filamentRammingVolumetricSpeed: -1,
  filamentRammingVolumetricSpeedNc: -1,
  filamentMinimalPurgeOnWipeTower: 15,
  filamentTowerInterfacePreExtrusionDist: 10,
  filamentTowerInterfacePreExtrusionLength: 0,
  filamentTowerInterfacePrintTemp: -1,
  filamentTowerInterfacePurgeVolume: 20,
  filamentTowerIroningArea: 4,
  filamentCoolingBeforeTower: 10,
  filamentCoolingInitialSpeed: 2.2,
  filamentCoolingFinalSpeed: 3.4,
  filamentCoolingMoves: 4,
  filamentFlushTemp: 0,
  filamentFlushTempFast: 0,
  filamentFlushVolumetricSpeed: 0,
  filamentPreCoolingTemperature: 0,
  filamentPreCoolingTemperatureNc: 0,
  filamentPreheatTemperatureDelta: 0,
  filamentPrimeVolumeNc: 60,
  filamentStampingDistance: 0,
  filamentStampingLoadingSpeed: 0,
  longRetractionsWhenEc: false,
  retractionDistancesWhenEc: 10,
  filamentStartGcode:
    '; filament start gcode\nM900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.08{endif} ; Filament gcode LA 1.5',
  filamentEndGcode: '; filament end gcode \n',
  filamentChangeExtrusionRoleGcode: '',
  filamentShrink: '100%',
  filamentShrinkageCompensationZ: '100%',
  compatiblePrinters: [],
  compatiblePrintersCondition: '',
  compatiblePrints: [],
  compatiblePrintsCondition: '',
  filamentNotes: ''
}

const REQUIRED_PROPERTIES = ['name', 'filamentType', 'filament']

const SUMMARY_PROPERTIES = [
  ...REQUIRED_PROPERTIES,
  ...FILAMENT_PROFILE_MATERIAL_PROPERTIES,
  ...FILAMENT_PROFILE_FLOW_PRESSURE_PROPERTIES,
  ...FILAMENT_PROFILE_TEMPERATURE_PROPERTIES,
  ...FILAMENT_PROFILE_BED_TEMPERATURE_PROPERTIES,
  ...FILAMENT_PROFILE_SPEED_PROPERTIES,
  ...FILAMENT_PROFILE_COOLING_PROPERTIES,
  ...FILAMENT_PROFILE_EXHAUST_FAN_PROPERTIES
]

const FilamentProfileWizard = ({
  objectData,
  formValid,
  loading,
  onSubmit,
  editing,
  filamentId = null
}) => {
  const visibleProperties = useMemo(() => {
    if (!filamentId) return undefined
    return { filament: false, 'filament._id': false }
  }, [filamentId])

  const steps = useMemo(
    () => [
      {
        title: 'Required',
        key: 'required',
        content: (
          <ObjectInfo
            type='filamentProfile'
            properties={REQUIRED_PROPERTIES}
            column={1}
            bordered={false}
            isEditing
            labelWidth='110px'
            required={true}
            objectData={objectData}
            visibleProperties={visibleProperties}
          />
        )
      },
      {
        title: 'Material',
        key: 'material',
        content: (
          <ObjectInfo
            type='filamentProfile'
            properties={FILAMENT_PROFILE_MATERIAL_PROPERTIES}
            column={1}
            bordered={false}
            isEditing
            labelWidth='210px'
            objectData={objectData}
          />
        )
      },
      {
        title: 'Flow & Pressure',
        key: 'flowPressure',
        content: (
          <ObjectInfo
            type='filamentProfile'
            properties={FILAMENT_PROFILE_FLOW_PRESSURE_PROPERTIES}
            column={1}
            bordered={false}
            isEditing
            labelWidth='290px'
            objectData={objectData}
          />
        )
      },
      {
        title: 'Temperature',
        key: 'temperature',
        content: (
          <ObjectInfo
            type='filamentProfile'
            properties={[
              ...FILAMENT_PROFILE_TEMPERATURE_PROPERTIES,
              ...FILAMENT_PROFILE_BED_TEMPERATURE_PROPERTIES
            ]}
            column={1}
            bordered={false}
            isEditing
            labelWidth='250px'
            objectData={objectData}
          />
        )
      },
      {
        title: 'Speed',
        key: 'speed',
        content: (
          <ObjectInfo
            type='filamentProfile'
            properties={FILAMENT_PROFILE_SPEED_PROPERTIES}
            column={1}
            bordered={false}
            isEditing
            labelWidth='220px'
            objectData={objectData}
          />
        )
      },
      {
        title: 'Cooling',
        key: 'cooling',
        content: (
          <ObjectInfo
            type='filamentProfile'
            properties={FILAMENT_PROFILE_COOLING_PROPERTIES}
            column={1}
            bordered={false}
            isEditing
            labelWidth='320px'
            objectData={objectData}
          />
        )
      },
      {
        title: 'Exhaust Fan',
        key: 'exhaustFan',
        content: (
          <ObjectInfo
            type='filamentProfile'
            properties={FILAMENT_PROFILE_EXHAUST_FAN_PROPERTIES}
            column={1}
            bordered={false}
            isEditing
            labelWidth='270px'
            objectData={objectData}
          />
        )
      },
      {
        title: 'Compatibility',
        key: 'compatibility',
        content: (
          <ObjectInfo
            type='filamentProfile'
            properties={FILAMENT_PROFILE_COMPATIBILITY_PROPERTIES}
            column={1}
            bordered={false}
            isEditing
            labelWidth='270px'
            objectData={objectData}
          />
        )
      },
      {
        title: 'Summary',
        key: 'summary',
        content: (
          <ObjectInfo
            type='filamentProfile'
            properties={SUMMARY_PROPERTIES}
            column={1}
            bordered={false}
            isEditing={false}
            labelWidth='250px'
            objectData={objectData}
            visibleProperties={visibleProperties}
          />
        )
      }
    ],
    [objectData, visibleProperties]
  )

  return (
    <WizardView
      sizeBarWidth='185px'
      steps={steps}
      loading={loading}
      formValid={formValid}
      title={editing ? 'Edit Filament Profile' : 'New Filament Profile'}
      submitText={editing ? 'Save' : 'Create'}
      onSubmit={onSubmit}
    />
  )
}

FilamentProfileWizard.propTypes = {
  objectData: PropTypes.object,
  formValid: PropTypes.bool.isRequired,
  loading: PropTypes.bool,
  onSubmit: PropTypes.func.isRequired,
  editing: PropTypes.bool,
  filamentId: PropTypes.string
}

const NewFilamentProfile = ({ onOk, filamentId = null }) => (
  <NewObjectForm
    type='filamentProfile'
    defaultValues={{
      ...PRUSA_GENERIC_PETG_DEFAULTS,
      ...(filamentId ? { filamentType: 'filament', filament: filamentId } : {})
    }}
  >
    {({ handleSubmit, submitLoading, objectData, formValid }) => (
      <FilamentProfileWizard
        objectData={objectData}
        formValid={formValid}
        loading={submitLoading}
        filamentId={filamentId}
        onSubmit={async () => {
          const result = await handleSubmit()
          if (result?._id) onOk(result)
        }}
      />
    )}
  </NewObjectForm>
)

NewFilamentProfile.propTypes = {
  onOk: PropTypes.func.isRequired,
  filamentId: PropTypes.string
}

export const EditFilamentProfile = ({ profileId, onOk, filamentId = null }) => {
  const formRef = useRef(null)
  const startedEditingRef = useRef(false)
  const saveRequestedRef = useRef(false)
  const stateRef = useRef({})
  const [formState, setFormState] = useState({
    loading: true,
    editLoading: false,
    formValid: false,
    isEditing: false,
    objectData: {}
  })

  useEffect(() => {
    if (
      !formState.loading &&
      !formState.isEditing &&
      !startedEditingRef.current
    ) {
      startedEditingRef.current = true
      formRef.current?.startEditing()
    }
  }, [formState.isEditing, formState.loading])

  return (
    <ObjectForm
      id={profileId}
      type='filamentProfile'
      ref={formRef}
      onStateChange={(nextState) => {
        stateRef.current = { ...stateRef.current, ...nextState }
        setFormState((current) => ({ ...current, ...nextState }))

        if (saveRequestedRef.current && nextState.isEditing === false) {
          saveRequestedRef.current = false
          onOk(stateRef.current.objectData)
        }
      }}
    >
      {({ objectData, formValid, editLoading, handleUpdate }) => (
        <FilamentProfileWizard
          editing
          objectData={objectData}
          formValid={formValid}
          loading={formState.loading || editLoading}
          filamentId={filamentId}
          onSubmit={() => {
            saveRequestedRef.current = true
            handleUpdate()
          }}
        />
      )}
    </ObjectForm>
  )
}

EditFilamentProfile.propTypes = {
  profileId: PropTypes.string.isRequired,
  onOk: PropTypes.func.isRequired,
  filamentId: PropTypes.string
}

export default NewFilamentProfile
