import React from 'react'
import { Prompt } from 'react-router-dom'
import { Form, Modal, Button, DatePicker, Select, Cascader, Checkbox, Radio, Spin } from 'antd'
import BasePage from 'toss.components/BasePage'
import FormInput from 'toss.components/FormInput'
import RadioInput from 'toss.components/RadioInput'
import { base, validate } from 'toss.utils'
import './styles.scss'

const { Item: FormItem } = Form
const { MonthPicker, WeekPicker } = DatePicker

const defaultFormItemLayout = {
  labelCol: { span: 3 },
  wrapperCol: { span: 15 },
}

const extendFieldTypes = {}

class FormBasePage extends BasePage {
  static extendFieldType(fieldType) {
    if (Array.isArray(fieldType)) {
      fieldType.forEach(FormBasePage.extendFieldType)
    } else if (fieldType && fieldType.type && fieldType.renderer) {
      extendFieldTypes[fieldType.type] = fieldType
    }
  }

  constructor(props) {
    super(props)
    this.state = {
      loading: false,
      submiting: false,
      pageAction: this.pageURLParams.action || 'view',
      isUrgentEditMode: this.pageURLParams.action === this.urgentEditModeAction,
      pageId: this.pageMatchParams.id !== 'create' ? this.pageMatchParams.id : null,
      willNavigate: false,
      collapsedSections: {},
      originalData: {},
      ...this.getDefaultState(),
    }
  }

  businessChannel = null
  urgentEditModeAction = 'urgentEdit'

  publishUpdate(data) {
    if (this.businessChannel) {
      this.publishBroadcast({
        channel: this.businessChannel,
        type: 'FORM_PAGE_UPDATE',
        data: data,
      })
    }
  }

  handleSectionTitleClick = event => {
    const { collapsedSections = {} } = this.state
    const { collapsible, name } = event.currentTarget.dataset

    if (collapsible === 'true') {
      this.setState({
        collapsedSections: {
          ...collapsedSections,
          [name]: !collapsedSections[name],
        },
      })
    }
  }

