<script setup lang="ts">
import get from 'lodash/get'
import kebabCase from 'lodash/kebabCase'
import { computed, toRef } from 'vue'

import { useLegendMarker } from '@/composables/useLegendMarker'
import { LegendCategory } from '@/enums'

defineOptions({
  name: 'OrdinalLegend'
})

export interface Datum { id?: string | number, color: string, path?: string, label: string }

export interface LegendOrdinalProps {
  data?: Datum[]
  horizontal?: boolean
  markerPath?: string | ((d: Datum) => string)
  categoryObjectsPath?: LegendCategory
  highlight?: string | number | null
  value?: string | number | null
}

const props = withDefaults(defineProps<LegendOrdinalProps>(), {
  data: () => [],
  horizontal: false,
  markerPath: 'M512 256C512 397.4 397.4 512 256 512C114.6 512 0 397.4 0 256C0 114.6 114.6 0 256 0C397.4 0 512 114.6 512 256z',
  categoryObjectsPath: LegendCategory.id,
  highlight: null,
  value: null
})

const emit = defineEmits<{
  'update': [value: string | number | null]
  'update:highlight': [value: string | number | null]
}>()

const { markerViewbox, markerPathFunction } = useLegendMarker(
  toRef(() => props.markerPath)
)

const classList = computed(() => {
  return {
    'ordinal-legend--horizontal': props.horizontal,
    'ordinal-legend--has-highlight': props.highlight !== null,
    'ordinal-legend--has-value': props.value !== null
  }
})

function itemClassList(d: Datum) {
  const id = d[props.categoryObjectsPath as LegendCategory] as string | number | undefined
  return {
    [`ordinal-legend__item--identifier-${kebabCase(d.label)}`]: true,
    'ordinal-legend__item--highlighted': id === props.highlight,
    'ordinal-legend__item--selected': id === props.value
  }
}

function update(d: Datum) {
  /**
   * Fired when user clicks on an item
   * @event update
   * @param Mixed Value of the category identifier
   */
  emit('update', get(d, props.categoryObjectsPath, null))
}

function updateHighlight(d: Datum | Record<string, never> = {}) {
  /**
   * Fired when user hover an item
   * @event update:highlight
   * @param Mixed Value of the category identifier
   */
  emit('update:highlight', get(d, props.categoryObjectsPath, null))
}
</script>

<template>
  <ul
    class="ordinal-legend list-unstyled"
    :class="classList"
  >
    <li
      v-for="d in data"
      :key="d[categoryObjectsPath]"
      class="ordinal-legend__item"
      :class="itemClassList(d)"
    >
      <a
        @click="update(d)"
        @mouseover="updateHighlight(d)"
        @mouseleave="updateHighlight()"
      >
        <span class="ordinal-legend__item__marker me-1">
          <slot
            name="marker"
            :marker="{ path: d.path, color: d.color }"
          >
            <svg :viewBox="markerViewbox">
              <path
                :d="markerPathFunction(d)"
                :fill="d.color"
                class="ordinal-legend__item__marker__path"
              />
            </svg>
          </slot>
        </span>
        <span class="ordinal-legend__item__label">
          <slot
            name="label"
            v-bind="d"
          >
            {{ d.label }}
          </slot>
        </span>
      </a>
    </li>
  </ul>
</template>

<style lang="scss" scoped>

.ordinal-legend {
  $muted-item-opacity: 0.2;
  $muted-item-filter: grayscale(30%) brightness(10%);
  $muted-item-transition:
    opacity 0.2s,
    filter 0.2s;

  font-size: $font-size-sm;

  &--horizontal &__item {
    display: inline-block;

    &:not(:last-child) {
      margin-right: $spacer;
    }
  }

  &__item {
    transition: $muted-item-transition;

    .ordinal-legend--has-highlight &:not(&--highlighted) {
      opacity: $muted-item-opacity;
      filter: $muted-item-filter;
    }

    &--selected {
      font-weight: bold;
    }

    a {
      color: inherit;
    }

    &__marker svg {
      height: 1em;
      width: 1em;
    }
  }
}
</style>
