<template>
  <div ref="panelRef" class="iro-picker-panel">
    <div class="iro-picker-panel__box-row">
      <div ref="boxPickerMountRef" class="iro-picker-panel__box-mount"></div>
    </div>

    <div class="iro-picker-panel__slider-row">
      <div ref="sliderPickerMountRef" class="iro-picker-panel__slider-mount"></div>
      <div class="iro-picker-panel__swatch-wrap">
        <div class="iro-picker-panel__swatch" :style="swatchStyle"></div>
      </div>
    </div>

    <div class="iro-picker-panel__footer">
      <div class="iro-picker-panel__fields">
        <el-select v-model="displayFormat" size="small" class="iro-picker-panel__format">
          <el-option label="RGBA" value="RGBA" />
          <el-option label="HEX" value="HEX" />
          <el-option label="HSLA" value="HSLA" />
        </el-select>

        <template v-if="displayFormat === 'RGBA'">
          <el-input v-model="rgbInput.r" size="small" @change="applyRgbInput" />
          <el-input v-model="rgbInput.g" size="small" @change="applyRgbInput" />
          <el-input v-model="rgbInput.b" size="small" @change="applyRgbInput" />
          <el-input v-model="rgbInput.a" size="small" @change="applyRgbInput" />
        </template>

        <template v-else-if="displayFormat === 'HSLA'">
          <el-input v-model="hslInput.h" size="small" @change="applyHslInput" />
          <el-input v-model="hslInput.s" size="small" @change="applyHslInput" />
          <el-input v-model="hslInput.l" size="small" @change="applyHslInput" />
          <el-input v-model="hslInput.a" size="small" @change="applyHslInput" />
        </template>

        <template v-else>
          <el-input v-model="hexInput" size="small" class="iro-picker-panel__hex" @change="applyHexInput" />
        </template>
      </div>
    </div>
  </div>
</template>

<script setup>
import iro from '@jaames/iro'
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'

const props = defineProps({
  modelValue: {
    type: String,
    default: '#1764FF',
  },
  visible: {
    type: Boolean,
    default: false,
  },
})

const emit = defineEmits(['update:modelValue'])

const panelRef = ref(null)
const boxPickerMountRef = ref(null)
const sliderPickerMountRef = ref(null)
const boxPickerInstance = ref(null)
const sliderPickerInstance = ref(null)
const displayFormat = ref('RGBA')
const rgbInput = ref({ r: '23', g: '100', b: '255', a: '1' })
const hslInput = ref({ h: '221', s: '100', l: '55', a: '1' })
const hexInput = ref('#1764FF')
const swatchRgba = ref('rgba(23, 100, 255, 1)')
let resizeObserver = null
let isSyncingPickers = false

const getBoxPickerWidth = () => {
  const panelWidth = panelRef.value?.clientWidth || 332
  return Math.max(panelWidth, 240)
}

const getSliderPickerWidth = () => {
  const panelWidth = panelRef.value?.clientWidth || 332
  return Math.max(panelWidth - 48, 192)
}

const formatOutputColor = (color) => {
  if (displayFormat.value === 'HEX') {
    return color.alpha < 1 ? color.hex8String.toUpperCase() : color.hexString.toUpperCase()
  }

  if (displayFormat.value === 'HSLA') {
    return color.alpha < 1 ? color.hslaString : color.hslString
  }

  return color.alpha < 1 ? color.rgbaString : color.rgbString
}

const clampNumber = (value, min, max) => {
  const parsed = Number(value)
  if (Number.isNaN(parsed)) return min
  return Math.min(max, Math.max(min, parsed))
}

const syncInputsFromColor = (color) => {
  rgbInput.value = {
    r: String(Math.round(color.red)),
    g: String(Math.round(color.green)),
    b: String(Math.round(color.blue)),
    a: String(Number(color.alpha.toFixed(2))),
  }

  hslInput.value = {
    h: String(Math.round(color.hue)),
    s: String(Math.round(color.hsl.s)),
    l: String(Math.round(color.hsl.l)),
    a: String(Number(color.alpha.toFixed(2))),
  }

  hexInput.value = color.alpha < 1 ? color.hex8String.toUpperCase() : color.hexString.toUpperCase()
  swatchRgba.value = color.rgbaString
}

const emitThemeColor = (color) => {
  emit('update:modelValue', formatOutputColor(color))
}

const handleColorChange = (color) => {
  syncInputsFromColor(color)
  emitThemeColor(color)
}

const syncOtherPicker = (source, value) => {
  if (isSyncingPickers) return

  isSyncingPickers = true
  try {
    if (source !== boxPickerInstance.value && boxPickerInstance.value) {
      boxPickerInstance.value.color.set(value)
    }
    if (source !== sliderPickerInstance.value && sliderPickerInstance.value) {
      sliderPickerInstance.value.color.set(value)
    }
  } finally {
    isSyncingPickers = false
  }
}

const createBoxPicker = () => {
  if (!boxPickerMountRef.value || boxPickerInstance.value) return

  boxPickerInstance.value = new iro.ColorPicker(boxPickerMountRef.value, {
    width: getBoxPickerWidth(),
    color: props.modelValue || '#1764FF',
    borderWidth: 0,
    padding: 0,
    margin: 0,
    sliderSize: 14,
    handleRadius: 8,
    boxHeight: 180,
    layoutDirection: 'vertical',
    layout: [
      {
        component: iro.ui.Box,
        options: {
          boxHeight: 180,
        },
      },
    ],
  })

  boxPickerInstance.value.on('color:change', (color) => {
    handleColorChange(color)
    syncOtherPicker(boxPickerInstance.value, color.rgbaString)
  })
}

