<template>
  <div
    ref="containerRef"
    :class="['split-pane', `split-pane--${direction}`, { 'split-pane--dragging': isDragging }]"
  >
    <div
      class="split-pane__first"
      :style="firstStyle"
    >
      <slot name="first" />
    </div>
    <div
      class="split-pane__handle"
      role="separator"
      :aria-orientation="direction === 'horizontal' ? 'vertical' : 'horizontal'"
      :aria-valuenow="Math.round(ratio * 100)"
      aria-valuemin="0"
      aria-valuemax="100"
      tabindex="0"
      @mousedown="onMouseDown"
      @touchstart.passive="onTouchStart"
      @keydown="onHandleKeydown"
    >
      <div class="split-pane__handle-line" />
    </div>
    <div
      class="split-pane__second"
      :style="secondStyle"
    >
      <slot name="second" />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'

const props = withDefaults(defineProps<{
  direction?: 'horizontal' | 'vertical'
  defaultRatio?: number
  minRatio?: number
  maxRatio?: number
  storageKey?: string
}>(), {
  direction: 'horizontal',
  defaultRatio: 0.5,
  minRatio: 0.2,
  maxRatio: 0.8,
})

const containerRef = ref<HTMLElement | null>(null)
const isDragging = ref(false)

const savedRatio = props.storageKey
  ? parseFloat(localStorage.getItem(props.storageKey) || String(props.defaultRatio))
  : props.defaultRatio

const ratio = ref(
  Math.min(props.maxRatio, Math.max(props.minRatio, savedRatio))
)

const firstStyle = computed(() => {
  if (props.direction === 'horizontal') {
    return { width: `${ratio.value * 100}%` }
  }
  return { height: `${ratio.value * 100}%` }
})

const secondStyle = computed(() => {
  if (props.direction === 'horizontal') {
    return { width: `${(1 - ratio.value) * 100}%` }
  }
  return { height: `${(1 - ratio.value) * 100}%` }
})

function clampRatio(val: number) {
  return Math.min(props.maxRatio, Math.max(props.minRatio, val))
}

function updateRatioFromPosition(clientPos: number, startPos: number, startRatio: number, containerSize: number) {
  const delta = (clientPos - startPos) / containerSize
  ratio.value = clampRatio(startRatio + delta)
}

function finishDrag() {
  isDragging.value = false
  if (props.storageKey) {
    localStorage.setItem(props.storageKey, String(ratio.value))
  }
  window.dispatchEvent(new Event('resize'))
}

function onMouseDown(e: MouseEvent) {
  e.preventDefault()
  if (!containerRef.value) return
  isDragging.value = true

  const startPos = props.direction === 'horizontal' ? e.clientX : e.clientY
  const containerSize = props.direction === 'horizontal'
    ? containerRef.value.offsetWidth
    : containerRef.value.offsetHeight
  const startRatio = ratio.value

  function onMouseMove(ev: MouseEvent) {
    const currentPos = props.direction === 'horizontal' ? ev.clientX : ev.clientY
    updateRatioFromPosition(currentPos, startPos, startRatio, containerSize)
  }

  function onMouseUp() {
    finishDrag()
    document.removeEventListener('mousemove', onMouseMove)
    document.removeEventListener('mouseup', onMouseUp)
  }

  document.addEventListener('mousemove', onMouseMove)
  document.addEventListener('mouseup', onMouseUp)
}

function onTouchStart(e: TouchEvent) {
  if (!containerRef.value) return
  isDragging.value = true

  const touch = e.touches[0]
  const startPos = props.direction === 'horizontal' ? touch.clientX : touch.clientY
  const containerSize = props.direction === 'horizontal'
    ? containerRef.value.offsetWidth
    : containerRef.value.offsetHeight
  const startRatio = ratio.value

  function onTouchMove(ev: TouchEvent) {
    const t = ev.touches[0]
    const currentPos = props.direction === 'horizontal' ? t.clientX : t.clientY
    updateRatioFromPosition(currentPos, startPos, startRatio, containerSize)
  }

  function onTouchEnd() {
    finishDrag()
    document.removeEventListener('touchmove', onTouchMove)
    document.removeEventListener('touchend', onTouchEnd)
  }

  document.addEventListener('touchmove', onTouchMove, { passive: true })
  document.addEventListener('touchend', onTouchEnd)
}

function onHandleKeydown(e: KeyboardEvent) {
  const step = 0.02 // 2% per key press
  if (props.direction === 'horizontal') {
    if (e.key === 'ArrowLeft') { e.preventDefault(); ratio.value = clampRatio(ratio.value - step) }
    else if (e.key === 'ArrowRight') { e.preventDefault(); ratio.value = clampRatio(ratio.value + step) }
  } else {
    if (e.key === 'ArrowUp') { e.preventDefault(); ratio.value = clampRatio(ratio.value - step) }
    else if (e.key === 'ArrowDown') { e.preventDefault(); ratio.value = clampRatio(ratio.value + step) }
  }
  if (e.key === 'Home') { e.preventDefault(); ratio.value = props.minRatio }
  else if (e.key === 'End') { e.preventDefault(); ratio.value = props.maxRatio }
}
</script>

<style scoped>
.split-pane {
  display: flex;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

.split-pane--horizontal {
  flex-direction: row;
}

.split-pane--vertical {
  flex-direction: column;
}

.split-pane__first,
.split-pane__second {
  overflow: hidden;
  min-width: 0;
  min-height: 0;
}

.split-pane__handle {
  flex-shrink: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color var(--transition-fast);
}

.split-pane--horizontal > .split-pane__handle {
  width: 8px;
  cursor: col-resize;
  flex-direction: column;
}

.split-pane--vertical > .split-pane__handle {
  height: 8px;
  cursor: row-resize;
  flex-direction: row;
}

.split-pane__handle:hover,
.split-pane--dragging .split-pane__handle {
  background-color: var(--color-primary-light);
}

.split-pane__handle-line {
  background-color: var(--border-color);
  border-radius: var(--radius-full);
  transition: background-color var(--transition-fast);
}

.split-pane--horizontal > .split-pane__handle > .split-pane__handle-line {
  width: 2px;
  height: 28px;
}

.split-pane--vertical > .split-pane__handle > .split-pane__handle-line {
  height: 2px;
  width: 28px;
}

.split-pane__handle:hover .split-pane__handle-line,
.split-pane--dragging .split-pane__handle-line {
  background-color: var(--color-primary);
}

.split-pane--dragging {
  user-select: none;
}
</style>
