<template>
  <div class="cpTelInput" :class="componentDynamicClass">
    <base-input-label v-if="label" v-bind-once="{ for: inputIdentifier }" :is-invalid :required :tooltip>
      {{ capitalizedLabel }}
    </base-input-label>
    <div class="cpTelInput__container">
      <vue-tel-input
        ref="telInputRef"
        v-model="inputModel"
        class="cpTelInput__wrapper"
        :class="wrapperDynamicClasses"
        :default-country
        :disabled
        :dropdown-options
        :input-options
        mode="international"
        placeholder="Enter your phone number"
        valid-characters-only
        @close="focusOnInput"
        @country-changed="handleCountryChanged"
        @open="handleDropdownOpen"
        @validate="handleOnValidate"
      >
        <template #arrow-icon>
          <cp-icon class="cpTelInput__arrow" size="20" type="chevron-down" />
        </template>
        <template #search-icon>
          <cp-icon class="cpTelInput__searchIcon" size="20" type="search" />
        </template>
      </vue-tel-input>
    </div>
    <cp-transition-expand mode="out-in">
      <p v-if="displayErrorMessage" :id="errorMessageId" class="cpTelInput__error">
        {{ errorMessage }}
      </p>
      <p v-else-if="displayHelp" :id="helpMessageId" class="cpTelInput__help">
        {{ help }}
      </p>
    </cp-transition-expand>
  </div>
</template>

<script setup lang="ts">
import type { ComponentPublicInstance } from 'vue'
import { computed, nextTick, ref, useAttrs, useId, useTemplateRef } from 'vue'

import type { Sizes } from '@/constants'

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

import { capitalizeFirstLetter } from '@/helpers'

interface Props {
  autofocus?: boolean
  defaultCountry?: string
  disabled?: boolean
  errorMessage?: string
  help?: string
  hideSearch?: boolean
  isInvalid?: boolean
  label?: string
  name?: string
  placeholder?: string
  required?: boolean
  size?: Sizes
  tooltip?: string
}

const props = withDefaults(defineProps<Props>(), {
  label: '',
  placeholder: 'Enter phone number',
  errorMessage: '',
  help: '',
  disabled: false,
  isInvalid: false,
  autofocus: false,
  required: false,
  tooltip: '',
  name: '',
  hideSearch: false,
  size: 'md',
  defaultCountry: undefined,
})

const emit = defineEmits<Emits>()

type PhoneObject = {
  country: string
  countryCode: string
  formatted: string
  nationalNumber: string
  possible: boolean
  valid: boolean
}

interface Emits {
  (e: 'countryChanged', countryCode: string): void
  (e: 'validate', phoneObject: PhoneObject): void
}

const inputModel = defineModel<string>()

type VueTelInputInstance = ComponentPublicInstance & {
  $el: HTMLElement
  focus: VoidFunction
}

const telInputRefElement = useTemplateRef<VueTelInputInstance>('telInputRef')

const helpMessageId = useId()
const errorMessageId = useId()

const attrs = useAttrs()

const componentDynamicClass = computed(() => [`cpTelInput--${props.size}`])

const wrapperDynamicClasses = computed(() => ({
  'cpTelInput__wrapper--isInvalid': props.isInvalid,
  'cpTelInput__wrapper--isDisabled': props.disabled,
}))

const dropdownOptions = computed(() => ({
  showSearchBox: !props.hideSearch,
  showFlags: true,
  tabindex: 0,
  searchBoxPlaceholder: 'Search country...',
}))

const inputOptions = computed(() => ({
  autofocus: props.autofocus,
  placeholder: props.placeholder,
  name: props.name,
  type: 'tel',
  styleClasses: {
    cpTelInput__input: true,
    'cpTelInput__input--isInvalid': props.isInvalid,
    'cpTelInput__input--isDisabled': props.disabled,
  },
}))

const inputIdentifier = ref<string>((attrs.id as string | undefined) || useId())

const capitalizedLabel = computed(() => capitalizeFirstLetter(props.label))

const displayErrorMessage = computed(() => props.isInvalid && props.errorMessage.length)

const displayHelp = computed(() => props.help?.length && !displayErrorMessage.value)

const focusOnInput = async () => {
  if (!telInputRefElement.value) return
  setTimeout(() => telInputRefElement.value?.focus(), 1)
}

const handleDropdownOpen = async () => {
  if (!telInputRefElement.value) return

  await nextTick()

  const searchBox = telInputRefElement.value?.$el?.querySelector('input.vti__search_box')

  if (!searchBox) return

  setTimeout(() => searchBox.focus(), 1)
}

