<template>
  <div class="ais-field flex flex-col gap-1" :class="shellClasses">
    <slot name="label" :id="fieldId" :label="label" :required="required">
      <label :for="fieldId" class="ml-0.5" :class="labelClasses">
        {{label}} <AisIcon v-if="required" icon="required" class="ml-0.5 mb-1 !text-xs" />
      </label>
    </slot>

    <slot name="default" :id="fieldId" :label="label" :required="required" />

    <slot name="description" :id="fieldId" :description="description">
      <div v-if="description && !hasErrors" class="ais-description">
        {{description}}
      </div>
    </slot>

    <slot name="errors" :errors="errorList">
      <ul v-if="hasErrors" class="ais-field-errors m-0 mt-1 pl-1.5 list-none text-danger text-base">
        <li v-for="error in errorList" class="">
          {{error}}
        </li>
      </ul>
    </slot>
  </div>
</template>

<script setup>

  import { ref, computed, provide, inject } from "vue";
  import {
    CONTROL_ID, CONTROL_NAME, CONTROL_DISABLED,
    CONTROL_REQUIRED, CONTROL_INVALID, CONTROL_SIZE,
  } from "@/util/controls";
  import {expectedValues} from "@/util/prop-validators";
  import {ensureArray, labelToId, valueForPath} from "@/util/util";

  import AisIcon from '@/components/icon/AisIcon.vue'
  import {useInjectedProp, useSize} from "@/composables/useInjections";

  const props = defineProps({
    id: { type: String, required: false, default: undefined },

    name: { type: String, required: false, default: undefined },

    label: { type: String, required: true },

    /**
     * Control the display of the field's label.
     *
     * normal: visible to users & screen readers (default option)
     * sr-only: visible only to screen readers -- does not occupy space on the page.
     * transparent: visible to screen readers -- holds label space on the page.
     *              transparent can be used to hold alignment with fields with labels.
     */
    labelMode: {
      type: String, required: false, default: 'normal',
      validator: expectedValues('normal', 'sr-only', 'transparent')
    },

    size: {
      type: String, required: false, default: undefined,
      validator: expectedValues('sm', 'md', 'lg')
    },

    required: { type: Boolean, required: false, default: false },

    disabled: { type: Boolean, required: false, default: false },

    description: { type: String, required: false, default: undefined },

    errors: { type: [String, Array], required: false, default: undefined },
  })

  const fieldId = computed(() => {
    return (props.id) ? props.id : labelToId(props.label)
  })

  const fieldSize = useSize(props)
  const injectedDisabled = useInjectedProp(CONTROL_DISABLED, props, 'disabled')
  const fieldDisabled = computed(() => injectedDisabled.value || props.disabled)

  const labelClasses = computed(() => {
    let labelClasses = []

    switch (props.labelMode) {
      case 'sr-only':
        labelClasses.push('sr-only')
        break
      case 'transparent':
        labelClasses.push('opacity-0')
        break;
    }

    if(fieldDisabled.value) {
      labelClasses.push('cursor-not-allowed')
    }

    return labelClasses
  })

  const errorList = computed(() => ensureArray(props.errors))
  const hasErrors = computed(() => errorList.value.length > 0)

  const shellClasses = computed(() => {
    let classes = []
    if(fieldSize.value) classes.push(fieldSize.value)
    if(fieldDisabled.value) classes.push('opacity-60', 'cursor-not-allowed')
    return classes
  })

  provide(CONTROL_ID, fieldId)
  provide(CONTROL_NAME, computed(() => props.name))
  provide(CONTROL_REQUIRED, computed(() => props.required))
  provide(CONTROL_SIZE, fieldSize)
  provide(CONTROL_DISABLED, fieldDisabled)
  provide(CONTROL_INVALID, hasErrors)
</script>
