<script lang="jsx">
import { renderEditor } from '../../dg-editor'
import { hasOwn } from '../../utils'

export default {
  name: 'DgSearch',
  props: {
    form: { type: Object, default: () => ({}) },
    options: {
      type: Array,
      default: () => [],
    },
    maxToFixed: {
      type: Number,
      default: 1,
    },
    size: {
      type: String,
      default: 'mini',
    },
    searchButtonProps: {
      type: Object,
      default: () => ({ visible: true }),
    },
    searchText: {
      type: String,
      default: '查询',
    },
    moreText: {
      type: String,
      default: '',
    },
    moreIcon: {
      type: String,
      default: 'el-icon-s-operation el-icon-dg-filter-line',
    },
    clearText: {
      type: String,
      default: '清除',
    },
    emptyText: {
      type: String,
      default: '全部',
    },
    resetText: {
      type: String,
      default: '重置',
    },
  },
  data: () => ({
    innerCurrentOptionalProp: null,
    showPanel: false,
  }),
  computed: {
    normalizedOptions() {
      const fixed = []
      const optional = []
      const resultMap = {}

      this.options.forEach((option) => {
        resultMap[option.prop] = option
        const { prop, editor } = option

        if (!hasOwn(this.form, prop)) {
          this.$set(this.form, prop, null)
        }

        option.models = option.models || { value: prop }
        option.editorProps = option.editorProps || {}
        option.editorProps.on = option.editorProps.on || {}

        if (editor === 'dg-select') {
          const nameProp = (option.models.name = option.models.name || `name_${prop}`)
          if (!hasOwn(this.form, nameProp)) {
            this.$set(this.form, nameProp, null)
          }
          option.resultFormat = option.resultFormat || (() => this.form[nameProp] || this.form[prop])
        }

        if (editor === 'dg-tree-select') {
          option.editorProps.on = {
            getValue: (value, name) => {
              this.$set(this.form, prop, value)
              this.$set(this.form, option.editorProps.selectNameKey || prop + 'Cn', name)
            },
            ...option.editorProps.on
          }
        }

        Object.entries(option.models).forEach(([attr, key]) => {
          option.editorProps[attr] = this.form[key]
          option.editorProps.on[attr === 'value' ? 'input' : `update:${attr}`] = (val) => {
            // eslint-disable-next-line vue/no-mutating-props
            this.form[key] = val
          }
        })

        option.optional ? optional.push(option) : fixed.push(option)
      })

      if (fixed.length > this.maxToFixed + 1) {
        optional.unshift(...fixed.splice(this.maxToFixed))
      }

      return { fixed, optional, resultMap }
    },
    result() {
      const { resultMap } = this.normalizedOptions

      return Object.keys(resultMap).reduce((res, key) => {
        const value = this.form[key]
        const notNill = value !== null && value !== undefined && value !== ''
        const isTruly = Array.isArray(value) ? !!value.length : true
        if (notNill && isTruly) {
          res.push(resultMap[key])
        }
        return res
      }, [])
    },
    currentOptional() {
      if (!this.innerCurrentOptionalProp) return this.normalizedOptions.optional[0]

      return this.normalizedOptions.optional.find(({ prop }) => prop === this.innerCurrentOptionalProp)
    },
  },
  methods: {
    handleCurrentOptionalChange(prop) {
      this.innerCurrentOptionalProp = prop
    },
    deleteAllSearchOption() {
      this.result.forEach(this.deleteSearchOption)
    },
    deleteSearchOption(option) {
      this.$set(this.form, option.prop, null)
      if (option.resultProp) {
        this.$set(this.form, option.resultProp, null)
      }
    },
    formatResult(option) {
      const key = option.prop
      const value = this.form[key]
      if (typeof option.resultFormat === 'function') {
        return option.resultFormat(this.form[key], key, this.form)
      }

      if (Array.isArray(value)) return value.join(',')

      return value
    },
    handleReset() {
      this.$refs.dgSearchForm.resetFields()
      this.handleSearch()
    },
    handleSearch(e) {
      e?.preventDefault()
      this.$emit('search', this.form, this.options)
      this.showPanel = false
    },
    toggleShowPanel() {
      this.showPanel = !this.showPanel
    },
  },
  render() {
    const {
      form,
      size,
      normalizedOptions,
      currentOptional,
      moreText,
      moreIcon,
      resetText,
      emptyText,
      clearText,
      result,
      searchButtonProps: _searchButtonProps,
      searchText,
      options,
      showPanel,
    } = this
    if (!options.length) return null

    const { monitor, ...searchButtonProps } = _searchButtonProps
    const searchButtonDirectives = []
    if (monitor) {
      searchButtonDirectives.push({
        name: 'monitor',
        rawName: 'v-monitor',
        value: { click: { params: this.form, ...(monitor.click || {}) } },
      })
    }

    return (
      <div class="dg-search">
        <el-form
          ref="dgSearchForm"
          class="dg-search-form"
          {...{ props: { model: form, size, inline: true }, nativeOn: { submit: this.handleSearch } }}
        >
          {normalizedOptions.fixed.map((option) => {
            return (
              <el-form-item key={option.prop} label={option.label} prop={option.prop}>
                {renderEditor(this._c, { ...option, className: 'dg-search__component' }, form)}
              </el-form-item>
            )
          })}

          {currentOptional && (
            <el-form-item class="dg-search-optional" key={currentOptional.prop}>
              <el-select
                class="dg-search-optional__select"
                value={currentOptional.prop}
                onChange={this.handleCurrentOptionalChange}
              >
                {normalizedOptions.optional.map((option) => {
                  return <el-option key={option.prop} label={option.label} value={option.prop}></el-option>
                })}
              </el-select>

              {renderEditor(this._c, { ...currentOptional, className: 'dg-search-optional__component' }, form)}
            </el-form-item>
          )}

          <div class="dg-search__spacer"></div>

          {!!normalizedOptions.optional.length && (
            <el-button
              class="dg-search-optional__more"
              type="plain"
              size="mini"
              icon={moreIcon}
              onClick={this.toggleShowPanel}
            >
              {moreText}
            </el-button>
          )}
          {searchButtonProps.visible !== false && (
            <el-form-item class="dg-search-form__submit">
              <el-button
                {...{
                  props: { size: 'mini', type: 'primary', icon: 'el-icon-search', ...searchButtonProps },
                  directives: searchButtonDirectives,
                }}
                native-type="submit"
              >
                {searchText}
              </el-button>
              <el-button
                {...{
                  props: { size: 'mini', type: 'secondary', icon: 'el-icon-refresh-left' },
                }}
                onClick={this.handleReset}
              >
                {resetText}
              </el-button>
            </el-form-item>
          )}
        </el-form>

        {!!normalizedOptions.optional.length && (
          <div class="dg-search-result">
            <span class="dg-search-result__label">搜索：</span>
            {result.map((item) => {
              return (
                <el-tag
                  key={item.prop}
                  class="dg-search-result-item"
                  closable
                  size="mini"
                  disable-transitions
                  onClose={() => this.deleteSearchOption(item)}
                >
                  {item.label}: {this.formatResult(item)}
                </el-tag>
              )
            })}

            {result.length ? (
              <el-button class="dg-search-result__clear" type="text" size="mini" onClick={this.deleteAllSearchOption}>
                {clearText}
              </el-button>
            ) : (
              <span class="dg-search-result__empty">{emptyText}</span>
            )}
          </div>
        )}

        {!!normalizedOptions.optional.length && (
          <el-popover class="dg-search-panel" {...{ props: { value: showPanel } }}>
            <el-form
              ref="dgSearchForm"
              class="dg-search-panel-form"
              {...{
                props: { model: form, size, labelWidth: '120px' },
                nativeOn: { submit: this.handleSearch },
              }}
            >
              {options.map((option) => {
                return (
                  <el-form-item key={option.prop} label={option.label} prop={option.prop}>
                    {renderEditor(this._c, { ...option, className: 'dg-search__component' }, form)}
                  </el-form-item>
                )
              })}

              <el-form-item class="dg-search-panel-form__submit">
                <el-button type="secondary" size="small" onClick={() => (this.showPanel = false)}>
                  取消
                </el-button>
                <el-button
                  {...{
                    props: { type: 'primary', ...searchButtonProps, size: 'small' },
                    directives: searchButtonDirectives,
                  }}
                  native-type="submit"
                >
                  {searchText}
                </el-button>
              </el-form-item>
            </el-form>
          </el-popover>
        )}
      </div>
    )
  },
}
</script>
