<template>
  <div
    class="vc-switch"
    :data-test="`switch-${ dataTestName }`"
    :tabindex="tabIndex"
    :class="[switchClass, {disabled, 'reverse-view': switchReverseView}]"
    @click="triggerToggle"
    @keypress="handleKeyPress($event)"
  >
    <div
      class="vc-switch-container"
      :class="colorClasses"
    >
      <div class="vc-switch-circle" />
    </div>
    <span
      v-if="!hideLabel && (labels.length || $slots.hasOwnProperty('default'))"
      class="vc-switch-label"
      :class="commonLabelClass"
    >
      <slot>
        <span>{{ switchLabel }}</span>
      </slot>
    </span>
  </div>
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue'
import defaultComponent from '@/mixins/defaultComponent.mixin'
import { VcSwitchValueType } from './VcSwitch.types'
import color from '@/mixins/input/color.mixin'

export default defineComponent({
  name: 'VcSwitch',
  extends: defaultComponent,
  mixins: [color],
  model: {
    prop: 'modelValue'
  },
  props: {
    modelValue: {
      type: [String, Number, Boolean] as PropType<VcSwitchValueType>,
      default: ''
    },
    values: {
      type: [Array],
      default: () => {
        return []
      }
    },
    labels: {
      type: Array,
      default: () => {
        return []
      }
    },
    hideLabel: {
      type: Boolean,
      default: false
    },
    disabled: {
      type: Boolean,
      default: false
    },
    switchReverseView: {
      type: Boolean,
      default: false
    },
    labelClass: {
      type: String,
      default: ''
    },
    onInput: {
      type: Function,
      default: null,
      description: 'Temporary @input method'
    },
    dataTestName: {
      type: String,
      default: 'default'
    }
  },
  emits: ['input', 'update:modelValue'],
  data () {
    return {
      model: '' as VcSwitchValueType,
      colorClass: 'vc-switch-color-',
      modelInitialized: false
    }
  },
  computed: {
    colorClasses (): string {
      const colorClass = this.color ? (this.colorClass + this.color) : ''

      return [
        colorClass
      ].join(' ')
    },
    switchClass () {
      return `vc-switch-${this.model === this.enabledValue ? 'on' : 'off'}`
    },
    switchLabel () {
      let label = ''
      let labelEnabled = 'enabled'
      let labelDisabled = 'disabled'
      if (this.labels?.length) {
        labelEnabled = this.labels[0] as string
        labelDisabled = this.labels[1] as string
      }
      label = this.isEnabled ? labelEnabled : labelDisabled
      return label
    },
    commonLabelClass () {
      let labelClass = 'text-base'
      if (this.labelClass) {
        labelClass = this.labelClass
      }
      return labelClass
    },
    isEnabled () {
      return this.model === this.enabledValue
    },
    enabledValue () {
      if (this.values.length > 1) {
        return this.values[0]
      } else {
        return typeof this.model === 'number' ? 1 : true
      }
    },
    disabledValue () {
      if (this.values.length > 1) {
        return this.values[1]
      } else {
        return typeof this.model === 'number' ? 0 : false
      }
    },
    tabIndex () {
      return this.disabled ? -1 : 0
    }
  },
  watch: {
    modelValue: {
      handler (newV: VcSwitchValueType) {
        this.model = newV
      },
      immediate: true
    },
    model (newV: VcSwitchValueType) {
      if (this.modelInitialized) {
        this.update(newV)
      }
      this.modelInitialized = true
    }
  },
  methods: {
    triggerToggle () {
      if (this.disabled) {
        return
      }
      let val: unknown
      if (this.values?.length || typeof this.model !== 'boolean') {
        val = this.model === this.enabledValue ? this.disabledValue : this.enabledValue
      } else {
        val = !this.model
      }
      this.modelInitialized = true
      this.model = val
    },
    handleKeyPress (e: KeyboardEvent) {
      const keyCode = e.keyCode as number
      const isSpace = keyCode === 32
      if (isSpace) {
        e.preventDefault()
        this.triggerToggle()
      }
    },
    update (value: VcSwitchValueType) {
      if (this.$attrs && this.$attrs.onInput) {
        this.$emit('input', value)
      } else if (this.onInput && typeof this.onInput === 'function') {
        this.onInput(value)
      } else {
        this.$emit('update:modelValue', value)
      }
    }
  }
})
</script>