  createFormField = fieldData => {
    let withoutForm = false
    let fieldComponent = null
    const { isUrgentEditMode } = this.state

    if (fieldData.exclude) {
      return null
    }

    if (typeof fieldData.type === 'string') {
      const fieldType = fieldData.type.toLowerCase()
      const { collapsedSections = {} } = this.state

      if (fieldType === 'section') {
        return (
          <div className="lz-form-section" data-collapsed={collapsedSections[fieldData.name]}>
            {fieldData.title ? (
              <h5
                className="section-title"
                data-collapsible={fieldData.collapsible}
                data-name={fieldData.name}
                onClick={this.handleSectionTitleClick}>
                <span className="title-text">{fieldData.title}</span>
                <div className="title-tail">{fieldData.tail}</div>
              </h5>
            ) : null}
            <div className="section-items">
              {fieldData.items.map(item => {
                if (!item) {
                  return null
                }
                const fieldName = item.name instanceof Array ? item.name.join('-') : item.name
                return (
                  <div key={fieldName} className={`lz-form-item ${item.hidden ? 'hidden' : ''} item-${fieldName} span-${item.span || 1}`}>
                    {this.createFormField(item)}
                  </div>
                )
              })}
            </div>
          </div>
        )
      }

      const { size } = fieldData
      const styleProps = {}
      if (size && size > 0) {
        styleProps.style = { width: size }
      } else if (typeof size === 'string') {
        styleProps['data-size'] = size
      }

      if (extendFieldTypes[fieldType]) {
        withoutForm = extendFieldTypes[fieldType].withoutForm
        fieldComponent = extendFieldTypes[fieldType].renderer(fieldData, form)
      } else if (fieldType === 'input') {
        fieldComponent = <FormInput readOnly={fieldData.readOnly} {...styleProps} {...fieldData.props} />
      } else if (fieldType === 'number') {
        fieldComponent = <FormInput readOnly={fieldData.readOnly} type="number" {...fieldData.props} />
      } else if (fieldType === 'textarea') {
        fieldComponent = <FormInput readOnly={fieldData.readOnly} type="textarea" {...fieldData.props} />
      } else if (fieldType === 'radio-input') {
        fieldComponent = <RadioInput className="lz-form-raido-input" {...fieldData.props} />
      } else if (fieldType === 'select') {
        const { valueKey = 'value', labelKey = 'label', options = [], showAll = false, allOptionValue = '', allOptionLabel = '请选择' } = fieldData
        fieldComponent = (
          <Select disabled={fieldData.readOnly} {...styleProps} {...fieldData.props}>
            {showAll ? <Select.Option value={allOptionValue}>{allOptionLabel}</Select.Option> : null}
            {options.map(item => (
              <Select.Option key={item[valueKey]} value={item[valueKey]}>
                {item[labelKey]}
              </Select.Option>
            ))}
          </Select>
        )
      } else if (fieldType === 'checkbox') {
        const { checkedItems = [], valueKey = 'value', labelKey = 'label', checkedOptionsLocked, handleChange = base.noop, options = [] } = fieldData
        fieldComponent = (
          <Checkbox.Group disabled={fieldData.readOnly} className="lz-checkbox-group" {...fieldData.props}>
            {options.map(item => (
              <Checkbox
                key={item[valueKey]}
                value={item[valueKey]}
                onChange={handleChange}
                disabled={(checkedOptionsLocked && checkedItems.map(item => `${item}`).includes(`${item[valueKey]}`)) || item.disabled}>
                {item[labelKey]}
              </Checkbox>
            ))}
          </Checkbox.Group>
        )
      } else if (fieldType === 'radio') {
        const { valueKey = 'value', labelKey = 'label', options = [] } = fieldData
        fieldComponent = (
          <Radio.Group disabled={fieldData.readOnly} className="lz-radio-group" {...fieldData.props}>
            {options.map(item => (
              <Radio key={item[valueKey]} value={item[valueKey]} disabled={item.disabled}>
                {item[labelKey]}
              </Radio>
            ))}
          </Radio.Group>
        )
      } else if (fieldType === 'date') {
        fieldComponent = <DatePicker disabled={fieldData.readOnly} className="lz-date-picker" {...fieldData.props} />
      } else if (fieldType === 'month') {
        fieldComponent = <MonthPicker readOnly={fieldData.readOnly} className="lz-month-picker" {...fieldData.props} />
      } else if (fieldType === 'week') {
        fieldComponent = <WeekPicker readOnly={fieldData.readOnly} className="lz-week-picker" {...fieldData.props} />
      } else if (fieldType === 'button') {
        fieldComponent = <Button {...fieldData.props} />
      } else if (fieldType === 'cascader') {
        fieldComponent = <Cascader readOnly={fieldData.readOnly} className="lz-cascader" {...fieldData.props} options={fieldData.options} />
      }
    }

    if (fieldData.component) {
      fieldComponent = fieldData.component
    } else if (fieldData.render) {
      fieldComponent = fieldData.render(this.props.form, this)
    }

    if (typeof fieldData.withoutForm !== 'undefined') {
      withoutForm = fieldData.withoutForm
    }

    if (fieldData.readOnly && fieldData.decoratorOptions) {
      // fieldData.decoratorOptions.rules = []
    }

    if (!withoutForm && fieldComponent) {
      const decoratorOptions = fieldData.decoratorOptions || {}
      let validateRules = decoratorOptions.rules || []

      if (isUrgentEditMode) {
        validateRules = validateRules.concat(decoratorOptions.urgentEditRules || [])
      }

      return (
        <FormItem
          {...this.getFormItemLayout()}
          className={`label-size-${fieldData.labelSize || 'medium'} ${fieldData.hideErrorText ? 'hide-error-text' : ''}`}
          label={fieldData.label}
          {...fieldData.formItemProps}>
          {fieldData.viewOnly
            ? fieldComponent
            : this.props.form.getFieldDecorator(fieldData.name, {
                initialValue: fieldData.initialValue,
                ...decoratorOptions,
                rules: validateRules,
              })(fieldComponent)}
          {fieldData.suffix ? fieldData.suffix : null}
        </FormItem>
      )
    }

    return fieldComponent
  }

  getFormItemLayout = () => {
    return defaultFormItemLayout
  }

  getFormFields = () => {
    return []
  }

  getNeedPrompt = () => {
    return !this.state.willNavigate && this.isFieldsTouched() && !this.preventNavigatePrompt
  }

