<template>
  <label class="cpSwitch" :class="computedClasses" :for="switchUniqueId" v-bind="inertProps">
    <span class="cpSwitch__switch">
      <input
        :id="switchUniqueId"
        :autofocus="autofocus"
        :checked="modelValue"
        :disabled="disabled"
        :name="groupName"
        type="checkbox"
        :value="modelValue"
        @change="handleClick(modelValue)"
      />
      <span class="cpSwitch__knobContainer">
        <span class="cpSwitch__knob" />
      </span>
    </span>
    <div class="cpSwitch__content">
      <div class="cpSwitch__labelContainer">
        <span v-if="label" class="cpSwitch__label">{{ label }}</span>
        <span v-if="isRequired" class="u-asterisk">*</span>
        <cp-tooltip v-if="tooltip" :content="tooltip">
          <cp-icon class="cpSwitch__tooltip" size="16" type="tooltip" />
        </cp-tooltip>
      </div>
      <span v-if="helper" class="cpSwitch__helper">{{ helper }}</span>
    </div>
  </label>
</template>

<script setup lang="ts">
import { computed, useId } from 'vue'

import CpTooltip from '@/components/CpTooltip.vue'

import { ToggleColors } from '@/constants'
import { capitalizeFirstLetter } from '@/helpers'

interface Props {
  autofocus?: boolean
  color?: ToggleColors
  disabled?: boolean
  groupName?: string
  helper?: string
  inert?: boolean
  isRequired?: boolean
  label?: string
  modelValue?: boolean
  reverseLabel?: boolean
  tooltip?: string
}

interface Emits {
  (e: 'update:modelValue', value: boolean): void
}

const props = withDefaults(defineProps<Props>(), {
  modelValue: false,
  label: '',
  helper: '',
  disabled: false,
  groupName: '',
  color: 'purple',
  reverseLabel: false,
  autofocus: false,
  isRequired: false,
  tooltip: '',
  inert: false,
})

const emit = defineEmits<Emits>()

const switchUniqueId = useId()

const capitalizedColor = computed(() => {
  return capitalizeFirstLetter(props.color)
})

const inertProps = computed(() => {
  if (!props.inert) return

  return {
    'aria-hidden': true,
    tabindex: -1,
    inert: true,
  }
})

const computedClasses = computed(() => {
  return [
    {
      'cpSwitch--hasLabel': props.label,
      'cpSwitch--isActive': props.modelValue,
      'cpSwitch--isDisabled': props.disabled,
      'cpSwitch--isReversed': props.reverseLabel,
    },
    `cpSwitch--is${capitalizedColor.value}`,
  ]
})

const handleClick = (value: boolean): void => {
  emit('update:modelValue', !value)
}
</script>

<style lang="scss">
@mixin cp-switch-style($color, $className) {
  &--is#{$className}#{&}--isActive:not(#{&}--isDisabled) &__switch {
    background-color: $color;
  }

  &--is#{$className}:has(input:focus-visible) &__switch {
    outline: $color solid calc(var(--cp-dimensions-0_5) * 1.5);
    outline-offset: calc(var(--cp-dimensions-0_5) * 1.5);
  }

  &--is#{$className}:hover:not(#{&}--isDisabled) &__switch {
    background-color: var(--cp-background-quaternary-hover);
  }

  &--is#{$className}#{&}--isActive:not(#{&}--isDisabled):hover &__switch {
    background-color: $color;
  }
}

.cpSwitch {
  --cp-switch-base-height: var(--cp-dimensions-5);

  align-items: center;

  &,
  * {
    cursor: pointer;
  }

  &__content {
    display: flex;
    flex-direction: column;
  }

  &__helper {
    color: var(--cp-text-secondary);
    line-height: var(--cp-line-height-md);
  }

  &__labelContainer {
    display: flex;
    align-items: center;
    gap: var(--cp-spacing-sm);
  }

  &__label {
    line-height: var(--cp-line-height-md);
  }

  &__tooltip {
    padding: var(--cp-spacing-sm);
    color: var(--cp-foreground-quaternary);
  }

  &--hasLabel {
    display: grid;
    align-items: flex-start;
    gap: var(--cp-spacing-lg);
    grid-template-columns: min-content 1fr;
  }

  &--isReversed {
    grid-template-columns: 1fr min-content;

    .cpSwitch__content {
      grid-row: 1;
    }
  }

  &__knobContainer {
    display: flex;
    width: 100%;
    align-items: center;
    padding: var(--cp-dimensions-0_5);
    transition: transform 300ms var(--cp-easing-elastic);
  }

  &__knob {
    width: calc(var(--cp-switch-base-height) - var(--cp-dimensions-1));
    height: calc(var(--cp-switch-base-height) - var(--cp-dimensions-1));
    border-radius: var(--cp-radius-full);
    background-color: var(--cp-foreground-white);
    box-shadow: var(--cp-shadows-3xs);
  }

  &--isActive .cpSwitch {
    &__knobContainer {
      transform: translate3d(50%, 0, 0);
    }
  }

  &__switch {
    position: relative;
    display: flex;
    overflow: hidden;
    width: calc(var(--cp-switch-base-height) * 2);
    height: var(--cp-switch-base-height);
    border-radius: var(--cp-radius-full);
    background-color: var(--cp-background-quaternary);
    margin-block: var(--cp-spacing-xs);
    transition: background-color 100ms ease-out;
  }

  &--isDisabled,
  &--isDisabled:hover {
    &,
    * {
      cursor: not-allowed;
    }

    .cpSwitch__switch {
      border: var(--cp-dimensions-0_25) solid var(--cp-border-disabled);
      background-color: var(--cp-background-disabled);
    }

    .cpSwitch__label,
    .cpSwitch__helper {
      color: var(--cp-text-disabled);
    }

    .cpSwitch__knob {
      width: calc(var(--cp-switch-base-height) * 0.5 - var(--cp-dimensions-0_5));
      height: calc(var(--cp-switch-base-height) * 0.5 - var(--cp-dimensions-0_5));
      background-color: var(--cp-foreground-disabled);
      box-shadow: none;
    }

    .cpSwitch__knobContainer {
      padding: calc(var(--cp-switch-base-height) * 0.25 + var(--cp-dimensions-0_25));
    }
  }

  @include cp-switch-style(var(--cp-background-accent-solid), 'Accent');
  @include cp-switch-style(var(--cp-background-accent-solid), 'Purple'); // TODO: Should be replace by ACCENT

  input {
    position: absolute;
    z-index: 1;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
  }
}
</style>