<style lang="scss" scoped>
$circle-size: 0.75em;
$circle-padding: 0.1875em;
$switch-height: 1.125em;
$switch-width: 1.875em;

.vc-switch {
  @apply cursor-pointer outline-none select-none inline-flex items-center text-default-text;
  min-height: $switch-height;

  .vc-switch-label {
    @apply align-top pl-2.5;
    line-height: $switch-height;
  }
  .vc-switch-mobile-label {
    @apply align-top pr-2;
    line-height: $switch-height;
  }

  &.text-sm {
    .vc-switch-label {
      font-size: inherit;
    }
  }

  &.disabled {
    @apply text-default-text cursor-not-allowed opacity-50;
  }

  .vc-switch-container {
    @apply rounded-full relative inline-block align-top bg-active-elements transition-all duration-300 ease-in;
    width: $switch-width;
    min-width: $switch-width;
    height: $switch-height;
    &.vc-switch-color-primary {
      @apply bg-primary-actions--focus;
    }
    &.vc-switch-color-secondary {
      @apply bg-secondary-actions--focus;
    }
    &.vc-switch-color-destructive {
      @apply bg-destructive-actions--focus;
    }

    .vc-switch-circle {
      @apply absolute bg-white rounded-full;
      top: 50%;
      transform: translateY(-50%);
      width: $circle-size;
      height: $circle-size;
      transition: margin-left .3s ease-in-out, width .1s linear;
      margin-left: $circle-padding;
    }
  }

  &.vc-switch-on .vc-switch-container .vc-switch-circle {
    margin-left: calc(100% - #{$circle-size} - #{$circle-padding});
  }

  &.vc-switch-off {
    .vc-switch-container {
      @apply bg-inactive-elements;
    }
  }

  &:hover:not(.disabled):not(.vc-switch-off) > .vc-switch-container {
    @apply bg-active-elements--focus;
    &.vc-switch-color-primary {
      @apply bg-primary-actions--focus;
    }
    &.vc-switch-color-secondary {
      @apply bg-secondary-actions--focus;
    }
    &.vc-switch-color-destructive {
      @apply bg-destructive-actions--focus;
    }
  }
  &:hover:focus:not(.disabled):not(.vc-switch-off) > .vc-switch-container {
    @apply bg-active-elements--pressed;
    &.vc-switch-color-primary {
      @apply bg-primary-actions--pressed;
    }
    &.vc-switch-color-secondary {
      @apply bg-secondary-actions--pressed;
    }
    &.vc-switch-color-destructive {
      @apply bg-destructive-actions--pressed;
    }
  }

  &:hover:not(.disabled) > .vc-switch-container {
    @apply shadow-hover-outer;
    &.vc-switch-color-primary {
      @apply shadow-hover-outer-primary;
    }
    &.vc-switch-color-secondary {
      @apply shadow-hover-outer-secondary;
    }
    &.vc-switch-color-destructive {
      @apply shadow-hover-outer-destructive;
    }
  }
  &:focus-visible:not(.disabled) > .vc-switch-container {
    @apply shadow-focus-outer;
    &.vc-switch-color-primary {
      @apply shadow-focus-outer-primary;
    }
    &.vc-switch-color-secondary {
      @apply shadow-focus-outer-secondary;
    }
    &.vc-switch-color-destructive {
      @apply shadow-focus-outer-destructive;
    }
  }

  &.reverse-view {
    @apply inline-flex flex-row-reverse;

    .vc-switch-label {
      @apply pl-0 pr-2.5;
    }
  }
}

[dir="rtl"] {
  .vc-switch {
    &.vc-switch-on .vc-switch-circle {
      margin-left: unset;
      margin-right: calc(100% - #{$circle-size} - #{$circle-padding});
    }

    &.vc-switch-off .vc-switch-circle {
      margin-left: unset;
      margin-right: $circle-padding;
    }

    .vc-switch-container .vc-switch-circle {
      transition: margin-right .3s ease-in-out, width .1s linear;
    }

    .vc-switch-label {
      @apply pl-0 pr-2.5;
    }

    &.reverse-view {
      @apply inline-flex flex-row-reverse;

      .vc-switch-label {
        @apply pr-0 pl-2.5;
      }
    }
  }
}
</style>
