<template>
  <label
    :class="className"
    :style="toggleStyle"
  >
    <input
      :id="inputId"
      type="checkbox"
      class="l-toggle-input"
      :name="name"
      :checked="value"
      @change.stop="toggle"
      @focus="onFocus"
      @blur="onBlur"
    >
    <div
      class="l-toggle-core"
      :style="coreStyle"
    >
      <div
        class="l-toggle-button"
        :style="buttonStyle"
      >
        <!-- <l-icon v-if="loading" name="loader-spinner" type="neutral" /> -->
      </div>
      <template v-if="labels">
        <span
          v-if="toggled"
          class="l-toggle-label v-left"
          :style="labelStyle"
        >
          <!-- Custom checked content -->
          <slot name="checked">
            {{ labelChecked }}
          </slot>
        </span>
        <span
          v-else
          class="l-toggle-label v-right"
          :style="labelStyle"
        >
          <!-- Custom unchecked content -->
          <slot name="unchecked">
            {{ labelUnchecked }}
          </slot>
        </span>
      </template>
    </div>
    <div
      v-if="title || description"
      class="l-toggle-content"
    >
      <span
        v-if="title"
        class="l-toggle-title"
      >{{ title }}</span>
      <span
        v-if="description"
        class="l-toggle-description"
      >
        {{ description }}
      </span>
    </div>
  </label>
</template>

<script>
// import LIcon from "./LIcon.vue";
const DEFAULT_LABEL_CHECKED = "Yes";
const DEFAULT_LABEL_UNCHECKED = "No";
const MARGIN = 3;
const contains = (object, title) =>
  typeof object === "object" && object.hasOwnProperty(title);
const px = (v) => v + "px";

export default {
  name: "LToggle",
  components: {
    // LIcon,
  },
  props: {
    /**
     * Use `v-model` instead
     */
    value: {
      type: Boolean,
    },
    /**
     * Title for the field - if needed
     */
    title: {
      type: String,
    },
    /**
     * Description for the field - if needed
     */
    description: {
      type: String,
    },
    /**
     * Name to attach to the generated input field
     */
    name: {
      type: String,
    },
    /**
     * Switch does not react on mouse events
     */
    disabled: {
      type: Boolean,
      default: false,
    },
    /**
     * Add a loading state to the switch.
     */
    // loading: {
    //   type: Boolean,
    //   default: false,
    // },
    /**
     * If set to `true`, will be watching changes in `value` property and overwrite the current state of the switch whenever `value` prop changes
     */
    sync: {
      type: Boolean,
      default: true,
    },
    /**
     * Color of the switch background when checked
     *
     * Use any of the color & shade values from the tokens, ie. `white`, `neutral-100`, `primary-500` etc.
     *
     */
    activeColor: {
      type: [String],
      default: "primary-500",
    },
    /**
     * Color of the switch background when unchecked
     *
     * Use any of the color & shade values from the tokens, ie. `white`, `neutral-100`, `primary-500` etc.
     *
     */
    inactiveColor: {
      type: [String],
      default: "neutral-200",
    },
    /**
     * Color of the switch button when checked
     *
     * Use any of the color & shade values from the tokens, ie. `white`, `neutral-100`, `primary-500` etc.
     */
    switchActiveColor: {
      type: [String],
      default: "white",
    },
    /**
     * Color of the switch button when unchecked
     *
     * Use any of the color & shade values from the tokens, ie. `white`, `neutral-100`, `primary-500` etc.
     */
    switchInactiveColor: {
      type: [String],
      default: "white",
    },
    /**
     * If `Boolean` - shows/hides default labels ("on" and "off")
     *
     * If `Object` - sets custom labels for both states.
     *
     * Example: `{
        "checked": "Foo",
        "unchecked": "Bar"
      }`
     */
    labels: {
      type: [Boolean, Object],
      default: false,
      validator(value) {
        return typeof value === "object"
          ? value.checked || value.unchecked
          : typeof value === "boolean";
      },
    },
    /**
     * Height of the switch
     */
    height: {
      type: Number,
      default: 30,
    },
    /**
     * Width of the switch
     */
    width: {
      type: Number,
      default: 60,
    },
     /**
     * ID used to identify the input, useful for testing or tracking purposes
     */
    inputId: {
      type: String,
      default: null,
    }
  },
  data() {
    return {
      toggled: !!this.value,
      focused: false,
    };
  },
  computed: {
    className() {
      let { toggled, disabled } = this;
      return [
        "l-toggle",
        { toggled, disabled },
        this.focused ? (toggled ? "focused-right" : "focused-left") : "",
        this.focused ? "focused" : "",
      ];
    },
    coreStyle() {
      return {
        width: px(this.width),
        height: px(this.height),
        borderRadius: px(Math.round(this.height / 2)),
      };
    },
    toggleStyle() {
      return {
        "--toggle-core-bg": this.colorCurrent,
        "--toggle-switch-bg": this.switchColorCurrent,
      };
    },
    buttonRadius() {
      return this.height - MARGIN * 2;
    },
    distance() {
      return px(this.width - this.height + MARGIN);
    },
    buttonStyle() {
      const transform = this.toggled
        ? `translate3d(${this.distance}, 3px, 0px)`
        : null;
      return {
        width: px(this.buttonRadius),
        height: px(this.buttonRadius),
        transform,
      };
    },
    labelStyle() {
      return {
        lineHeight: px(this.height),
      };
    },
    colorChecked() {
      let { activeColor } = this;
      return `var(--${activeColor})`;
    },
    colorUnchecked() {
      let { inactiveColor } = this;
      return `var(--${inactiveColor})`;
    },
    colorCurrent() {
      return this.toggled ? this.colorChecked : this.colorUnchecked;
    },
    labelChecked() {
      const { labels } = this;
      return contains(labels, "checked")
        ? labels.checked
        : DEFAULT_LABEL_CHECKED;
    },
    labelUnchecked() {
      const { labels } = this;
      return contains(labels, "unchecked")
        ? labels.unchecked
        : DEFAULT_LABEL_UNCHECKED;
    },
    switchColorChecked() {
      let { switchActiveColor } = this;
      return `var(--${switchActiveColor})`;
    },
    switchColorUnchecked() {
      let { switchInactiveColor } = this;
      return `var(--${switchInactiveColor})`;
    },
    switchColorCurrent() {
      return this.toggled ? this.switchColorChecked : this.switchColorUnchecked;
    },
  },
  watch: {
    value(value) {
      if (this.sync) {
        this.toggled = !!value;
      }
    },
  },
  methods: {
    /** Triggered when switch value changed
     * @event change
     * @type {Event}
     */
    toggle(event) {
      this.toggled = !this.toggled;
      this.$emit("input", this.toggled);
      this.$emit("change", {
        value: this.toggled,
        srcEvent: event,
      });
    },
    onFocus() {
      this.focused = true;
    },
    onBlur() {
      this.focused = false;
    },
  },
};
</script>