const createSliderPicker = () => {
  if (!sliderPickerMountRef.value || sliderPickerInstance.value) return

  sliderPickerInstance.value = new iro.ColorPicker(sliderPickerMountRef.value, {
    width: getSliderPickerWidth(),
    color: props.modelValue || '#1764FF',
    borderWidth: 0,
    padding: 0,
    margin: 8,
    sliderSize: 14,
    handleRadius: 8,
    layoutDirection: 'vertical',
    layout: [
      {
        component: iro.ui.Slider,
        options: {
          sliderType: 'hue',
        },
      },
      {
        component: iro.ui.Slider,
        options: {
          sliderType: 'alpha',
        },
      },
    ],
  })

  sliderPickerInstance.value.on('color:change', (color) => {
    handleColorChange(color)
    syncOtherPicker(sliderPickerInstance.value, color.rgbaString)
  })
}

const resizePickers = async () => {
  await nextTick()
  boxPickerInstance.value?.resize(getBoxPickerWidth())
  boxPickerInstance.value?.forceUpdate()
  sliderPickerInstance.value?.resize(getSliderPickerWidth())
  sliderPickerInstance.value?.forceUpdate()
}

const applyRgbInput = () => {
  const nextColor = {
    r: clampNumber(rgbInput.value.r, 0, 255),
    g: clampNumber(rgbInput.value.g, 0, 255),
    b: clampNumber(rgbInput.value.b, 0, 255),
    a: clampNumber(rgbInput.value.a, 0, 1),
  }
  boxPickerInstance.value?.color.set(nextColor)
  sliderPickerInstance.value?.color.set(nextColor)
}

const applyHslInput = () => {
  const nextColor = {
    h: clampNumber(hslInput.value.h, 0, 360),
    s: clampNumber(hslInput.value.s, 0, 100),
    l: clampNumber(hslInput.value.l, 0, 100),
    a: clampNumber(hslInput.value.a, 0, 1),
  }
  boxPickerInstance.value?.color.set(nextColor)
  sliderPickerInstance.value?.color.set(nextColor)
}

const applyHexInput = () => {
  const normalized = hexInput.value.startsWith('#') ? hexInput.value : `#${hexInput.value}`
  boxPickerInstance.value?.color.set(normalized)
  sliderPickerInstance.value?.color.set(normalized)
}

watch(
  () => props.modelValue,
  (value) => {
    if (!boxPickerInstance.value || !value) return
    const currentValue = formatOutputColor(boxPickerInstance.value.color)
    if (String(currentValue).toUpperCase() === String(value).toUpperCase()) return
    syncOtherPicker(null, value)
    syncInputsFromColor(boxPickerInstance.value.color)
  }
)

watch(
  () => props.visible,
  async (value) => {
    if (!value) return
    await resizePickers()
  }
)

onMounted(() => {
  createBoxPicker()
  createSliderPicker()
  if (boxPickerInstance.value) {
    syncInputsFromColor(boxPickerInstance.value.color)
  }
  if (typeof ResizeObserver !== 'undefined' && panelRef.value) {
    resizeObserver = new ResizeObserver(() => {
      resizePickers()
    })
    resizeObserver.observe(panelRef.value)
  }
})

onBeforeUnmount(() => {
  resizeObserver?.disconnect()
})

const swatchStyle = computed(() => ({
  backgroundColor: swatchRgba.value,
}))
</script>

<style scoped lang="scss">
.iro-picker-panel {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.iro-picker-panel__box-row {
  width: 100%;
}

.iro-picker-panel__box-mount {
  width: 100%;
}

.iro-picker-panel__slider-row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
}

.iro-picker-panel__slider-mount {
  flex: 1;
  min-width: 0;
}

.iro-picker-panel__footer {
  width: 100%;
}

.iro-picker-panel__fields {
  display: grid;
  grid-template-columns: 88px repeat(4, minmax(0, 1fr));
  gap: 8px;
  width: 100%;
}

.iro-picker-panel__format {
  width: 88px;
}

.iro-picker-panel__hex {
  grid-column: span 4;
}

.iro-picker-panel__swatch-wrap {
  flex: 0 0 36px;
  width: 36px;
  height: 36px;
  padding: 2px;
  border-radius: 8px;
  background-image:
    linear-gradient(45deg, #d9dee7 25%, transparent 25%),
    linear-gradient(-45deg, #d9dee7 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #d9dee7 75%),
    linear-gradient(-45deg, transparent 75%, #d9dee7 75%);
  background-size: 12px 12px;
  background-position: 0 0, 0 6px, 6px -6px, -6px 0px;
}

.iro-picker-panel__swatch {
  width: 100%;
  height: 100%;
  border-radius: 6px;
  border: 1px solid rgba(15, 23, 42, 0.08);
}

.iro-picker-panel :deep(.IroColorPicker) {
  width: 100% !important;
}

.iro-picker-panel :deep(svg) {
  display: block;
}
</style>
