<script setup lang="ts">
import {
  computed,
  onBeforeUnmount,
  onMounted,
  ref,
  useTemplateRef,
  watch,
} from 'vue';

const DEFAULT_MIN = 0;
const DEFAULT_MAX = 100;

interface Props {
  splitOrientation?: 'horizontal' | 'vertical';
  min?: number;
  max?: number;
  primary?: 'first' | 'second';
  collapsed?: boolean;
  ariaLabel?: string;
}

const props = withDefaults(defineProps<Props>(), {
  splitOrientation: 'vertical',
  primary: 'first',
  min: DEFAULT_MIN,
  max: DEFAULT_MAX,
  collapsable: false,
});

const emit = defineEmits(['update:collapsed']);

const split = defineModel<number>({
  default: 50,
});

const container = useTemplateRef('split-window');
const primaryId = ref<string>(crypto.randomUUID());

//State Machine
const isDragging = ref<boolean>(false);
const startSplit = ref<number | null>(null);
const startPosition = ref<number | null>(null);

const min = computed(() =>
  clamp(props.min ?? DEFAULT_MIN, DEFAULT_MIN, DEFAULT_MAX),
);
const max = computed(() =>
  clamp(props.max ?? DEFAULT_MAX, DEFAULT_MIN, DEFAULT_MAX),
);

// Correction: état local pour collapsed
const collapsedLocal = ref(props.collapsed ?? false);

watch(
  () => props.collapsed,
  (val) => {
    collapsedLocal.value = val ?? false;
  },
);

function setCollapsed(val: boolean) {
  collapsedLocal.value = val;
  emit('update:collapsed', val);
}

const clampSplit = computed(() => {
  if (collapsedLocal.value) {
    return props.primary === 'first' ? min.value : max.value;
  } else {
    return clamp(split.value, min.value, max.value);
  }
});

const size = computed(() => {
  return `${clampSplit.value}fr auto ${100 - clampSplit.value}fr`;
});

const gridStyle = computed(() => {
  return props.splitOrientation === 'horizontal'
    ? {
        gridTemplateRows: size.value,
        gridTemplateColumns: 'none',
      }
    : {
        gridTemplateColumns: size.value,
        gridTemplateRows: 'none',
      };
});

const HANDLED_KEYS = [
  'ArrowLeft',
  'ArrowRight',
  'ArrowUp',
  'ArrowDown',
  'Enter',
  'Home',
  'End',
];

onMounted(() => {
  window.addEventListener('pointermove', onPointerMove);
  window.addEventListener('pointerup', onPointerUp);
});

onBeforeUnmount(() => {
  window.removeEventListener('pointermove', onPointerMove);
  window.removeEventListener('pointerup', onPointerUp);
});

function onPointerDown(evt: PointerEvent) {
  isDragging.value = true;
  if (collapsedLocal.value) {
    setCollapsed(false);
  }
  startSplit.value = clampSplit.value;
  startPosition.value =
    props.splitOrientation === 'vertical' ? evt.clientX : evt.clientY;
}

function onPointerMove(evt: PointerEvent) {
  if (!isDragging.value) return;
  _move(evt);
}

function onPointerUp(evt: PointerEvent) {
  if (!isDragging.value) return;
  _move(evt);
  isDragging.value = false;
  startPosition.value = null;
  startSplit.value = null;
}

function onKeyDown(evt: KeyboardEvent) {
  if (
    !HANDLED_KEYS.includes(evt.key) ||
    (props.splitOrientation === 'horizontal' &&
      (evt.key === 'ArrowLeft' || evt.key === 'ArrowRight')) ||
    (props.splitOrientation === 'vertical' &&
      (evt.key === 'ArrowUp' || evt.key === 'ArrowDown'))
  ) {
    return;
  }

  evt.preventDefault();
  evt.stopPropagation();

  switch (evt.key) {
    case 'ArrowLeft':
    case 'ArrowUp':
      if (collapsedLocal.value) {
        setCollapsed(false);
      }
      split.value = Math.max(min.value, clampSplit.value - 1);
      break;
    case 'ArrowRight':
    case 'ArrowDown':
      if (collapsedLocal.value) {
        setCollapsed(false);
      }
      split.value = Math.min(max.value, clampSplit.value + 1);
      break;
    case 'Enter':
      setCollapsed(!collapsedLocal.value);
      break;
    case 'Home':
      if (collapsedLocal.value) {
        setCollapsed(false);
      }
      split.value = props.primary === 'first' ? min.value : max.value;
      break;
    case 'End':
      if (collapsedLocal.value) {
        setCollapsed(false);
      }
      split.value = props.primary === 'first' ? max.value : min.value;
      break;
  }
}

function _move(evt: PointerEvent) {
  if (startPosition.value === null || startSplit.value === null) return;

  const currentPosition =
    props.splitOrientation === 'vertical' ? evt.clientX : evt.clientY;

  const delta = currentPosition - startPosition.value;

  const containerSize =
    props.splitOrientation === 'vertical'
      ? container.value?.getBoundingClientRect().width || 1
      : container.value?.getBoundingClientRect().height || 1;

  const deltaPercent = (delta / containerSize) * 100;
  split.value = startSplit.value + deltaPercent;
}

function clamp(value: number, minValue: number, maxValue: number) {
  return Math.min(Math.max(value, minValue), maxValue);
}
</script>

<template>
  <div
    ref="split-window"
    class="split-window"
    :class="[
      `split-window--${splitOrientation}`,
      { 'split-window--dragging': isDragging },
    ]"
    :style="gridStyle"
  >
    <div
      class="split-window__pane split-window__first-pane"
      :id="primary === 'first' ? primaryId : undefined"
    >
      <slot name="first" />
    </div>
    <div
      class="split-window__separator"
      role="separator"
      tabindex="0"
      :aria-label="props.ariaLabel || 'Séparateur de volet'"
      :aria-valuemin="min"
      :aria-valuemax="max"
      :aria-valuenow="clampSplit"
      :aria-orientation="splitOrientation"
      :aria-controls="primaryId"
      @pointerdown.prevent.stop="onPointerDown"
      @keydown="onKeyDown"
    >
      <slot name="separator" />
    </div>
    <div
      class="split-window__pane split-window__second-pane"
      :id="primary === 'second' ? primaryId : undefined"
    >
      <slot name="second" />
    </div>
  </div>
</template>

<style scoped lang="scss">
.split-window {
  display: grid;
  width: 100%;
  height: 100%;
  overflow: hidden;

  &__pane {
    display: flex;
    max-height: 100%;
    overflow: hidden;
  }

  &__separator {
    position: relative;
    z-index: calc(var(--bms-z-index-modal));

    &:before {
      content: '';
      position: absolute;
      top: 0;
      left: 0;
    }

    &:focus-within {
      &:before {
        outline: 2px solid black;
      }
    }
  }

  &--vertical {
    .split-window__separator {
      height: 100%;
      width: 0;

      &:before {
        content: '';
        width: 8px;
        height: 100%;
        transform: translate(-50%, 0);
        cursor: col-resize;
      }
    }
  }

  &--horizontal {
    .split-window__separator {
      height: 0;
      width: 100%;

      &:before {
        content: '';
        width: 100%;
        height: 8px;
        transform: translate(0, -50%);
        cursor: row-resize;
      }
    }
  }

  &--dragging {
    .split-window__separator {
      &:before {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        transform: unset;
      }
    }
  }
}
</style>