const handleCountryChanged = (country: { iso2: string }) => emit('countryChanged', country?.iso2 || '')

const handleOnValidate = (phoneObject: PhoneObject) => emit('validate', phoneObject)
</script>

<style lang="scss">
.cpTelInput {
  position: relative;
  display: flex;
  flex-direction: column;

  &:has(.cpTelInput__help, .cpTelInput__error) .cpTelInput__container {
    margin-bottom: var(--cp-spacing-md);
  }

  &__container {
    position: relative;
    z-index: 9;
    display: flex;
  }

  &__wrapper {
    --box-shadow: var(--cp-shadows-3xs-inset);

    width: 100%;
    border: 0;
    border-radius: var(--cp-radius-md-lg);
    appearance: none;
    box-shadow:
      var(--box-shadow),
      0 0 0 var(--cp-dimensions-0_25) var(--cp-border-soft);
    color: inherit;

    &:not(.cpTelInput__wrapper--isDisabled):focus-within {
      box-shadow:
        var(--box-shadow),
        0 0 0 var(--cp-dimensions-0_25) var(--cp-border-soft-hover);

      &.cpTelInput__wrapper--isInvalid {
        box-shadow:
          var(--box-shadow),
          0 0 0 var(--cp-dimensions-0_25) var(--cp-border-error-solid);
      }
    }

    &:not(.cpTelInput__wrapper--isInvalid, .cpTelInput__wrapper--isDisabled):hover {
      box-shadow:
        var(--box-shadow),
        0 0 0 var(--cp-dimensions-0_25) var(--cp-border-soft-hover);
    }

    &--isInvalid {
      box-shadow:
        var(--box-shadow),
        0 0 0 var(--cp-dimensions-0_25) var(--cp-border-error-solid);
    }

    &.disabled {
      overflow: hidden;
      box-shadow:
        var(--box-shadow),
        0 0 0 var(--cp-dimensions-0_25) var(--cp-border-soft);

      .vti__dropdown {
        &:focus,
        &:focus-within {
          border-right: var(--cp-dimensions-0_25) solid var(--cp-border-strong);
        }
      }
    }
  }

  &__input {
    padding: var(--cp-spacing-md) var(--cp-spacing-xl);
    border-bottom-right-radius: var(--cp-radius-md-lg);
    border-top-right-radius: var(--cp-radius-md-lg);
    color: inherit;
    font-size: var(--cp-text-size-sm);
    line-height: var(--cp-line-height-md);

    &:is(:hover, :focus) {
      z-index: 1;
    }

    &:not(.cpTelInput__input--isInvalid, .cpTelInput__input--isDisabled):focus {
      background-color: var(--cp-background-primary);
      outline: var(--cp-dimensions-0_5) solid var(--cp-border-accent-solid);
    }

    &::placeholder {
      color: var(--cp-text-placeholder);
    }

    &--isDisabled {
      background-color: var(--cp-background-disabled);
      pointer-events: none;

      &::placeholder {
        color: var(--cp-text-disabled);
      }
    }
  }

  &__input--isInvalid {
    &:focus {
      outline: var(--cp-dimensions-0_5) solid var(--cp-border-error-solid);
    }

    .cpTelInput__input:focus {
      box-shadow: 0 0 0 var(--cp-dimensions-0_25) solid var(--cp-border-error-solid);
      outline: var(--cp-dimensions-0_5) solid var(--cp-border-error-solid);
    }
  }

  &__arrow,
  &__searchIcon {
    color: var(--cp-foreground-secondary);
    transition: transform 150ms ease;
  }

  .vti__dropdown.open .cpTelInput__arrow {
    color: var(--cp-foreground-primary);
    transform: rotate(180deg);
  }

  .vti__dropdown {
    position: unset;
    z-index: 1;
    border-right: var(--cp-dimensions-0_25) solid var(--cp-border-strong);

    &:focus,
    &:focus-within {
      z-index: 2;
      border-right: var(--cp-dimensions-0_25) solid transparent;
      outline: var(--cp-dimensions-0_5) solid var(--cp-border-accent-solid);
    }
  }

  &__wrapper--isInvalid {
    .vti__dropdown:focus,
    .vti__dropdown:focus-within {
      outline: var(--cp-dimensions-0_5) solid var(--cp-border-error-solid);
    }
  }

  &__wrapper--isDisabled {
    background-color: var(--cp-background-disabled);
    color: var(--cp-foreground-disabled);
    cursor: not-allowed;

    .vti__dropdown:focus {
      outline: none;
    }
  }

  .vti__selection {
    gap: var(--cp-spacing-md);

    .vti__flag {
      margin: 0;
    }
  }

  .vti__flag {
    min-width: var(--cp-dimensions-5);
    max-width: var(--cp-dimensions-5);
    border-radius: var(--cp-radius-xs);
  }

  .vti__dropdown {
    padding: var(--cp-spacing-md) var(--cp-spacing-md) var(--cp-spacing-md) var(--cp-spacing-lg);
    border-bottom-left-radius: var(--cp-radius-md-lg);
    border-top-left-radius: var(--cp-radius-md-lg);
  }

  .vti__search_box_container {
    position: sticky;
    top: 0;
    display: flex;
    margin-bottom: var(--cp-spacing-sm);
  }

  .vti__search_box {
    width: 100%;
    padding: var(--cp-spacing-sm) var(--cp-spacing-sm) var(--cp-spacing-sm) var(--cp-spacing-md);
    border: none;
    border-radius: var(--cp-radius-sm-md);
    appearance: none;
    box-shadow:
      var(--cp-shadows-3xs),
      0 0 0 var(--cp-dimensions-0_25) var(--cp-border-soft);
    color: inherit;
    font-size: var(--cp-text-size-sm);
    line-height: var(--cp-line-height-md);
    text-indent: calc(var(--cp-dimensions-1) * 7);

    &:hover {
      box-shadow: 0 0 0 var(--cp-dimensions-0_25) var(--cp-border-soft-hover);
    }

    &:focus {
      background-color: var(--cp-background-primary);
      outline: var(--cp-dimensions-0_5) solid var(--cp-border-accent-solid);
    }

    &::placeholder {
      color: var(--cp-text-placeholder);
    }
  }

  &__searchIcon {
    position: absolute;
    top: 50%;
    left: var(--cp-dimensions-3);
    width: var(--cp-dimensions-4);
    height: var(--cp-dimensions-4);
    transform: translateY(-50%);
  }

  .vti__dropdown-list {
    width: calc(100% + var(--cp-dimensions-0_5));
    max-height: calc(var(--cp-dimensions-1) * 32.5);
    padding: var(--cp-spacing-sm);
    border: var(--cp-dimensions-0_25) solid var(--cp-border-soft);
    border-radius: var(--cp-radius-md-lg);
    clip-path: inset(0 round var(--cp-radius-md-lg));

    &:has(.vti__search_box) {
      max-height: calc(var(--cp-dimensions-1) * 42.5);
    }

    &.below {
      top: calc(var(--cp-dimensions-10) + var(--cp-spacing-md));
    }

    &.above {
      bottom: calc(100% + var(--cp-dimensions-2));
    }
  }

  .vti__dropdown-item {
    display: flex;
    align-items: flex-start;
    padding: var(--cp-spacing-lg) var(--cp-spacing-xl) var(--cp-spacing-lg) var(--cp-spacing-lg);
    border-radius: var(--cp-radius-sm-md);
    font-size: var(--cp-text-size-sm);
    line-height: var(--cp-line-height-md);

    .vti__flag {
      margin: auto var(--cp-spacing-xl) auto 0;
    }

    strong,
    span:not(.vti__flag) {
      font-weight: 400;
    }

    strong {
      @extend %u-text-ellipsis;

      white-space: nowrap;
    }

    span:not(.vti__flag) {
      margin-left: var(--cp-spacing-sm);
    }
  }

  &--lg {
    .cpTelInput__input {
      padding: var(--cp-spacing-lg) var(--cp-spacing-xl);
      font-size: var(--cp-text-size-md);
    }

    .vti__dropdown {
      padding: var(--cp-spacing-lg) var(--cp-spacing-md) var(--cp-spacing-lg) var(--cp-spacing-lg);
    }

    .vti__search_box {
      padding: var(--cp-spacing-md) var(--cp-spacing-xl);
      text-indent: var(--cp-dimensions-6);
    }

    .cpTelInput__searchIcon {
      left: var(--cp-dimensions-4);
    }

    .vti__dropdown-list.below {
      top: var(--cp-dimensions-12) + var(--cp-spacing-md);
    }

    .vti__dropdown-item {
      font-size: var(--cp-text-size-md);
    }
  }

  &__help,
  &__error {
    font-size: var(--cp-text-size-sm);
    line-height: var(--cp-line-height-md);
  }

  &__help {
    color: var(--cp-text-secondary);
  }

  &__error {
    color: var(--cp-text-error-primary);

    &::first-letter {
      text-transform: capitalize;
    }
  }
}
</style>