<style>
.l-toggle {
  display: inline-flex;
  align-content: center;
  position: relative;
  user-select: none;
  font-size: var(--sizing-xs);
  cursor: pointer;
  gap: var(--spacing-3);
}
/* Hide native checkbox input */
.l-toggle-input {
  opacity: 0;
  position: absolute;
  width: 1px;
  height: var(--spacing-px);
}
.l-toggle-label {
  position: absolute;
  top: 0;
  font-weight: 600;
  color: var(--toggle-switch-bg);
  z-index: 40;
}
.l-toggle-label.v-left {
  left: 10px;
}
.l-toggle-label.v-right {
  right: 10px;
}
.l-toggle-core {
  display: block;
  position: relative;
  box-sizing: border-box;
  outline: 0;
  margin: 0;
  transition: border-color var(--duration-150),
    background-color var(--duration-150);
  user-select: none;
  background-color: var(--toggle-core-bg);
  flex-shrink: 0;
}
.l-toggle.focused .l-toggle-core {
  outline-color: var(--toggle-core-bg);
  outline-width: var(--spacing-0-5);
  outline-offset: var(--spacing-px);
  outline-style: solid;
}
.l-toggle-button {
  display: block;
  position: absolute;
  overflow: hidden;
  top: 0;
  left: 0;
  transform: translate3d(3px, 3px, 0);
  border-radius: var(--radius-full);
  background-color: var(--toggle-switch-bg);
  z-index: 40;
  transition: transform var(--duration-300) cubic-bezier(0.68, -0.4, 0.32, 1.4);
  box-shadow: var(--shadow-lg);
}
.l-toggle-button .svg-icon {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  margin: auto;
}
.l-toggle.disabled {
  pointer-events: none;
  opacity: 0.6;
  cursor: not-allowed;
}
.l-toggle-content {
  display: flex;
  flex-direction: column;
}
.l-toggle-title {
  font-size: var(--sizing-base);
  font-weight: 600;
  line-height: 1.2;
  color: var(--text-color-secondary);
  padding: var(--spacing-1-5) 0;
}
</style>
