<script setup lang="ts">
import Star from "./ui/Star.vue";
import type {StarMoveEvent} from "./types";
import AAlert from "#adata-ui/components/elements/alert/AAlert.vue";

const modelValue = defineModel<number>(0)

interface Props {
  max?: number;
  readonly?: boolean;
  increment?: number;
  roundStartRating?: boolean;
  starSize?: number;
  activeOnClick?: boolean;
  fixedPoints?: number | null;
  gap?: number;
  ratingPlacement?: 'left' | 'right';
  fontSize?: number;
  error?: string | boolean;
  errorSize?: 'sm' | 'lg';
}

const props = withDefaults(defineProps<Props>(), {
  max: 5,
  increment: 1,
  readonly: false,
  roundStartRating: true,
  starSize: 16,
  activeOnClick: false,
  fixedPoints: null,
  gap: 4,
  ratingPlacement: 'left',
  fontSize: 14,
  error: false,
  errorSize: 'lg'
})

const fillLevel = ref<number[]>([])
const currentRating = ref<number>(modelValue.value)
const selectedRating = ref<number>(modelValue.value)
const ratingSelected = ref<boolean>(false)

const gapStyle = computed(() => {
  return {
    gap: `${props.gap}px`
  }
})
const roundedRating = computed(() => {
  const inv = 1.0 / props.increment
  return Math.min(props.max, Math.ceil(currentRating.value * inv) / inv)
})
const shouldRound = computed(() => ratingSelected.value || props.roundStartRating)


watch(modelValue, (e)=>{
  currentRating.value = e
  selectedRating.value = e
  createStars()
})

const resetRating = () => {
  if (!props.readonly) {
    currentRating.value = selectedRating.value
    modelValue.value = selectedRating.value
    createStars(shouldRound.value)
  }
}

const createStars = (round: boolean = true, applyFill: boolean = true) => {
  currentRating.value = (round) ? roundedRating.value : currentRating.value

  for (let i = 0; i < props.max; i++) {
    let level = 0
    if (i < currentRating.value) {
      level = (currentRating.value - i > 1) ? 100 : (currentRating.value - i) * 100
    }
    if (applyFill) {
      fillLevel.value[i] = Math.round(level)
    }
  }
}

const setRating = (event: StarMoveEvent, persist: boolean) => {
  if (!props.readonly) {
    const position = event.position / 100
    currentRating.value = ((event.id + position) - 1).toFixed(2)
    currentRating.value = (currentRating.value > props.max) ? props.max : currentRating.value
    modelValue.value = roundedRating.value
    if (persist) {
      createStars(true, true)
      selectedRating.value = (currentRating.value === selectedRating.value) ? 0 : currentRating.value
      modelValue.value = selectedRating.value
      ratingSelected.value = true

    } else {
      createStars(true, !props.activeOnClick)
    }
  }
}

createStars(shouldRound.value)
</script>

<template>
  <div>
    <div class="w-full flex items-center gap-2">
      <span
        v-if="ratingPlacement === 'left'"
        class="text-gray-600 dark:text-gray-300 mt-px"
        :style="{fontSize: `${fontSize}px`}"
      >
        {{ modelValue }}
      </span>
      <div
        :class="{'pointer-events-none': readonly}"
        class="flex items-center"
        :style="gapStyle"
        @mouseleave="resetRating"
      >
        <star
          v-for="n in max"
          :key="n"
          :fill="fillLevel[n-1]"
          :size="starSize"
          :star-id="n"
          @star-selected="setRating($event, true)"
        />
      </div>
      <span
        v-if="ratingPlacement === 'right'"
        class="text-sm text-gray-600 dark:text-gray-300 mt-px"
      >
        {{ modelValue }}
      </span>
    </div>
    <Transition name="slide-in">
      <div
        v-if="error && typeof error === 'string'"
        class="mt-1"
      >
        <a-alert
          :color="'red'"
          :size="errorSize"
          :icon-type="'triangle'"
        >
          <template #default>
            <span
              v-for="(err, index) in error.split(', ')"
              :key="index"
            >{{ err }}<br></span>
          </template>
        </a-alert>
      </div>
    </Transition>
  </div>
</template>

<style scoped>
.slide-in-enter-active {
  z-index: 0;
  opacity: 0;
  transform: translateY(-50%);
  transition: transform 0.5s;
}

.slide-in-enter-to {
  z-index: auto;
  opacity: 1;
  transform: translateY(0);
}

.slide-in-leave-active {
  opacity: 1;
  transform: translateY(0);
}

.slide-in-leave-to {
  opacity: 0;
  transform: translateY(-100%);
}
</style>