  routeInterceptFn = location => {
    return '确认要离开此页面么？'
  }

  getFieldValue = fieldName => {
    return this.props.form.getFieldValue(fieldName)
  }

  getFieldsValue = fieldNames => {
    return this.props.form.getFieldsValue(fieldNames)
  }

  setFieldsValue = (fieldsData, callback) => {
    return this.props.form.setFieldsValue(fieldsData, callback)
  }

  isFieldsTouched = () => {
    return this.props.form.isFieldsTouched()
  }

  getInitData = () => null

  initForm = async () => {
    try {
      this.setState({ loading: true })
      const result = await this.getInitData()
      this.setState({ loading: false })
      if (result && result.initData) {
        this.setFieldsValue(result.initData)
        this.setState({
          originalData: result.originalData,
        })
      }
    } catch (error) {
      console.warn(error)
      this.setState({ loading: false })
    }
  }

  submitForm = formData => {}

  formWillSubmit = async event => {
    return true
  }

  handleSubmit = async event => {
    if (event && (!event.target || event.target.dataset.role !== 'lz-page-form')) {
      return false
    }

    if (event) {
      event.preventDefault()
    }

    this.setState({ willNavigate: true }, () => {
      this.validateFieldsAndScroll(async (errors, fields) => {
        if (!errors) {
          try {
            const allowSubmit = await this.formWillSubmit()
            if (allowSubmit === false) {
              return false
            }
            this.setState({ submiting: true })
            await this.submitForm(fields)
            this.safeSetState({ submiting: false })
          } catch (error) {
            console.warn(errors)
            this.setState({ submiting: false })
          }
        } else {
          console.warn(errors)
        }
      })
      this.safeSetState({ willNavigate: false })
    })
  }

  getCustomValidator = validateFn => (rule, value, callback) => {
    try {
      callback(validateFn(value, validate, rule))
    } catch (error) {
      callback(error.message)
    }
  }

  getFieldValidator = (validatorName, dependencies, validateRule, message) => {
    return (rule, value, callback) => {
      let dependenciesValue = null
      if (Array.isArray(dependencies)) {
        dependenciesValue = this.getFieldsValue(dependencies)
      } else if (typeof dependencies === 'string') {
        dependenciesValue = this.getFieldValue(dependencies)
      }
      if (validate.validators[validatorName]) {
        if (validate.validators[validatorName](value, validateRule || dependenciesValue)) {
          callback()
        } else {
          callback(message)
        }
      } else {
        callback(`找不到校验器:${validatorName}`)
      }
    }
  }

  validateFields = (filedNames, options, callback) => {
    return this.props.form.validateFields(filedNames, options, callback)
  }

  validateFieldsAndScroll = (filedNames, options, callback) => {
    return this.props.form.validateFieldsAndScroll(filedNames, options, callback)
  }

  handleExitPage = () => {
    if (this.isFieldsTouched()) {
      Modal.confirm({
        width: 360,
        title: '温馨提示',
        content: '关闭后当前已输入信息不会保存，确定关闭吗？',
        onOk: () => {
          window.close()
        },
      })
    } else {
      window.close()
    }
  }

  renderFormFooter = () => {
    return null
  }

  renderForm = () => {
    const { loading, submiting } = this.state

    return (
      <Spin spinning={loading || submiting}>
        <Form onSubmit={this.handleSubmit} data-role="lz-page-form" className="lz-page-standard-form">
          <div className="form-content">
            <Prompt when={this.getNeedPrompt()} message={this.routeInterceptFn} />
            {this.getFormFields().map(item => {
              const fieldName = item.name instanceof Array ? item.name.join('-') : item.name
              return item.exclude ? null : (
                <div
                  key={fieldName}
                  className={`lz-form-item type-${item.type || 'custom'} item-${fieldName} ${item.hidden ? 'hidden' : ''} span-${item.span || 1}`}>
                  {this.createFormField(item)}
                </div>
              )
            })}
          </div>
          <div className="lz-form-footer">{this.renderFormFooter(loading)}</div>
        </Form>
      </Spin>
    )
  }

  componentDidMount() {
    super.componentDidMount()
    this.initForm()
  }
}

export default FormBasePage
