<template>
  <label
    :class="{
      'mm-switch__mini': mini,
    }"
    class="mm-switch">
    <input
      :disabled="realLoading || realDisabled"
      v-bind="attrs"
      type="checkbox"
      @change="handleChange($event, $event.target.checked)" />

    <div class="mm-switch-faux">
      <slot :checked="localChecked">
        <div class="mm-switch-input">
          <span
            class="mm-switch-checked-box">
            <slot name="checked"></slot>
          </span>
          <span class="mm-switch-icon-wrap">
            <ui-icon
              v-if="!realLoading && icon"
              :name="icon"
              v-bind="realIconProps"
              class="mm-switch-icon"></ui-icon>
            <ui-loading
              v-if="realLoading"
              :size="loadingSize"
              class="mm-switch--loading"></ui-loading>
          </span>
          <span
            class="mm-switch-unchecked-box">
            <slot name="unchecked"></slot>
          </span>
        </div>
      </slot>
      <div
        class="mm-switch__pending-hint">
        <slot
          name="hint"
          :checked="localChecked"
          :loading="realLoading">
        </slot>
      </div>
    </div>
  </label>
</template>

<script>
import Loading from "../loading"
import input from "../../mixins/input"
import Icon from "../icon"
import config from "../../managers/config"

config.defaults({
  "Switch.onValue": true,
  "Switch.offValue": false,
})

export default {
  name: "mm-switch",
  components: {
    "ui-loading": Loading,
    "ui-icon": Icon,
  },
  mixins: [input],
  inheritAttrs: false,
  model: {
    prop: "checked",
    event: "input",
  },
  props: {
    onValue: {
      type: null,
      default: () => config.get("Switch.onValue"),
    },
    offValue: {
      type: null,
      default: () => config.get("Switch.offValue"),
    },
    checked: null,
    icon: String,
    iconProps: {
      type: Object,
      default: () => ({}),
    },
    mini: {
      type: Boolean,
      default: false,
    },
    loading: {
      type: Boolean,
      default: false,
    },
    onChange: {
      type: Function,
    },
    beforeChange: {
      type: Function,
      default: () => {},
    },
  },
  data() {
    return {
      localChecked: this.checked,
      localLoading: this.loading,
    }
  },
  computed: {
    attrs() {
      return {
        ...this.$attrs,
        name: this.realName,
        disabled: this.realDisabled || this.realReadonly,
        checked: this.localChecked,
      }
    },
    realIconProps() {
      return {
        size: "12px",
        ...this.iconProps,
      }
    },
    loadingSize() {
      return this.mini ? "10px" : "16px"
    },
    realLoading() {
      return this.loading || this.localLoading
    },
  },
  watch: {
    checked(val) {
      this.localChecked = val
    },
  },
  methods: {
    handleChange(event, checked) {
      const triggerPending = value => {
        this.localLoading = value
        this.$emit("update:loading", value)
      }
      const preChecked = !checked
      const switchEle = event.target
      switchEle.checked = preChecked

      const returnValue = this.beforeChange(preChecked)

      triggerPending(true)
      Promise.resolve(returnValue).then((value = checked) => {
        this.localChecked = value ? this.onValue : this.offValue
        this.$emit("input", this.localChecked)
        this.$emit("change", event, this.localChecked)
        this.onChange && this.onChange(event, this.localChecked)
      }, () => {}).finally(() => {
        triggerPending(false)
      })
    },
  },
}
</script>
